Send Emails in Django: A Developer’s Tutorial to SMTP & Email API Methods

On July 23, 2025
18min read
Dmitriy Shcherbakan Full Stack Developer @Railsware
Artem Litvinenko Software Engineer

In this tutorial, I’ll show you how to send emails in Django by using any of the following:

In the end, I’ll also show you how to test all of the code and email sending functionality to ensure everything is perfect. But before we start, make sure you have the latest version of Python and Django installed on your system.

Disclaimer: Every line of code in this article has been written and checked by our developers.

Send emails in Django using SMTP

Let’s start by setting up django.core.email, which is a default Django SMTP solution. The package includes a send_mail method, providing simple email sending in any Django application. Python already has a default package for SMTP email sending, smtplib, whereas django.core.mail is more of a wrapper over this package to work natively in Django. 

1. Setting up the project

Make a new working directory, which we’ll use to bootstrap a new Django project: 

$ mkdir django-email
$ django-admin startproject emails django-email

The code will create a Django project called emails in the django-email directory, and we’ll get a structure looking like this: 

Next, we need to modify the settings.py file, which is a Python module in Django that defines and stores all the settings to tweak how our Django application works and runs.

So, go to the settings.py file, and add the following code at the end of the file:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'live.smtp.mailtrap.io'
EMAIL_PORT = 587
EMAIL_HOST_USER = '<your-mailtrap-username>'
EMAIL_HOST_PASSWORD = '<your-mailtrap-password>'
EMAIL_USE_TLS = True

Important: These variable names shouldn’t be tweaked since they’re used internally by Django. This configuration works for Mailtrap as an SMTP provider, so if you’re using Mailtrap, just provide your credentials from your dashboard.

As you see above, we also have the EMAIL_BACKEND variable provided, and this is exactly the email backend that we previously mentioned, which is responsible for sending emails in Django. 

You can think of the email backend as a core instance that manages all the technical details of sending emails, from opening and closing email-sending connections to actual email sending implementation, etc. 

The default email backend in Django is backends.smtp.EmailBackend, which is literally what we provided for the EMAIL_BACKEND variable, so basically we’re using a default SMTP email backend in Django to handle emails. 

3. Creating an email-sending function

views.py is a Django file that defines the content returned when a user navigates to a specific page. We can also perform miscellaneous actions in it, like sending emails. 

So, create and open a views.py file in the emails folder, then copy/paste the following code in order to create a sending function which will also return the results:

from django.core.mail import send_mail
from django.http import HttpResponse

def send_example_email(request):
    try:        
        send_mail(
            subject='Email from Django',
            message='This is an email sent from Django application.',
            from_email='example@your_mailtrap_domain',
            recipient_list=['test@example.com'],
            fail_silently=False,
        )
        
        return HttpResponse('Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

4. Creating a URL path for email sending

urls.py file is a Django file that allows us to associate the created views with corresponding URLs in the browser. 

If we import the implemented view and add it to URL patterns, Django will associate our view with the URL, and we’ll be able to test the email sending function by opening that URL.

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('send-email/', views.send_example_email, name='send_example_email'),
]

5. Running the application and sending the email

Finally, run the Django application to test the implemented function.

python manage.py runserver

If you have followed everything so far, you should see the following in the console.

As you open the development server in your browser, change the URL path to /send-email instead of the root URL. You should see a text “Email sent successfully!”, meaning the implemented send_example_email function was executed, and we just sent the email. 

Send HTML email 

To send HTML emails in Django, simply modify the previously created views.py file:

from django.core.mail import send_mail
from django.http import HttpResponse

def send_example_email(request):
    try:
        send_mail(
            subject='Test HTML Email from Django',
            message='This is a test email sent from Django application.',
            from_email='reply@your_mailtrap_domain',
            recipient_list=['example@example.com'],
            fail_silently=False,
            html_message='<p>This is a <strong>HTML test email</strong> sent from Django application.</p>',
        )
        
        return HttpResponse('HTML Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send HTML email: {str(e)}')

As you see, the email sending logic is basically the same as in the previous example, except we added html_message property to include the HTML content of the email we’re sending.

Run the application and open /send-email page in the browser.

python manage.py runserver

Send email to multiple recipients

To send an email to multiple recipients, let’s use the recipients_list parameter, extend it, and add another recipient we would like to send an email to:

from django.core.mail import send_mail
from django.http import HttpResponse

