How to Send an Email with boto and SES - Stack Abuse

How to Send an Email with boto and SES


Pretty much every user-based app and website needs to send an email to the user at some point, so eventually you'll have to deal with the joyous world of programmatic emailing. There are quite a few services popping up to help you with this, but with every app having its own unique requirements, few of these services adequately get the job done.

So having faced the problem a few times (and in a few languages), I decided to write up this short post on how to send email with boto and SES. The boto library is a very nice wrapper for Python that helps you interact with the AWS API. To see some more of it's capabilities, check out my other boto article on how to upload a file to s3.


Before you can really do anything, you'll need an AWS account (obviously) along with the access key and secret key to your account, which will be used for authenticating with the SES servers. There are a few different ways you can use the keys to authenticate, but here we'll just be passing them to the connect_to_region method provided by boto.

Next, you'll need to either verify with SES an email address (a Gmail address works just fine) or a domain you own. If you're just testing this feature out, I'd suggest verifying only a single email address since it's a bit quicker. All you need to do is click on a link in the verification email they send you, as opposed to adding TXT records to your zone file for verifying a domain.

If you're just using SES for the first time and your application will need to send out a high volume of emails, then you may need to submit a request to increase your Sending Quota. Your SES account is initially put in to a 'sandbox', which means you can only send 200 emails within a 24 hour period. Once you've successfully requested to increase your sending limits then you should be able to send many more (I was approved for 50,000 per day, which is far more than I need).

The Code

Once you've done the initial setup I mentioned above, you should be able to use the code below to send emails.



class Email(object):
    def __init__(self, to, subject): = to
        self.subject = subject
        self._html = None
        self._text = None
        self._format = 'html'

    def html(self, html):
        self._html = html

    def text(self, text):
        self._text = text

    def send(self, from_addr=None):
        body = self._html

        if isinstance(, basestring):
   = []
        if not from_addr:
            from_addr = '[email protected]'
        if not self._html and not self._text:
            raise Exception('You must provide a text or html body.')
        if not self._html:
            self._format = 'text'
            body = self._text

        connection =

        return connection.send_email(

To use this code, all you need to do is this:

email = Email(to='[email protected]', subject='You are HTML Awesome')
email.text('This is a text body. Foo bar.')
email.html('<html><body>This is a text body. <strong>Foo bar.</strong></body></html>')  # Optional

The email.html() call is optional. If you include both text and HTML in your email then both will be included in the resulting MIME and the email client will show whichever format is supported or preferred by the user.

Using Email Templates

If you want to get really fancy, try using a template engine. Instead of passing the email body string directly, we can load it from a template, much like you would render an HTML page in a web framework like Django.

Here we use the Jinja2 templating engine to handle the template loading and rendering:

from jinja2 import Environment, PackageLoader

# Loads templates from the yourapp.templates folder
env = Environment(loader=PackageLoader('yourapp', 'templates'))


class Email(object):
    def __init__(self, to, subject): = to
        self.subject = subject
        self._html = None
        self._text = None

    def _render(self, filename, context):
        template = env.get_template(filename)
        return template.render(context)

    def html(self, filename, context):
        self._html = self._render(filename, context)

    def text(self, filename, context):
        self._text = self._render(filename, context)

    def send(self, from_addr=None):
        # Same as before...

Note: For production code, don't actually put your AWS security keys directly in your code. Use something like environmental variables instead.

Using this code is similar to before, but instead we'll just pass the template filename and the context from which the template will be populated:

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

email = Email(to='[email protected]', subject='OMG an Email!')
ctx = {'username': user.username}
email.text('email.txt', ctx)
email.html('email.html', ctx)  # Optional

Now you can easily create and render HTML emails just like you do webpages.


Hopefully this short tutorial was useful to you. The code here should be good for most use-cases, although you can get even more advanced by adding Cc, Bcc, reply-to addresses, return paths, or even file attachments.

All of these extra features I just mentioned, except attachments, can be handled by the send_email function. To send attachments you'll have to use the lower-level send_raw_email function, which requires you to construct the MIME message yourself. Don't worry, that isn't too hard thanks to Python's email package, but we'll save that for another article.

Was this useful to you? Did you find a mistake? Let me know in the comments!

Last Updated: October 1st, 2019
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

Want a remote job?

    Prepping for an interview?

    • Improve your skills by solving one coding problem every day
    • Get the solutions the next morning via email
    • Practice on actual problems asked by top companies, like:

    Better understand your data with visualizations

    •  30-day no-questions refunds
    •  Beginner to Advanced
    •  Updated regularly (update June 2021)
    •  New bonus resources and guides

    © 2013-2021 Stack Abuse. All rights reserved.