Sending dynamic emails with Python/Django and SendGrid

Sending dynamic emails with Python/Django and SendGrid
Sending dynamic emails with Python/Django and SendGrid

In this article, we'll cover how to send emails with Python using Sendgrid's dynamic templates. Normally, we used to set up email backend for Django project also keeping our templates inside the specific directory using jinja template syntax. However, with dynamic templates, you only need to define rendered data that will be handled by SendGrid.

Create a new template

You'll need to create a Sendgrid account with your business domain. Once you access your dashboard, navigate to Email API > Dynamic Templates and then create a new template. Sendgrid allows versioning of templates, so simply, add a version that is going to be an actual template.

If you don't have any test email template I would recommend using the below:

GitHub - leemunroe/responsive-html-email-template: A free simple responsive HTML email template
A free simple responsive HTML email template. Contribute to leemunroe/responsive-html-email-template development by creating an account on GitHub.

Copy the content of email-inlined.html and paste it inside Sendgrid's email template code editor.

Next, you should make changes in the template by adding placeholders that will render particular data. In Sendgrid's official documentation those placeholders are named as handlebars that can be used inside templates with a wide range of functionalities.

Once it's created, you'll be able to see Template ID which defines a specific template.


First, let's install a python client for Sendgrid:

pip install sendgrid

It will allow us to interact with Sendgrid API using Python.

Next, we're going to create a reusable class that will include a set of email utility functions by using sendgrid package such as adding attachments, setting dynamic content, and sending emails.

import logging
from typing import List

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Attachment, ContentId, Disposition, FileContent, FileName, FileType, Mail

SENDGRID_API_KEY = "YOUR_API_KEY" #(SG.randomcharacters)

class SendgridClient:
    logger = logging.getLogger(__name__)
    logging.basicConfig(level = logging.INFO)

    def __init__(self, recipients: List, sender: str = DEFAULT_SENDER):
        self.sendgrid_client = SendGridAPIClient(SENDGRID_API_KEY)
        self.mail = Mail(

    def set_template_data(self, data: dict):
        self.mail.dynamic_template_data = {

    def set_template_id(self, template_id: str):
        self.mail.template_id = template_id
    def set_attachment(self, filename: str, content: bytes, **kwargs):
        file_type = kwargs.get("type", "application/pdf")
        content_id = kwargs.get("content_id", "Invoice PDF")
        attachment = Attachment()
        attachment.file_content = FileContent(content)
        attachment.file_type = FileType(file_type)
        attachment.file_name = FileName(filename)
        attachment.disposition = Disposition('attachment')
        attachment.content_id = ContentId(content_id)
        self.mail.attachment = attachment

    def send(self):
        except Exception as e:

We're initializing Mail instance with sender addresses and a list of recipients. The other functions are preparing the email content and then sending it to recipients.

Template data should be dictionary and keys must be the same as handlebar names:

email_data = {
    "first_name": user.first_name # {{first_name}},
    "email": # {{email}}

Note that, you always have to include Template ID  as part of an API call to identify which template should be used for an email request. IDs can be found inside SendGrid under the email template name starting with d-.

If you're going to use it in Django, I would suggest putting it inside utils a directory named Then, use different parts of the codebase as required by initialling the client and also setting the dynamic data. Feel free to extend this class as you need.

Support 🌏

If you feel like you unlocked new skills, please share them with your friends and subscribe to the youtube channel to not miss any valuable information.