⚙️ The Foundation – Configuring Your settings.py
Before sending any email, Django needs to know which backend to use. You’ll typically configure one backend for development and another for production.
🧪 For Development: Test Emails in Your Console (No Server Needed)
When testing locally, it’s best not to send real emails — you’ll waste time, hit API limits, and possibly spam your own inbox.
Instead, Django provides a convenient backend that outputs emails directly to your console:
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Now, when you send an email, it’ll print right in your terminal where runserver is running. Perfect for debugging without sending anything real. 🧰
🌍 For Production: Use a Real SMTP Server
In production, you’ll need a real SMTP server. SMTP (Simple Mail Transfer Protocol) is the standard for sending emails across the web.
Here’s an example setup using Gmail:
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your_email@gmail.com'
EMAIL_HOST_PASSWORD = 'your_app_password' # Use App Password for Gmail
You can replace Gmail with providers like SendGrid, Mailgun, or Amazon SES for better deliverability.
🚫 Best Practice: Don’t Use Personal Gmail in Production
Using a personal Gmail account in production can lead to:
- Security risks (hardcoding credentials in code)
- Rate limiting by Google
- Two-Factor Authentication issues
Instead, use a dedicated email service with an app-specific password or API key.
🔐 Best Practice: Securely Store Your Credentials
Never hardcode passwords or API keys in settings.py. Instead, store them in a .env file and load them using python-decouple:
# settings.py
from decouple import config
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = config('EMAIL_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_PASSWORD')
.env file:
EMAIL_USER=your_email@gmail.com
EMAIL_PASSWORD=your_secret_app_password
This keeps your secrets safe and your repo clean. 🧹
✉️ Method 1 – The Simple Way with send_mail()
Django’s send_mail() is perfect for straightforward plain-text messages.
💬 How to Send a Plain-Text Email
Here’s a simple example in a Django view:
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse
def my_email_view(request):
subject = 'Welcome to My Site!'
message = 'Thanks for signing up. Glad to have you!'
from_email = 'from@example.com'
recipient_list = ['to@example.com']
try:
send_mail(subject, message, from_email, recipient_list, fail_silently=False)
except BadHeaderError:
return HttpResponse('Invalid header found.')
return HttpResponse('Email sent successfully!')
Parameters explained:
- subject: Email subject line
- message: Body text
- from_email: Must match your SMTP user if using Gmail
- recipient_list: A list of recipient addresses
- fail_silently=False: Raises exceptions on errors
👥 Send to Multiple Recipients
Just add more emails to the list:
recipient_list = ['user1@example.com', 'user2@example.com', 'user3@example.com']
send_mail(subject, message, from_email, recipient_list)
That’s all it takes!
💡 Method 2 – The Advanced Way with EmailMessage
For production apps, you’ll often need HTML templates, attachments, and BCCs. That’s where EmailMessage comes in.
🖋️ Why send_mail() Isn’t Enough
send_mail() can’t send HTML, attachments, or manage advanced headers. EmailMessage offers complete control and flexibility.
🌈 How to Send HTML Emails (The Right Way)
HTML emails look great and can include branding, buttons, and formatted content.
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
html_content = render_to_string('emails/welcome.html', {'user_name': 'Alice'})
text_content = 'Welcome to My Site! Thanks for signing up.'
email = EmailMessage(
'Welcome, Alice!',
text_content,
'from@example.com',
['to@example.com']
)
email.attach_alternative(html_content, "text/html")
email.send()
This creates a multi-part email that includes both plain-text and HTML — ensuring compatibility with all email clients.
📎 Send Attachments, BCC, and Reply-To
Need to attach files or hide recipients? Here’s how:
from django.core.mail import EmailMessage
email = EmailMessage(
'Your Invoice',
'Please find your invoice attached.',
'billing@example.com',
['customer@example.com'],
['accounting@example.com'], # BCC
reply_to=['support@example.com'],
headers={'Message-ID': 'foo'},
)
# Attach a file
email.attach_file('/path/to/invoice.pdf')
email.send()
BCC hides recipients from each other. Reply-To defines where responses should go.
🛡️ The Critical Next Step… You Can Send Email. But Should You?
Now that you can send emails… here’s the real question: Should you send them all? 🤔
🚨 The Problem
What happens when users register with fake or misspelled addresses like test@test.com or user@gmil.com?
Your perfectly crafted emails bounce, and too many bounces damage your sender reputation — even causing your domain or IP to be blacklisted.
✅ The Solution: Validate Emails Before Sending
While Django’s built-in EmailValidator checks for proper format, it can’t verify if the mailbox actually exists.
That’s where the AbstractAPI Email Validation API comes in. It checks deliverability, MX records, disposable domains, and syntax in real time.

🧠 How to Validate Emails in Django with AbstractAPI
Here’s how to integrate validation right before sending:
import requests
from django.core.mail import send_mail
from django.http import HttpResponse
def send_validated_email(request):
email_to_validate = 'user@example.com' # e.g. from a form
api_key = 'YOUR_ABSTRACTAPI_KEY'
# 1. VALIDATE THE EMAIL
response = requests.get(
f"https://emailvalidation.abstractapi.com/v1/?api_key={api_key}&email={email_to_validate}"
)
if response.status_code == 200 and response.json()['is_smtp_valid']['value']:
# 2. SEND THE EMAIL
send_mail(
'Subject',
'This email was validated first!',
'from@example.com',
[email_to_validate],
)
return HttpResponse('✅ Success! Valid email sent.')
else:
return HttpResponse('❌ Invalid or undeliverable email address.')
With just a few lines of code, you can protect your sender reputation and reduce bounce rates dramatically.
🎯 Conclusion: You’re Now a Django Email Expert
You’ve just built a production-ready email workflow in Django! 🚀
Let’s recap:

The final piece? Integrate AbstractAPI’s Email Validation API to ensure every email you send actually lands in an inbox.
👉 Get your free AbstractAPI key today and start sending smarter, validated emails from your Django app.