def send_example_email(request):
    try:
        send_mail(
            subject='Test HTML Email from Django',
            message='This is a test email sent from Django application.',
            from_email='reply@your_mailtrap_domain',
            recipient_list=['example@example.com', 'example2@example.com'],
            fail_silently=False,
            html_message='<p>This is a <strong>HTML test email</strong> sent from Django application.</p>',
        )
        
        return HttpResponse('Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Run the server with:

python manage.py runserver

As you run your Django application and open the /send-email page, your email should be sent successfully. In your Mailtrap Dashboard, you should now see 2 new emails being sent in the Email Logs tab.

Note: For older versions of Django you may need EmailMultiAlternatives.

Send email with attachments

For attachments, we’ll have to use the EmailMessage class, since not all features from the EmailMessage class are available in send_mail

If you have to use features such as file attachments, you’ll have to make EmailMessage instances directly. For file attachment logic, we’re going to use the attach_file method. Make sure you have the right file in your working directory. For this example, we’re going to use the Kitten Mailtrap.jpg file.

Here’s a code snippet you can copy/paste in your views.py file:

from django.core.mail import EmailMessage
from django.http import HttpResponse
from django.conf import settings
import os

def send_example_email(request):
    try:
        email = EmailMessage(
            subject='Test Email with Attachment from Django',
            body='<p>This is a <strong>HTML test email</strong> with an attachment sent from Django application.</p>',
            from_email='reply@your_mailtrap_domain',
            to=['recipient@example.com'],
        )
        email.content_subtype = "html"
        
        file_path = os.path.join(settings.BASE_DIR, 'Kitten Mailtrap.jpg')
        email.attach_file(file_path)
        
        email.send(fail_silently=False)
        
        return HttpResponse('Email with attachment sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email with attachment: {str(e)}')

Send email with embedded image

To send an email with an embedded image, we’ll use the EmailMessage class. For image attachment, we’re going to use the attach method.

In this demo, I’ll use the Kitten Mailtrap.jpg file again, which I’ll refer to by using cid: syntax (Content ID). To do this, I’ll add a Content ID header, so that it can be associated with the img tag in the html and get embedded.

from django.core.mail import EmailMessage
from email.mime.image import MIMEImage
from django.http import HttpResponse
from django.conf import settings
import os

def send_example_email(request):
    try:        
        email = EmailMessage(
            subject='Test Email with Embedded Image from Django',
            body='''
                <p>This is a <strong>HTML test email</strong> with an embedded image sent from Django application.</p>
                <img src="cid:kitten_image">
            ''',
            from_email='reply@your_mailtrap_domain',
            to=['recipient@example.com']
        )
        email.content_subtype = "html"
        
        image_path = os.path.join(settings.BASE_DIR, 'Kitten Mailtrap.jpg')
        with open(image_path, 'rb') as img:
            image = MIMEImage(img.read())
            image.add_header("Content-ID", "<kitten_image>")
            email.attach(image)
        
        email.send(fail_silently=False)
        
        return HttpResponse('Email with embedded image sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email with embedded image: {str(e)}')

After running the server and opening the /send-email page, your email should be sent successfully, and you’ll be able to see our kitten in the Email Logs.

Asynchronous email sending

The primary problem with synchronous email sending is performance. On a large scale, when a lot of users trigger email sending logic, it may slow down the overall process of email sending in the application. This is crucial in high-load applications, and using an asynchronous approach is one of the most efficient ways to optimize it.

As for asynchronous email sending, there’re a few approaches to achieve it. In complex Django applications, the most typical solution would be using Celery and Redis. We’re not going to cover them for this topic, since using Celery and Redis is a large topic of its own worthy of a separate article. Instead, we’ll focus on setting everything up so that we can perform asynchronous email sending simply using the Post Office Library

Post Office is a powerful solution, providing us all the tools we need to handle emails asynchronously. Compared to Celery, we don’t need a message broker, since Post Office uses Django’s Backend to queue tasks, which means we can literally use Post Office from the box without much fuss in setting things up.

Unlike Django’s default send_mail method, which blocks execution until the email is sent, Post Office queues emails in the database and sends them via background jobs. This improves performance and prevents request timeouts in applications that send multiple emails. 

Additionally, starting from version 3.0, Django Post Office uses multiple threads to significantly speed up email delivery. By default, it runs 5 threads per process, but this can be adjusted. It’s also fully compatible with task queues like Celery and RQ.

First, let’s install the django-post_office package.

pip install django-post_office

In settings.py, let’s add the Post Office to the list of installed apps. Just add "post_office" at the end of the list.

INSTALLED_APPS = (
   "post_office",
)

Run Django migration:

python manage.py migrate

The Post Office provides its own Custom Email Backend. We’ll also need to set it in settings.py:

EMAIL_BACKEND = 'post_office.EmailBackend'  

Lastly, in the views.py file, use Post Office’s mail.send method to send emails. This way, the emails are now sent asynchronously.

from post_office import mail
from django.http import HttpResponse

def send_example_email(request):
    try:        
        response = mail.send(
            'recipient@example.com', 
            'reply@your_mailtrap_domain',
            subject='Post Office Email',
            html_message='Hi! This is a Post Office Email from <strong>Mailtrap!</strong>!',
            priority='now',
        )
        print(response)
        
        return HttpResponse('Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Important: mail.send may not send your emails immediately because it puts them in a queue. For the email to be sent immediately, you need to pass priority=’now’, like in the example above.

Send bulk email

For this example, we won’t need the Post Office, so we’ll go back to django.core.mail and the default configuration required for it in the settings.py file. This way, we can use the send_mass_email function to send multiple emails in one go.

Simply copy/paste the following code snippet in the views.py file:

from django.core.mail import send_mass_mail
from django.http import HttpResponse

def send_example_email(request):
    try:
        message1 = ('Subject 1', 'Message body 1', 'from@your_mailtrap_domain', ['to@example.com'])
        message2 = ('Subject 2', 'Message body 2', 'from@your_mailtrap_domain', ['to@example.com'])
        messages = (message1, message2)
        
        sent_count = send_mass_mail(messages, fail_silently=False)
        
        return HttpResponse(f'{sent_count} emails sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send emails: {str(e)}')

Upon running the server and opening the /send-email page, you should see the following message:

And two emails waiting for you in the Email Logs:

Note: You can also use the dedicated Mailtrap Bulk Stream, which is designed to handle large amounts of emails without a stutter, all the while keeping your deliverability high.

Send emails in Django using email API

Sending emails using email API isn’t much different from how we did it for the SMTP approach, so the code examples are going to be very similar in most of the subtopics. The primary difference here is that using Mailtrap Python SDK as our email API solution, we’ll have much less fuss configuring the Django project. 

Start by making a separate working directory and initializing the Django project:

$ mkdir django-email-api
$ django-admin startproject email_api_emails django-email-api

Then, install Mailtrap Python SDK with pip if you haven’t:

pip install mailtrap

Once the installation is complete, go to the settings.py file and add the following code at the end of the file: 

MAILTRAP_API_KEY = 'your_mailtrap_key'

Note: Make sure to provide your Mailtrap API key from the dashboard.

Send plain text email

Next, copy/paste the following script into your views.py file:

from django.http import HttpResponse
from django.conf import settings
import mailtrap as mt

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="your@email.com")],
            subject="You are awesome!",
            text="Congrats for sending test email with Mailtrap!",
        )
        client.send(mail)
        
        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Before opening the browser and sending an email, the last thing you need to do is configure urls.py to add a required URL-view association:

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('send-email/', views.send_example_email, name='send_example_email'),
]

