Stand with Ukraine 🇺🇦 Donate to support

How to Send an Email in Python

On September 28, 2022
12min read
Aleksandr Varnin Full Stack Developer @ Railsware
How to Send an Email in Python

What do you need in order to send an email with Python? Some basic programming and web knowledge along with elementary Python skills. We assume you’ve already had a web app built with this language and now you need to extend its functionality with notifications or other email sending options. 

With numerous code examples, in this 2022 tutorial, we’ll guide you through the fundamental steps of sending different types of emails via an SMTP (Simple Mail Transfer Protocol) server using Python. 

Note: Written and tested on Python 3.6.9. See more Python-related repositories on Github.

What email sending options with Python are there?

The two primary ways of sending emails in Python are using an SMTP method and the second one is a transactional email service. Let’s take a closer look at the two and unpack how they work. 

Send emails with Python and SMTP

The first good news about Python is that in its standard library there is a built-in smtplib module that is used for sending emails via SMTP connection. The module uses the standard RFC 821 protocol, so no extra installations or tricks are required. 

Send emails with Python using Transactional Email Services

If you decide to set up the sending functionality in your Python app without built-in SMTP, you can integrate third-party transactional email API. 

One of the main reasons to go this route is if a strong deliverability rate is a critical factor for you. Most third-party API services have analytical tools and provide scalability opportunities you might need as your project grows. 

Sending emails in Python: A Step-by-Step Guide

How to send emails using SMTP

The built-in smtplib module can be imported using the following statement:

import smtplib

To send an email later, create one SMTP object:

smtpObj = smtplib.SMTP( [host [, port]] )

Parameter details: 

  • host − this is an optional argument and is the host running your SMTP server. The IP address of the host or a domain name can be specified. 
  • port − if the host argument is specified, specify a port, where the SMTP server is listening. 
  • local_hostname − if the used SMTP server is running on your local machine, specify localhost

An SMTP object has an instance method called sendmail that is used to send a message and has three parameters:

  • sender − string with the address of the sender.
  • receivers − list of strings, one for each recipient.
  • message − a message as a string formatted as specified in RFCs.

smtpObj.sendmail(sender, receivers, message)

To make sure that the email module has been imported properly and get the full description of its classes and arguments, type in an interactive Python session:

help(smtplib)

Refer to Python documentation to further review the rest of the SMTP Objects (e.g., smtp.ehlo; smtp.send_message(msg) etc.) and how to apply them.

Below you can find an example of a simple Python script that shows how one can send an email from a local SMTP. 

import smtplib
sender = 'from@example.com'
receivers = ['to@example.com']

message = """From: From Person <from@example.com>
To: To Person <to@example.com>
Subject: SMTP email example


This is a test message.
"""
try:
   smtpObj = smtplib.SMTP('localhost')
   smtpObj.sendmail(sender, receivers, message)         
   print("Successfully sent email")
except SMTPException:

However, this code wouldn’t work.

Firstly, you’ll need an actual working SMTP server. Secondly, most recipients reject emails from untrusted sources. On top of that, getting all the relevant validations and certifications for other servers to approve your emails is not easy.

Using a third-party API for sending functionality in your Python app is the best way to go about it. One like Mailtrap Email API, where the tedious work of ensuring your emails get delivered is already done, and there is no need to set up your own SMTP server. 

How to send emails using Email API

With the help of actionable analytics, our Email API helps devs gain full control over their email deliverability, allowing a sending throughput of roughly 10,000 emails a second.

Before you’re able to send out any type of email with Mailtrap, you’ll need to connect and verify your domain name. Check out our detailed guide and a bonus video instruction on how to do this.

  1. Once you’ve set the domain, in the Email API section, from the Sending Domains tab, select API/SMTP Integration.
  2. Choose API, and from the drop-down menu, select Python.
  3. Copy the code that will be displayed and run it.

Try Mailtrap for Free

How to send HTML emails in Python?

In most cases, you need to add some formatting, links, or images to your email notifications. We can simply put all of these with the HTML content. For this purpose, Python has an email package. 

We will deal with the MIME message type, which can combine HTML/CSS and plain text. In Python, it is handled by the email.mime module. 

