Python: Sending HTML emails via Gmail API or SMTP relay

python-logoAlthough sending a plain text message through an unauthenticated SMTP relay might be easily done with a few lines of Python code, sending rich HTML email through authenticated relays or via the Gmail API using OAuth2 requires a bit of work.

In this article, I will provide sample code for sending rich HTML email with attachments either via SMTP relay or the Gmail API.

Final Goal

Our goal in this article is to send a rich HTML email with attachments that is viewable by a maximum audience.  Below is the result in the Mozilla Thunderbird mail client, but it renders the same in Gmail, Outlook.com, iPhone, and the desktop Outlook client.

Per user settings, the Outlook desktop client will probably not show the external images until the user clicks on message allowing them to be shown.  The same goes for iOS clients, external images don’t load till the user presses on the top bar allowing them to be downloaded.  This cannot be circumvented by using embedded/inline images.

Multipart MIME

Multipart MIME allow us to not only send a rich HTML message, but an alternate text message in case the email client viewer cannot display HTML.  This is also the way attachments are appended to a message.

For this article, I use the following MIME structure:

multipart/mixed
    - multipart/alternative
        - text/plain
        - multipart/related
            - text/html
    - [attachment1]
    - [attachment2]

This generates a rich HTML message with text fallback, and the optional ability to add attachments.  If we were not dealing with attachments, I would simply have used a multipart/alternative with a text/plain and text/html section.

If you ever want to verify whether your MIME packaging lines up with industry standards for maximum client viewing, simply go to your email and view the source of messages sent by major retailers and bulk emailers.

In past years, it was common to embed images (‘cid:’) in the multipart/related section or even Base64 encode the source of an image directly in the HTML, but modern email clients such as Outlook and the iphone iOS don’t display these by default anymore.

SMTP Relays

The first thing to determine if you want to send email is which SMTP relay server you will use.

In a corporate environment, this is usually not as difficult, since email is understood as a required business function.  And if you are behind the firewall, that relay may not even require authentication.

However, if you are working over the public internet at large, it is going to be very difficult to find an open SMTP relay since that would only invite usage by bulk mail and spammers.  There is always the option of choosing a SaaS solution like SendGrid or mailgun that offer authenticated relays to customers.

Another option, and the one I illustrate in this article is using the API of your personal email provider, in this case Google Gmail API to relay the message.

Gmail API Prerequisite

If you are going to use the Gmail API as your relay, then you need to download a “credentials.json” file containing your OAuth2 credentials.

See Google’s Python Quickstart page for instructions.

Example Program

I have added send_html_email.py to github.  Using this program, you can either send a rich HTML email message via an SMTP relay or Gmail API.

First, you need to install the Google OAuth2 libraries using pip.  If you are using python3 on Ubuntu, you might need to substitute ‘pip3’.

# install Google libraries for OAuth2
sudo pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

# get code
git clone https://github.com/fabianlee/python-send-html-email.git
cd python-send-html-email

Then if you are using the Gmail API and have “credentials.json” in the same directory, you can invoke like this:

./send_html_email.py me send.to@gmail.com \
thesubject John google.com \
--attach attachments/test.txt attachments/testdocument.pdf

If you are sending to an unauthenticated relay:

./send_html_email.py myuser@domain.com sendto@domain.com \
thesubject John <relayAddress> \
--attach attachments/test.txt attachments/testdocument.pdf

And if you are sending to an authenticated relay that needs credentials:

./send_html_email.py myuser@domain.com sendto@domain.com \
thesubject John <relayAddress> \
--port=587 --tls --user=myuser@domain.com --password=MyP4ss! \
--attach attachments/test.txt attachments/testdocument.pdf

 

It can take a few minutes for the message to be relayed, but ultimately you should see an email similar to below in your inbox.

 

 

REFERENCES

apanada stackoverflow, multipart alternative versus multipart mixed sending with python 

stackoverflow, sending via google

stackoverflow, using embedded images html/text in alternative with cid

Felix Robles, sending email using gmail api and python

google developers, sending mail via gmail api, python quickstart sample code

google, python quickstart

google, python library reference

sendgrid, options for sending embedded images

timrichardson, gist sending email to googleapi

stackoverflow, when sending mail via googleapi avoiding error “TypeError: a bytes-like object is required, not ‘str'”

mailtrap.io, sending mail in python

campaignmonitor, describes popular email clients and embedded image display