Send HTML email

To send HTML emails in Django, we’ll add the html property to our code:

from django.http import HttpResponse
from django.conf import settings
import mailtrap as mt

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="your@email.com")],
            subject="You are awesome!",
            html="<h1>Congrats for sending test email with Mailtrap!</h1>",
        )
        client.send(mail)
        
        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Send email to multiple recipients

Sending an email to multiple recipients is just as easy. As you can see, in the to parameter we use a list to pass all the recipients. So, let’s add an extra recipients to the list:

from django.http import HttpResponse
from django.conf import settings
import mailtrap as mt

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="your1@email.com"), mt.Address(email="your2@email.com")],
            subject="You are awesome!",
            html="<h1>Congrats for sending test email with Mailtrap!</h1>",
        )
        client.send(mail)
        
        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Send email with attachments

For file attachment logic, we’re going to use the attachments property and mt.Attachment wrappers to pass the correct format of attachments. For this example, we’re going to use the Kitten Mailtrap.jpg file.

To send an email with attachments, let’s modify the example in the views.py file with the following code:

from django.http import HttpResponse
from django.conf import settings
from pathlib import Path
import mailtrap as mt
import base64

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    image = Path(settings.BASE_DIR).joinpath("Kitten Mailtrap.jpg").read_bytes()

    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="recipient@example.com")],
            subject="You are awesome!",
            html="<h1>Congrats for sending test email with Mailtrap!</h1>",
            attachments=[
                mt.Attachment(
                    content=base64.b64encode(image),
                    filename="kitten.jpg",
                    disposition=mt.Disposition.ATTACHMENT,
                    mimetype="image/jpeg",
                )
            ],
        )
        client.send(mail)
        
        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Important: Pay attention to the disposition property. There are 2 types of dispositions in Mailtrap Python SDK: INLINE and ATTACHMENT. We will use INLINE type in the next subtopic, and for this subtopic, we have to use ATTACHMENT.