It is better to write a text version and an HTML version separately and then merge them with the MIMEMultipart("alternative") instance. It means that such a message has two rendering options accordingly. In case an HTML isn’t rendered successfully for some reason, a text version will still be available. 

Input:

# import the necessary components first

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap
sender_email = "mailtrap@example.com"
receiver_email = "new@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "multipart test"
message["From"] = sender_email
message["To"] = receiver_email
# write the text/plain part
text = """\
Hi,
Check out the new post on the Mailtrap blog:
SMTP Server for Testing: Cloud-based or Local?
https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server/
Feel free to let us know what content would be useful for you!"""
# write the HTML part
html = """\
<html>
  <body>
    <p>Hi,<br>
       Check out the new post on the Mailtrap blog:</p>
    <p><a href="https://blog.mailtrap.io/2018/09/27/cloud-or-local-smtp-server">SMTP Server for Testing: Cloud-based or Local?</a></p>
    <p> Feel free to <strong>let us</strong> know what content would be useful for you!</p>
  </body>
</html>
"""
# convert both parts to MIMEText objects and add them to the MIMEMultipart message
part1 = MIMEText(text, "plain")
part2 = MIMEText(html, "html")
message.attach(part1)
message.attach(part2)
# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login(login, password)
    server.sendmail(
        sender_email, receiver_email, message.as_string()
    )
print('Sent') 

Output:

How to send emails with attachments?

The next step in mastering sending emails with Python is attaching files. Attachments are still the MIME objects, but we need to encode them with the base64 module that encodes all binary data into ASCII characters.

A couple of important points about the attachments:

  1. Python lets you attach standard .txt, rich text format, and all other main text files, images, audio files, and even applications. You just need to use the appropriate email class like:

    email.mime.audio.MIMEAudio or email.mime.image.MIMEImage
  2. For the full information, refer to this section of the Python documentation. Also, you can check the examples provided by Python for a better understanding. 
  3. Remember about the file size: sending files over 20MB is a bad practice. 

In transactional emails, PDF files are the most frequently used: we usually get receipts, tickets, boarding passes, order confirmations, and many other authentication-related documents. So let’s review how to send a boarding pass as a PDF file. 

Input:

import smtplib

# import the corresponding modules
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

subject = "An example of boarding pass"
sender_email = "mailtrap@example.com"
receiver_email = "new@example.com"

message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Add body to email
body = "This is an example of how you can send a boarding pass in attachment with Python"
message.attach(MIMEText(body, "plain"))

filename = "yourBP.pdf"
# Open PDF file in binary mode

# We assume that the file is in the directory where you run your Python script from
with open(filename, "rb") as attachment:
    # The content type "application/octet-stream" means that a MIME attachment is a binary file
    part = MIMEBase("application", "octet-stream")
    part.set_payload(attachment.read())

# Encode to base64
encoders.encode_base64(part)

# Add header 
part.add_header(
    "Content-Disposition",
    f"attachment; filename= {filename}",
)

# Add attachment to your message and convert it to string
message.attach(part)
text = message.as_string()

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login(login, password)
    server.sendmail(
        sender_email, receiver_email, text
    )
print('Sent') 

Output:

To attach several files, you can call the message.attach() method several times.

How to send emails to multiple recipients using Python?

Sending multiple emails to different recipients and making them personal is the special thing about emails in Python. 

To add several more recipients, you can just type their addresses separated by a comma and add CC and BCC. But if you work with bulk email sending, Python will save you with loops. 

One of the options is to create a database in a .csv format (we assume it is saved to the same folder as your Python script). 

We often see our names in transactional or even promotional examples. Here is how we can make it with Python.

Let’s organize the list in a simple table with just two columns: name and email address. It should look like the following example:

#name,email
John Johnson,john@johnson.com
Peter Peterson,peter@peterson.com

The code below will open the file and loop over its rows line by line, replacing the {name} with the value from the “name” column.

Input:

import csv, smtplib

port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

message = """Subject: Order confirmation
To: {recipient}
From: {sender}

Hi {name}, thanks for your order! We are processing it now and will contact you soon"""
sender = "new@example.com"
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login(login, password)
    with open("contacts.csv") as file:
        reader = csv.reader(file)
        next(reader)  # it skips the header row
        for name, email in reader:
            server.sendmail(
               sender,
                email,
                message.format(name=name, recipient=email, sender=sender)
            )
            print(f'Sent to {name}')

