Guides
Last updated
November 26, 2025

How to Send Email with Django

Nicolas Rios

Table of Contents:

Get your free
Email Validation
 API key now
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
START FOR FREE
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required

The Complete Guide (From Development to Production)

Sending emails is one of the most essential features in any real-world Django project — from user registrations and password resets to notifications and marketing updates. But while it’s easy to make an email work locally, taking that same setup to production often breaks things fast. 😬

A quick “hello world” email might work on your laptop, but once deployed, your app needs to handle SMTP configuration, authentication, HTML formatting, attachments, and sender reputation — or risk getting flagged as spam.

How to Send Email with Django- Abstract API

This guide walks you through the complete, production-ready workflow for sending emails with Django. You’ll learn how to:

  • ✅ Configure your app for both local testing and production
  • 🧠 Use Django’s built-in tools like send_mail() for simple messages
  • 🚀 Send HTML emails and attachments with EmailMessage
  • 🔒 Follow best practices for storing credentials securely
  • 🛡️ Validate email addresses before sending using AbstractAPI’s Email Validation API

Let’s dive in!

Enter your email address to start
Need inspiration? Try
test@abstractapi.com
VALIDATE
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Checking
5
Results for
email
Deliverability:
TEST
Free email:
TEST
Valid format:
TEST
Disposable email:
TEST
Valid SMTP:
TEST
Valid MX record:
TEST
Get free credits, more data, and faster results

⚙️ 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.

The Solution: Validate Emails Before Sending

🧠 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.

Nicolas Rios

Head of Product at Abstract API

Get your free
Email Validation
key now
See why the best developers build on Abstract
get started for free

Related Articles

Get your free
Email Validation
key now
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required