Send email with embedded image

To send an email with an embedded image, insert the following snippet into your views.py:

from django.http import HttpResponse
from django.conf import settings
from pathlib import Path
import mailtrap as mt
import base64


client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    image = Path(settings.BASE_DIR).joinpath("Kitten Mailtrap.jpg").read_bytes()

    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="to@example.com")],
            subject="You are awesome!",
            html='''
                <h1>Congrats for sending test email with Mailtrap!</h1>
                <img src="cid:kitten" alt="Kitten" />
            ''',
            attachments=[
                mt.Attachment(
                    content=base64.b64encode(image),
                    filename="kitten.jpg",
                    disposition=mt.Disposition.INLINE,
                    mimetype="image/jpeg",
                    content_id="kitten",
                )
            ],
        )
        client.send(mail)
        
        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Code breakdown:

  • First, we changed the disposition property to mt.Disposition.INLINE. Then we would need to provide a new content_id="kitten" property. The property is used to associate the attached image with the HTML element in the markup that we have in the example. 
  • In the tag’s src property we use cid:kitten. Basically, it looks for an image with content_id kitten in the attachments, and displays it in the HTML document if such an image is found.

And here’s our kitten in the Mailtrap Email Logs.

Asynchronous email sending

To send emails in Mailtrap Python SDK asynchronously, we can still use the Post Office library. However, this approach is considerably more complex, since the Post Office library has its own API and methods for email sending, which means that we would have to create a Custom Email Backend over their own email backend to make it work. 

Instead, we’re going to use django-q, another solid multiprocessing task-queue library, which allows us to perform asynchronous tasks in Django easily. Unlike with Celery, we don’t need to set up a message broker in both Post Office and django-q, meaning we can use this tool right from the box without any extra fuss in configuring things.

So, let’s install django-q using the pip package manager:

pip install django-q2

Note that the django-q package is for older versions of Django, whereas for the newest version, we will use django-q2.

Next, add django_q to INSTALLED_APPS in the settings.py file to the bottom of the list. Also, in the same file, let’s add a new Q_CLUSTER configuration to configure django-q2 to use Django’s Backend as a message broker, just like so:

INSTALLED_APPS = (
    'django_q',
)

Q_CLUSTER = {
    "name": "DjangORM",
    "workers": 4,  
    "timeout": 60, 
    "queue_limit": 50,
    "bulk": 10,
    "orm": "default",  
}

Run migration to create the db tables for django-q:

python manage.py migrate

Run the Django Q2 Cluster in the terminal to handle asynchronous tasks:

python manage.py qcluster

Use async_task function, and pass the client.send function with the mail as a parameter:

from django_q.tasks import async_task
from django.http import HttpResponse
from django.conf import settings
import mailtrap as mt

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    try:
        mail = mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="recipient@example.com")],
            subject="You are awesome!",
            html='<h1>Congrats for sending test email with Mailtrap!</h1>',
        )
        async_task(client.send, mail)

        return HttpResponse(f'Email sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send email: {str(e)}')

Send bulk email

To bulk send emails, we’ll send multiple emails in one go using loops. Similarly, you can make a list of recipients to send an email for each recipient in the list:

from django.http import HttpResponse
from django.conf import settings
import mailtrap as mt

client = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

def send_example_email(request):
    email_list = [
        mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="to@example.com")],
            subject="Email 1",
            html='<h1>Congrats for sending an email with Mailtrap!</h1>',
        ),
        mt.Mail(
            sender=mt.Address(email="from@your_mailtrap_domain", name="Mailtrap Test"),
            to=[mt.Address(email="to@example.com")],
            subject="Email 2",
            html='<h1>Congrats for sending another email with Mailtrap!</h1>',
        )
    ]

    try:
        for email in email_list:
            client.send(email)

        return HttpResponse(f'{len(email_list)} emails sent successfully!')
    except Exception as e:
        return HttpResponse(f'Failed to send emails: {str(e)}')