After running the script, we get the following response:

Sent to John Johnson
Sent to Peter Peterson
>>> 

Output:

In our Mailtrap inbox, we see two messages: one for John Johnson and another for Peter Peterson, delivered simultaneously:

How to send emails with images? 

Images, even if they are a part of the email message body, are still attachments as well. There are three types of them:

  • CID attachments (embedded as a MIME object) 
  • Base64 images (inline embedding)
  • Linked images 

We have described their peculiarities, pros and cons, and compatibility with most email clients in this post

To add a CID attachment, we will create a MIME multipart message with MIMEImage component:

# import all necessary components
import smtplib
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

port = 2525
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

sender_email = "mailtrap@example.com"
receiver_email = "new@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "CID image test"
message["From"] = sender_email
message["To"] = receiver_email

# write the HTML part
html = """\
<html>
 <body>
   <img src="cid:Mailtrapimage">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# We assume that the image file is in the same directory that you run your Python script from
fp = open('mailtrap.jpg', 'rb')
image = MIMEImage(fp.read())
fp.close()

# Specify the  ID according to the img src in the HTML part
image.add_header('Content-ID', '<Mailtrapimage>')
message.attach(image)

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

Output:

The CID image is shown as part of the HTML message and as an attachment. Messages with this image type are often considered spam: check the Analytics tab in Mailtrap to see the spam rate and recommendations for improvement. In most cases, many email clients  – Gmail in particular – don’t display CID images. So let’s review how to embed a base64 encoded image.

Here we will use base64 module and experiment with the same image file:

# import the necessary components first
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import base64

port = 2525
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap

sender_email = "mailtrap@example.com"
receiver_email = "new@example.com"
message = MIMEMultipart("alternative")
message["Subject"] = "inline embedding"
message["From"] = sender_email
message["To"] = receiver_email

# We assume that the image file is in the same directory that you run your Python script from
encoded = base64.b64encode(open("mailtrap.jpg", "rb").read()).decode()

html = f"""\
<html>
 <body>
   <img src="data:image/jpg;base64,{encoded}">
 </body>
</html>
"""

part = MIMEText(html, "html")
message.attach(part)

# send your email
with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
   server.login(login, password)
   server.sendmail(
       sender_email, receiver_email, message.as_string()
   )
print('Sent')

Output:

Now the image is embedded into the HTML message and is not available as an attached file. Python has encoded our jpg image, and if we go to the HTML Source tab, we will see the long image data string in the img src

Sending emails with Python via Gmail

You can configure your production server when ready to send your custom emails to a real recipient’s email address. It also depends on your needs, goals, and preferences: your local host or any external SMTP. 

One of the most popular options is Gmail so let’s take a closer look at it. 

We often see titles like “How to set up a Gmail account for development”. It means you will create a new Google account and use it for a particular purpose. 

To be able to send emails via your Gmail account, you need to provide access to it for your application. You can Allow less secure apps or take advantage of the OAuth2 authorization protocol. It’s way more difficult but recommended due to security reasons. 

Further, to use Gmail’s SMTP server, you need to know:

  • the server name = smtp.gmail.com
  • port = 465 for SSL/TLS connection (preferred)
  • or port = 587 for STARTTLS connection
  • username = your Gmail email address
  • password = your password
import smtplib, ssl

port = 465  
password = input("your password")
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login("my@gmail.com", password)

If you tend to simplicity, then you can use Yagmail, the dedicated Gmail/SMTP. It makes email sending really easy. Just compare the above examples with these several lines of code:

import yagmail
yag = yagmail.SMTP()
contents = [
    "This is the body, and here is just text http://somedomain/image.png",
    "You can find an audio file attached.", '/local/path/to/song.mp3'
]
yag.send('to@someone.com', 'subject', contents)

Test emails with Mailtrap Email Sandbox

When creating a new app or adding any functionality, especially when doing it for the first time, it’s essential to experiment on a test server. Here is a brief list of reasons:

  1. You won’t hit your friends’ and customers’ inboxes. This is vital when you test bulk email sending or work with an email database.
  2. You won’t flood your inbox with testing emails. 
  3. Your domain won’t be blacklisted for spam.

A testing SMTP server environment imitates the work of a real 3rd party web server. In the following examples, we’ll use Mailtrap Email Sandbox, which allows devs to capture SMTP traffic from staging and inspect and debug emails before they go out to actual recipients.

On top of that, the Email Sandbox can help validate your HTML/CSS, analyze the email’s content, and give a relevant spam score. The Sandbox is easy to set up; all you need is to copy the credentials generated by the app and paste them into your code. 

Here is how it looks in practice:

import smtplib
port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # your password generated by Mailtrap

Mailtrap makes things even easier. Go to the Integrations section in the SMTP settings tab and get the ready-to-use template of the simple text message with your Mailtrap credentials. The most basic option for instructing your Python code on who sends what to who is the sendmail() instance method:

The code snippet looks pretty straightforward, right? Let’s take a closer look at it and add some error handling (see the #explanations in between). To catch errors, we use the “try” and “except” blocks. Refer to the documentation for the list of exceptions here. 

# the first step is always the same: import all necessary components:
import smtplib
from socket import gaierror
# now you can play with your code. Let’s define the SMTP server separately here:
port = 2525 
smtp_server = "smtp.mailtrap.io"
login = "1a2b3c4d5e6f7g" # paste your login generated by Mailtrap
password = "1a2b3c4d5e6f7g" # paste your password generated by Mailtrap
# specify the sender’s and receiver’s email addresses
sender = "from@example.com"
receiver = "mailtrap@example.com"
# type your message: use two newlines (\n) to separate the subject from the message body, and use 'f' to  automatically insert variables in the text
message = f"""\
Subject: Hi Mailtrap
To: {receiver}
From: {sender}