Pro tip: As with the SMTP method in the previous chapter, I recommend you use the Mailtrap Bulk Stream again here for its ability to handle large volumes of emails.

Send emails in Django using custom email backend

With custom email backends, we can create our own wrapper over Mailtrap’s Python SDK and have our own Custom Email Backend configured with Mailtrap in Django. This way, we will be able to use the django.core.mail package, while using Mailtrap’s SDK in Django. 

Additionally, with this approach, we don’t need to configure email keys in the settings.py file. We can simply specify the token variable to use in our code when passing to Mailtrap’s SDK client initialization method, and the email backend we’re using.

First, install the Mailtrap Python SDK using pip:

pip install mailtrap

Then, in the emails folder, create a backends.py file, where we’ll specify our own custom Email backend class. All custom backends are extended from the Django’s BaseEmailBackend class. 

The implementation is simple: we just initialize MailtrapClient as a connection, configure send_messages to implement email sending logic of the Custom backend, and disable fail_silently. For your convenience, here’s a code snippet you can use:

from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend
import mailtrap as mt

class MailtrapEmailBackend(BaseEmailBackend):
    def __init__(self, fail_silently=False, *args, **kwargs):
        super().__init__(fail_silently=fail_silently, *args, **kwargs)
        self.connection = mt.MailtrapClient(token=settings.MAILTRAP_API_KEY)

    def send_messages(self, email_messages):
        sent_count = 0
        for message in email_messages:
            print(message)
            mail = mt.Mail(
                sender=mt.Address(email=message.from_email),
                to=[mt.Address(email) for email in message.recipients()],
                subject=message.subject,
                text=message.body,
            )
            response = self.connection.send(mail)
            if response.get('success'):
                sent_count += 1
        return sent_count

What’s cool about this approach is that we don’t need any specific keys like we did in the django.core.mail example. In the settings.py file, we will just specify 2 keys: path to the custom email backend to configure it in the Django application, and Mailtrap API key used. Since it’s an Email API based approach, all the connection logic is configured in the Mailtrap SDK. 

In settings.py, you can replace the previously defined keys with these 2 lines:

EMAIL_BACKEND = 'emails.backends.MailtrapEmailBackend'
MAILTRAP_API_KEY = 'your_mailtrap_key' 

Important: In the EMAIL_BACKEND variable, make sure to use the following format: 'project_name.file.class'.

As for the API key, you can find it in your Mailtrap Dashboard in the API tab:

Test emails and email sending on staging

There’s usually a lot of code to deal with when adding email sending functionality to your project or app, so testing whether everything works is crucial if you don’t want to face issues on deployment.

A simple but very limited and minimal way to test emails in Django is to use TestCase, a solution for testing a few aspects of your email delivery. It uses locmem.EmailBackend, which stores messages in the local memory cache, that is, django.core.mail.outbox. So, this test runner does not actually send emails. 

To use it, go to settings.py and set up the email backend variable for testing:

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Then, you can use the following unit test sample to test your email sending capability:

from django.core import mail
from django.test import TestCase

class EmailTestCase(TestCase):
    def test_send_email(self):
        mail.send_mail(
            'Hi from Mailtrap!', 'This is a Mailtrap test email.',
            'from@your_mailtrap_domain', ['to@example.com'],
            fail_silently=False,
        )

        self.assertEqual(len(mail.outbox), 1)        
        self.assertEqual(mail.outbox[0].subject, 'Hi from Mailtrap!')
        self.assertEqual(mail.outbox[0].body, 'This is a Mailtrap test email.')
        self.assertEqual(mail.outbox[0].from_email, 'from@your_mailtrap_domain')
        self.assertEqual(mail.outbox[0].to, ['to@example.com'])

If everything was set up correctly, in the terminal, you should see a positive result after running the tests:

Now, to make sure the tests actually work and validate the data that we pass when sending an email, let’s mess up some data when we send an email. For example, we can pass an empty string to a subject instead of an actual subject. 

When you run the tests again, you’ll notice an AssertionError, telling you what value it received and what value it expected to receive. 

However, if you want to thoroughly test your emails and email sending functionality, you need to ensure:

  • Your HTML/CSS designs are rendered correctly across different browsers and clients.
  • Your emails pass the spam filters and reach the recipient’s main inboxes.
  • Your domain isn’t on any blacklists.

A solution that allows me to do all of the above, and more, is Mailtrap Sandbox, another inseparable part of Mailtrap Email Delivery Platform.

With Mailtrap Sandbox, you can inspect the HTML/CSS of your emails and easily spot and fix any faulty lines of code. By doing this, you ensure your messages look the way you intend them to, whether they’re opened in Outlook, Thunderbird, or even on an iPhone.

After you make sure everything looks good, you can move on to checking your spam score. If you keep it below 5, you can prevent a significant amount of potential email deliverability issues your project/app could face when you move it to production.

Now, let me show you how easy it is to set up Mailtrap Sandbox!

SMTP

For the SMTP approach, all you need to do is create a free Mailtrap account and then navigate to SandboxSandboxesIntegration. Once there, copy/paste the provided fake SMTP server credentials into your Django configuration.

API 

If you want to integrate Mailtrap’s Email API for testing, automation, and testing automated sequences, I recommend creating a new Django project:

mkdir django-api-test-email
django-admin startproject emails_api_test django-api-test-email

In the settings.py, insert the following snippet:

MAILTRAP_HOST = 'sandbox.api.mailtrap.io'
MAILTRAP_API_KEY = 'your_test_api_key'
MAILTRAP_ACCOUNT_ID = 10000
MAILTRAP_TEST_INBOX_ID = 10000

As for the variables, you can find them in the following locations:

  • MAILTRAP_API_KEY – Sandbox > API
  • MAILTRAP_ACCOUNT_ID – Settings > Account settings 
  • TEST_INBOX_ID – In the URL of the page when you open your Sandbox

Then, in your emails-api-test working directory, create a views.py file and copy/paste the following code there:

from django.http import HttpResponse
from django.conf import settings
import http.client
import json

payload = {
    "to": [{"email": "to@test_inbox_id", "name": "Test Inbox"}],
    "from": {"email": "from@example.com", "name": "Example Sender"},
    "custom_variables": {"user_id": settings.MAILTRAP_ACCOUNT_ID, "batch_id": "PSJ-12"},
    "headers": {"X-Message-Source": "dev.mydomain.com"},
    "subject": "Test Email using Mailtrap API",
    "text": "Congrats on sending test email with Mailtrap!",
    "category": "Test"
}

headers = {
    'Content-Type': "application/json",
    'Accept': "application/json",
    'Api-Token': settings.MAILTRAP_API_KEY
}

def send_test_email(request):
    conn = http.client.HTTPSConnection(settings.MAILTRAP_HOST)

    conn.request("POST", f"/api/send/{settings.MAILTRAP_TEST_INBOX_ID}", json.dumps(payload), headers)
    response = conn.getresponse()

    print(response.status, response.reason)
    conn.close()

    if (response.status != 200):
        return HttpResponse(f'Failed to send email: {response.reason}')

    return HttpResponse('Email sent successfully!')

Important: Don’t forget to replace from and to emails with the emails you expect. This way, real users won’t receive any emails.

Finally, insert the following code in the urls.py file: 

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('send-test-email/', views.send_test_email, name='send_test_email'),
]

Conclusion

That was a lot of code, wasn’t it? Hopefully, you now understand how to send emails in Django!

If you don’t know which method to choose, here’s a quick summary table for you:

MethodBest forKey advantage
SMTPSimple integration, quick setupNative Django support + sandbox ready
Email API (SDK)Modern apps, bulk sends, structured workflowsCleaner logic, fewer connection issues
Custom backendLarge Django apps, reusable architectureCentralized SDK logic, scalable design

And if you’re up for more reads, here are some of our other Python-related articles from:

Article by Dmitriy Shcherbakan Full Stack Developer @Railsware
Article by Artem Litvinenko Software Engineer

Software engineer with over 4 years of experience.

Comments

3 replies

will

this does not explain how to recieve emails, blatant lie on the title. shameful

mama

yes!

Hattie

Very nice рost. I just stumbⅼed upon your blog and wisheԀ to sаy that I have truly
enjoyed surfing arоund yߋur bloց pоsts. After all I’ll be subscribing to your rss feeⅾ and I hope you write again very soon!

Comments are closed.