This is my first message with Python."""

try:
    #send your message with credentials specified above
    with smtplib.SMTP(smtp_server, port) as server:
        server.login(login, password)
        server.sendmail(sender, receiver, message)
    # tell the script to report if your message was sent or which errors need to be fixed 
    print('Sent')
except (gaierror, ConnectionRefusedError):
    print('Failed to connect to the server. Bad connection settings?')
except smtplib.SMTPServerDisconnected:
    print('Failed to connect to the server. Wrong user/password?')
except smtplib.SMTPException as e:
    print('SMTP error occurred: ' + str(e))

Once you get the Sent result in Shell, you should see your message in your Mailtrap inbox:

If you prefer working in the local environment, the local SMTP debugging server might be an option. For this purpose, Python offers an smtpd module. It has a DebuggingServer feature, which will discard messages you are sending out and will print them to stdout. It is compatible with all operations systems.

Set your SMTP server to localhost:1025

python -m smtpd -n -c DebuggingServer localhost:1025

In order to run SMTP email server on port number 25, you’ll need root permissions:

sudo python -m smtpd -n -c DebuggingServer localhost:25

It will help you verify whether your code is working and point out the possible problems if there are any. However, it won’t allow you to check how your HTML email template is rendered.

Next steps with emails in Python

We have demonstrated just basic options of sending emails with Python to describe the logic and a range of its capabilities. We recommend reviewing the Python documentation and experimenting with your own code to get great results!

There are a bunch of various Python frameworks and libraries which make creating apps more elegant and dedicated. In particular, some of them can help improve your experience with building email sending functionality:

The most popular frameworks are:

  1. Flask, which offers a simple interface for email sending— Flask Mail. Feel free to learn more in our guide on how to send emails with Flask.
  2. Django can be a great option for building HTML templates. Also, take a look at our Django email sending tutorial.
  3. Zope comes in handy for website development. 
  4. Marrow Mailer is a dedicated mail delivery framework adding various helpful configurations.
  5. Plotly and its Dash can help with mailing graphs and reports.

Good luck, and don’t forget to stay on the safe side when sending your emails!

Article by Aleksandr Varnin Full Stack Developer @ Railsware

Comments

1 replies

Chris

You can send emails with attachments using Apprise. It supports Google, Hotmail, Proton Mail, and even custom severs right out of the box. Best of all, it’s free; and open source. See https://github.com/caronc/apprise

Comments are closed.