Blog

How to send emails in Python (with script examples)

Tautvydas Tijūnaitis Tautvydas Tijūnaitis
· 25 min read · Tips and resources · July 25th, 2024
Thinking about using Python to send emails? The versatile programming language is a good choice for sending emails due to the various libraries available, not to mention its ease of use.

Python is a high-level language used for creating a variety of programs. Due to its readability and simplicity, it’s a preferred language for both beginners and experienced developers. Another perk is Python’s rich standard library that provides numerous functions and modules, reducing the need to write code from scratch and, as a result, the time needed for development.

When it comes to sending emails programmatically, Python offers several options to simplify the process. These include its built-in library for sending emails and simple integration with third-party libraries for added reliability and functionality. So let’s take a look at how you send emails in a Python environment via API and SMTP (Simple Mail Transfer Protocol).

Why use Python to send emails?

Aside from Python’s clean and simple syntax, which makes it easy to create and maintain email-sending scripts, and the built-in smtplib library and email package, we think Python has some other nice benefits that make it a great choice for automating email tasks.

1.  Flexibility and customization options: With Python, you can easily customize email content dynamically, send HTML emails, and use various MIME types.

2. Easy integration with other services: Python very easily integrates with APIs, databases and more so you can send emails based on data from numerous sources. You can connect with third-party libraries and services, like MailerSend, for more advanced functionality including tracking, analytics, bulk sending, and more.

3. Scalability: You can scale Python scripts to handle large volumes of emails so as your sending requirements grow, your implementation can be easily adapted. 

4. Security protocols: Secure email sending is supported with TLS/SSL to ensure emails are encrypted and secure. 

5. Large community: Python is open-source and has a large online community in which you can access tutorials, forums, and extensive documentation for troubleshooting and help with implementation. 

Python scripts are effective for automating tasks—just like sending emails. So this makes it a good solution for sending automated notifications and alerts, automated reports, and transactional emails such as order confirmations, password resets, shipping notifications, and more.

Choosing between an email API or smtplib (SMTP)

Python’s built-in smtplib library is a great solution for simple, custom email-sending implementations. It provides full control over email content, is pretty cost-effective, and of course has direct integration into Python scripts. 

But if you want more advanced features and functionality such as tracking, analytics, reporting, email template builders and more, plus increased reliability and scalability, an email API is the way to go. 

Here’s a quick comparison of the two:

Feature

smtplib

Third-Party Email API

Ease of Setup

Simple setup using built-in libraries

Simple, requires API key and setup with provider

Email Customization

High - Full control over email content

High - Supports templates and personalization

Security

Supports TLS/SSL

Advanced security features, compliance tools, plus TLS/SSL

Reliability

Depends on SMTP server stability

High reliability with built-in retries

Scalability

Limited by server and connection handling

Highly scalable, designed for high volumes of emails

Tracking and Analytics

Minimal, requires additional implementation and tools

Built-in tracking, analytics, and reporting

Cost

Free (excluding SMTP server costs)

May involve costs based on usage tiers

Integration

Manual integration with Python code

Easy integration with SDKs and RESTful APIs

Error Handling

Basic error handling

Advanced error handling and logging

Rate Limiting

Depends on SMTP server

Managed rate limiting and throttling

Documentation and Support

Extensive Python documentation

Provider-specific documentation and support

Compliance

Basic compliance handled manually

Compliance features included (e.g., GDPR, CAN-SPAM)

Attachments and MIME Types

Full support via email library

Full support with simpler interfaces

Deployment

Runs anywhere Python is supported

Runs anywhere with API access, often with SDKs

How to send emails with Python

Now we’ve been through the features and limitations of using smtplib and an email API, we’ll show you how to send emails with both methods, beginning with setting up smtplib and then using your MailerSend SMTP credentials to send emails. 

If you’d like to try this out with MailerSend, you can sign up for free and access a trial domain for testing purposes.

For SMTP:

1. Create an account for free and sign in.

2. From the dashboard, click Domains in the left-hand menu. If you’d like to use your own domain, you can add one by clicking Add domain, or you can use the trial domain.

3. Next to the domain you want to use, click Manage. Scroll down to the SMTP section and click Generate new user. A pop-up will appear, enter a recognizable name for your new SMTP user then click Save user.

4. Your SMTP user credentials will be generated and will include a username, password, server address, and port number. Click Save user. You can then save the credentials somewhere or access them at any time by clicking Manage.

Sending emails with the smtplib module

To get started with smtplib in Python, you’ll want to create some environment variables to store any sensitive information such as login credentials. This will prevent you from exposing this information in your code. 

Set the environment variables in your terminal or by using a .env file in your project directory. 

In your terminal:

export SENDER_EMAIL="youremail@example.com" 
export EMAIL_PASSWORD="yourpassword" 
export SMTP_SERVER="smtp.mailersend.net" 
export SMTP_PORT="587"

With a .env file:

Create a new .env file in your project directory with the following:

SENDER_EMAIL=youremail@example.com 
EMAIL_PASSWORD=yourpassword 
SMTP_SERVER=smtp.mailersend.net 
SMTP_PORT=587

Then use the python-dotenv package to load the .env file:

pip install python-dotenv

You can then use os.getenv in your script to fetch your sensitive information via the environment variables. 

In the script, we’ll import the required libraries, these being os for accessing the environment variables, smtplib for handling the SMTP connection, and email.mime classes for creating the email content.

import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Set up email details from environment variables
sender_email = os.getenv("SENDER_EMAIL")
password = os.getenv("EMAIL_PASSWORD")
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT"))

# Define the receiver email
receiver_email = "receiver@example.com"

subject = "Welcome to Amazing Company"
body = """\
Hey there!

Thanks for joining us at Amazing Company. Your email has been verified and your account has been created.
Head to the website to login and start using our features."""

# Create the email message
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Attach the email body to the message
message.attach(MIMEText(body, "plain"))

# Establish a connection to the SMTP server and send the email
try:
    # Connect to the SMTP server
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()  # Secure the connection
        server.login(sender_email, password)  # Log in to the SMTP server
        server.sendmail(
            sender_email, receiver_email, message.as_string()
        )  # Send the email
    print("Email sent successfully")
except Exception as e:
    print(f"Error: {e}")

Send an HTML email with smtplib

By making a few tweaks to the script, you can send HTML emails instead of plain text.

import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Set up email details from environment variables
sender_email = os.getenv("SENDER_EMAIL")
password = os.getenv("EMAIL_PASSWORD")
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT"))

# Define the receiver email
receiver_email = "receiver@example.com"

subject = "Welcome to Amazing Company"
body = "Hey there! Thanks for joining us at Amazing Company. Your email has been verified and your account has been created. Head to the website to login and start using our features."

# HTML content
html_content = """
<html>
  <body>
    <h1>Hey there! Thanks for joining us at Amazing Company.</h1>
    <p>Your email has been verified and your account has been created. Head to the website to login and start using our features.</p>
  </body>
</html>
"""

# Create the email message
message = MIMEMultipart("alternative")
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Attach the plain text and HTML parts to the message
text_part = MIMEText(body, "plain")
html_part = MIMEText(html_content, "html")

message.attach(text_part)
message.attach(html_part)

# Establish a connection to the SMTP server and send the email
try:
    # Connect to the SMTP server
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()  # Secure the connection
        server.login(sender_email, password)  # Log in to the SMTP server
        server.sendmail(sender_email, receiver_email, message.as_string())  # Send the email
    print("Email sent successfully")
except Exception as e:
    print(f"Error: {e}")

How to send an email with an attachment with smtplib

Let’s say we want to send an invoice to our recipient. We can send the invoice as an attachment by including the file as a MIME part of the message.

import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Set up email details from environment variables
sender_email = os.getenv("SENDER_EMAIL")
password = os.getenv("EMAIL_PASSWORD")
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT"))

# Define the receiver email
receiver_email = "receiver@example.com"  # Replace with the actual recipient's email address

subject = "Here’s your invoice for July"
body = "We have received your payment for your subscription with Amazing Company for the month of July. Please find attached your invoice."

# HTML content
html_content = """
<html>
  <body>
    <p>We have received your payment for your subscription with Amazing Company for the month of July. Please find attached your invoice.</p>
  </body>
</html>
"""

# Create the email message
message = MIMEMultipart("alternative")
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject

# Attach the plain text and HTML parts to the message
text_part = MIMEText(body, "plain")
html_part = MIMEText(html_content, "html")
message.attach(text_part)
message.attach(html_part)

# Attach a file
filename = "invoice.pdf"  # Replace with your file path
try:
    with open(filename, "rb") as attachment:
        part = MIMEBase("application", "octet-stream")
        part.set_payload(attachment.read())
        encoders.encode_base64(part)
        part.add_header(
            "Content-Disposition",
            f"attachment; filename={filename}",
        )
        message.attach(part)
except FileNotFoundError:
    print(f"File {filename} not found. No attachment will be sent.")

# Establish a connection to the SMTP server and send the email
try:
    # Connect to the SMTP server
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()  # Secure the connection
        server.login(sender_email, password)  # Log in to the SMTP server
        server.sendmail(sender_email, receiver_email, message.as_string())  # Send the email
    print("Email sent successfully")
except Exception as e:
    print(f"Error: {e}")

How to send emails in bulk with smtplib

For some types of emails, it might make more sense to send them in bulk. For example, non-time-sensitive emails such as feedback requests. Here’s how you can do that:

import os
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Set up email details from environment variables
sender_email = os.getenv("SENDER_EMAIL")
password = os.getenv("EMAIL_PASSWORD")
smtp_server = os.getenv("SMTP_SERVER")
smtp_port = int(os.getenv("SMTP_PORT"))

# List of recipient emails
receiver_emails = [
    "recipient1@example.com",
    "recipient2@example.com",
    "recipient3@example.com",
    "recipient4@example.com",
]  # Add more recipients as needed

subject = "Give us your feedback!"
body = "We want to hear from you! We’d love to learn more about your experience with Amazing Company. It’ll only take a few minutes and you can get started by clicking here."

# HTML content
html_content = """
<html>
  <body>
    <h1>We want to hear from you!</h1>
    <p>We’d love to learn more about your experience with Amazing Company. It’ll only take a few minutes and you can get started by clicking <a href="https://www.amazing-company.com/feedback">here</a>.</p>
  </body>
</html>
"""

# Function to create the email message
def create_message(sender_email, receiver_email, subject, body, html_content):
    message = MIMEMultipart("alternative")
    message["From"] = sender_email
    message["To"] = receiver_email
    message["Subject"] = subject

    # Attach the plain text and HTML parts to the message
    text_part = MIMEText(body, "plain")
    html_part = MIMEText(html_content, "html")
    message.attach(text_part)
    message.attach(html_part)

    return message


# Establish a connection to the SMTP server and send emails in bulk
try:
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()  # Secure the connection
        server.login(sender_email, password)  # Log in to the SMTP server

        for receiver_email in receiver_emails:
            message = create_message(
                sender_email, receiver_email, subject, body, html_content
            )
            server.sendmail(
                sender_email, receiver_email, message.as_string()
            )  # Send the email
            print(f"Email sent successfully to {receiver_email}")

except Exception as e:
    print(f"Error: {e}")

receiver_emails allows you to add a list of recipients to send the emails to. The SMTP connection is established once and the emails are sent in a loop to each recipient in the list.

Sending emails in Python with an email API

Using a third-party email API is a great option if you want to create more advanced workflows with additional email-sending features and tracking options. MailerSend also provides an official Python SDK, making the implementation straightforward and efficient. 

If you’d like to try this out MailerSend’s email API, you can sign up for free and use a trial domain for testing purposes. It only takes a few minutes to sign up and you can start sending immediately.

To use the API:

1. Create an account for free and sign in.

2. From the dashboard, click Domains in the left-hand menu. If you’d like to use your own domain, you can add one by clicking Add domain, or you can use the trial domain.

3. Next to the domain you want to use, click Manage. In the API section, click Generate new token. A pop-up will appear, enter a recognizable name for your new API token, set the permissions, and then click Create token.

4. Your API key will be generated and displayed. Make sure you copy and save it somewhere, or download it, as you won’t be able to access this again for security purposes.

Check out our API docs

To get started, install MailerSend’s Python SDK in your Python app:

$ python -m pip install mailersend
Note:

Python 3.6.1 or higher is required.

You’ll also need to install the requests library to make HTTP requests to the MailerSend API:

pip install requests

Next, we recommend using environment variables for your API key and other sensitive information to avoid exposing it in your code. 

You can do this in your Terminal:

export MAILERSEND_API_KEY="your_api_key"

Or by creating a .env file with the following in your project directory:

MAILERSEND_API_KEY=your_api_key

How to send an HTML email with an email API and Python

Let’s send a simple HTML email using Python and the MailerSend email API. For this example, we’ll send a simple account confirmation email to our customer, Brian. 

Here’s the Python script:

from mailersend import emails
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv("MAILERSEND_API_KEY"))

# Define an empty dict to populate with mail values
mail_body = {}

# Define the sender email details
mail_from = {
    "name": "Amazing Company",
    "email": "hello@amazingcompany.com",
}

# Define the recipient email details
recipients = [
    {
        "name": "Brian Griffin",
        "email": "brian@example.com",
    }
]

# Define the reply-to email details
reply_to = {
    "name": "Customer Support Replies",
    "email": "reply@amazingcompany.com",
}

# Set the sender email details
mailer.set_mail_from(mail_from["email"], mail_body)

# Set the recipient email details
mailer.set_mail_to(recipients, mail_body)

# Set the subject of the email
mailer.set_subject("Your account has been created!", mail_body)

# Set the HTML content of the email
mailer.set_html_content(
    "<h1>Welcome to Amazing Company!</h1> "
    "<p>Thanks for joining us, we’re so happy to have you. Your account is ready to go.</p>"
    "<p>To get started, login to your dashboard and head to your profile.</p>"
    "<p>Best regards, The Amazing Company Team</p>",
    mail_body,
)

# Set the plain text content of the email
mailer.set_plaintext_content(
    "Welcome to Amazing Company! Thanks for joining us, we’re so happy to have you. "
    "Your account is ready to go. To get started, login to your dashboard and head to your profile. "
    "Best regards, The Amazing Company Team",
    mail_body,
)

# Set the reply-to email details
mailer.set_reply_to(reply_to["email"], mail_body)

# Send the email and print the response (status code and data)
try:
    response = mailer.send(mail_body)
    print("Status Code:", response.status_code)
    print("Response JSON:", response.json())
except Exception as e:
    print(f"An error occurred: {e}")

How to send a template-based email

For more professional and better-looking emails, it’s always best to use a well-designed template. You can easily create great-looking, functional templates in one of MailerSend’s 3 email template builders and then simply copy the template ID and add it to your script. Let’s send an email verification templated email to another customer, Stewart.

Here’s how the script for that would look:

from mailersend import emails
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv("MAILERSEND_API_KEY"))

# Define the sender email details
mail_from = {
    "name": "Amazing Company",
    "email": "hello@amazingcompany.com",
}

# Define the recipient email details
recipients = [
    {
        "name": "Stewart Griffin",
        "email": "stewart@example.com",
    }
]

# Define the variables for personalization
variables = {
    "email": "stewart@example.com",
    "substitutions": [
        {"var": "foo", "value": "bar"},
    ],
}

# Set the sender email details
mailer.set_mail_from(mail_from["email"])

# Set the recipient email details
mailer.set_mail_to(recipients)

# Set the subject of the email (if needed, otherwise the template's subject will be used)
mailer.set_subject("Verify your email address to access all features.")

# Set the template ID
mailer.set_template("templateID")

# Set personalization variables
mailer.set_personalization(variables)

# Send the email and print the response (status code and data)
try:
    response = mailer.send()
    print("Status Code:", response.status_code)
    print("Response JSON:", response.json())
except Exception as e:
    print(f"An error occurred: {e}")

How to send emails with personalization

Using personalization in your transactional emails is key to the customer journey. Not only does it allow you to provide users and customers with a more personal experience, but it also allows you to deliver personalized, important details about their purchases and activity in an efficient way. You can use a single email template and personalize it to thousands of customers. 

Brian has gone ahead and placed an order, so we’ll send them a personalized order confirmation email. We’ll use a loop structure to dynamically insert the products’ details.

Here’s the script:

from mailersend import emails
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY'))

# Define the sender email details
mail_from = {
    "name": "Amazing Company Orders",
    "email": "orders@amazingcompany.com",
}

# Define the recipient email details
recipients = [
    {
        "name": "Brian Griffin",
        "email": "brian@example.com",
    }
]

# Define the personalization data
personalization = [
    {
        "email": "brian@example.com",
        "data": {
            "name": "Brian Griffin",
            "var": "value",
            "boolean": True,
            "object": {
                "key": "object-value"
            },
            "number": 2,
            "array": [
                1,
                2,
                3
            ],
            "purchased_products": [
                {"name": "Product 1", "price": 19.99},
                {"name": "Product 2", "price": 29.99}
            ]
        }
    }
]

# Set the sender email details
mailer.set_mail_from(mail_from['email'])

# Set the recipient email details
mailer.set_mail_to(recipients)

# Set the subject of the email
mailer.set_subject("Your Amazing Company order is confirmed!")

# Set the HTML content of the email
mailer.set_html_content("""
<html>
  <body>
    <h1>We’ve got your order.</h1>
    <p>Hi, {{name}}!</p>
    <p>Thanks for shopping at Amazing Company. Your order is currently processing and will be shipped to you within 24 hours.</p>
    <p>We’ll update you with a tracking number when it’s on the way.</p>
    <ul>
      {% for product in purchased_products %}
        <li>{{product.name}} - ${{product.price}}</li>
      {% endfor %}
    </ul>
  </body>
</html>
""")

# Set the plain text content of the email
mailer.set_plaintext_content("""
Hello {{name}},

Thank you for purchasing the following products:

{% for product in purchased_products %}
    - {{product.name}} - ${{product.price}}
{% endfor %}
""")

# Set personalization variables
mailer.set_personalization(personalization)

# Send the email and print the response (status code and data)
try:
    response = mailer.send()
    print("Status Code:", response.status_code)
    print("Response JSON:", response.json())
except Exception as e:
    print(f"An error occurred: {e}")

How to send an email with an attachment

Since our customer placed an order, we need to send them a receipt or invoice. We can do this by sending the invoice file as an attachment.

from mailersend import emails
import base64
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY'))

# Define the sender email details
mail_from = {
    "name": "Amazing Company Orders",
    "email": "orders@amazingcompany.com",
}

# Define the recipient email details
recipients = [
    {
        "name": "Brian Griffin",
        "email": "brian@example.com",
    }
]

# Define the personalization variables
variables = [
    {
        "email": "brian@example.com",
        "data": {
            "foo": "bar",
            "name": "Brian Griffin"
        }
    }
]

# Open and read the file for attachment
with open('path-to-file', 'rb') as attachment:
    att_read = attachment.read()
    att_base64 = base64.b64encode(att_read).decode('ascii')

attachments = [
    {
        "id": "my-attached-file",
        "filename": "invoice.pdf",
        "content": att_base64,
        "disposition": "attachment"
    }
]

# Set the sender email details
mailer.set_mail_from(mail_from['email'])

# Set the recipient email details
mailer.set_mail_to(recipients)

# Set the subject of the email
mailer.set_subject("Your order is on the way.")

# Set the HTML content of the email
mailer.set_html_content("""
<html>
<body>
<h1>Your order is on the way!</h1>
<p>Hi, {{name}}!</p>
<p>We’re happy to let you know that your order is on the way and a tracking number will be sent to you shortly.</p>
<p>Please find your invoice attached.</p>
<p>Thanks and we hope you enjoyed shopping with Amazing Company!</p>
</body>
</html>
""")

# Set the plain text content of the email
mailer.set_plaintext_content("""
Your order is on the way!

Hi, {{name}}!

We’re happy to let you know that your order is on the way and a tracking number will be sent to you shortly.

Please find your invoice attached.
Thanks and we hope you enjoyed shopping with Amazing Company!
""")

# Set personalization variables
mailer.set_personalization(variables)

# Set the attachment
mailer.set_attachments(attachments)

# Send the email and print the response (status code and data)
try:
    response = mailer.send()
    print("Status Code:", response.status_code)
    print("Response JSON:", response.json())
except Exception as e:
    print(f"An error occurred: {e}")

How to send emails in bulk

Sending bulk messages isn’t just for marketing emails. You can also utilize bulk email sending for transactional emails to preserve your resources and bypass rate limiting, essentially making your email sending more efficient. 

This works best when sending emails that aren’t as time-sensitive as order confirmations or email verification. Let’s prepare some emails for our customers Brian and Stewart.

from mailersend import emails
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv("MAILERSEND_API_KEY"))

# Define the list of emails to be sent
mail_list = [
    {
        "from": {"email": "hello@amazingcompany.com", "name": "Amazing Company"},
        "to": [{"email": "brian@example.com", "name": "Brian Griffin"}],
        "subject": "What do you think of your recent order?",
        "text": "Thanks for shopping with us at Amazing Company! We’d love to hear how you’ve got on with your products. If you could spare a few minutes to leave us some feedback, it will help us to provide a better service to you in the future. Click below to submit your thoughts.",
        "html": '<p>Thanks for shopping with us at Amazing Company!</p><p>We’d love to hear how you’ve got on with your products. If you could spare a few minutes to leave us some feedback, it will help us to provide a better service to you in the future. Click below to submit your thoughts.</p><p><a href="https://amazingcompany.com/feedback">Submit feedback</a></p>',
    },
    {
        "from": {"email": "hello@amazingcompany.com", "name": "Amazing Company"},
        "to": [{"email": "stewart@example.com", "name": "Stewart Griffin"}],
        "subject": "Rate your experience with our customer service team",
        "text": "Thanks for being a loyal customer of Amazing Company! After your recent experience with our customer service team, we’d love to know how we did. Simply click below to submit your rating.",
        "html": '<p>Thanks for being a loyal customer of Amazing Company!</p><p>After your recent experience with our customer service team, we’d love to know how we did.</p><p>Simply click below to submit your rating.</p><p><a href="https://amazingcompany.com/customer-service-rating">Rate customer service</a></p>',
    },
]

# Send bulk emails and print the response
try:
    response = mailer.send_bulk(mail_list)
    print(f"Bulk email send status code: {response.status_code}")
    print(f"Response JSON: {response.json()}")
except Exception as e:
    print(f"An error occurred: {e}")

How to schedule an email

By using MailerSend’s send_at parameter and setting the date and time with the ISO 8601 format, you can easily schedule an email to go out at a specified time. This is useful for system notifications or updates, such as letting users know about scheduled maintenance or downtime. 

Here’s the script:

from mailersend import emails
from dotenv import load_dotenv
import os
from datetime import datetime, timedelta

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend email client with the API key
mailer = emails.NewEmail(os.getenv("MAILERSEND_API_KEY"))

# Define the email details
email_data = {
    "from": {"email": "hello@amazingcompany.com", "name": "Amazing Company"},
    "to": [{"email": "stewart@example.com", "name": "Stewart Griffin"}],
    "subject": "Scheduled Maintenance on Wednesday, 24th July",
    "text": "We’re writing to inform you that the website will be unavailable on Wednesday, 24th July between the hours of 11 PM and 11:30 PM due to scheduled maintenance. We’re sorry for any inconvenience caused.",
    "html": "<p>We’re writing to inform you that the website will be unavailable on <b>Wednesday, 24th July between the hours of 11 PM and 11:30 PM</b> due to scheduled maintenance.</p> <p>We’re sorry for any inconvenience caused.</p>",
    "send_at": (datetime.utcnow() + timedelta(hours=2)).isoformat()
    + "Z",  # Schedule for 2 hours from now in ISO 8601 format
}

# Send the scheduled email
try:
    response = mailer.send(email_data)
    print(f"Status Code: {response.status_code}")
    print(f"Response JSON: {response.json()}")
except Exception as e:
    print(f"An error occurred: {e}")

How to retrieve activity and analytics data

MailerSend offers advanced email analytics and activity tracking data which, in addition to accessing via the dashboard, you can also access via the API. 

Here’s a look at the code to fetch a full list of activities, which includes queued, sent and delivered emails, soft and hard bounces, opens, clicks, unsubscribes, and spam complaints. Note that you can set the date range using a Unix timestamp.

from mailersend import activity
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Initialize the MailerSend activity client with the API key
mailer = activity.NewActivity(os.getenv("MAILERSEND_API_KEY"))

# Define the parameters for retrieving domain activity
page = 1
limit = 20
date_from = 1623073576  # Example Unix timestamp
date_to = 1623074976  # Example Unix timestamp
events = [
    "queued",
    "sent",
    "delivered",
    "soft-bounced",
    "hard-bounced",
    "junk",
    "opened",
    "clicked",
    "unsubscribed",
    "spam_complaints",
]

try:
    # Retrieve domain activity
    response = mailer.get_domain_activity(
        "domain-id",  # Replace with your actual domain ID
        page,
        limit,
        date_from,
        date_to,
        events,
    )
    # Print the response
    print(f"Status Code: {response.status_code}")
    print(f"Response JSON: {response.json()}")

except Exception as e:
    print(f"An error occurred: {e}")

Best practices for sending emails in Python

Although sending emails in Python is made relatively simple with the built-in libraries and other third-party providers, there are a few things to keep in mind to make your sending more secure and efficient. Here are some of our top best practices for getting the best experience out of sending emails with Python.

1. Take appropriate security measures

Always store sensitive information such as API keys and SMTP credentials in environment variables or config files that are not publicly accessible. The code examples throughout this article take this into account with steps to set up your environment variables and how to correctly use them in your scripts.

You should also consider how you store such credentials for your own access—i.e. no post-it notes! In all seriousness, we recommend using a popular secrets management platform like AWS Secrets Manager or Azure Key Vault.

2. Always use authentication and encryption

Use of SMTP requires TLS/SSL to encrypt the connection and protect email content during transit. What’s more, you should use proper authentication protocols—SPF, DKIM and DMARC—to secure access to your email services and prevent abuse.

3. Use proper error handling

Implement error handling via try/except blocks to catch and manage exceptions. This will help you to manage authentication errors, invalid email addresses, and network issues more efficiently and improve the experience of the user. 

You should also make use of sufficient error logging for the purposes of debugging and monitoring significant events. Having logs to look back on will help you with diagnosing issues and tracking activity. 

4. Use ESPs like MailerSend

Email providers give you access to loads of features and functionality that make sending emails easier, more productive, and a lot more interesting. You’ll be able to make use of features like email templates, advanced analytics and tracking, inbound routing, bulk sending, and even SMS. All of which allow you to consolidate the majority of your communications onto a single platform and send emails more efficiently. 

5. Manage email sending with rate-limiting

Rate-limiting your email sending is important to avoid exceeding service provider limits and keep your services running as usual. It can also help to prevent abuse and maintain good deliverability. 

The best way to do this with Python—especially for more advanced implementations and high email sending volumes—is by using a task queue. There are various job queue systems available to help you manage this, including Celery and RQ. 

And if you’re using a third-party provider, you can make use of their API’s built-in features, such as batching emails with the bulk email endpoint in MailerSend. 

6. Validate email addresses

Verifying lists of email addresses or validating them in real-time as they get added to your system, is a great way to maintain healthy email sending practices that will enhance your deliverability. 

There are a few different ways to do this, but by far the most reliable and efficient is by using an email verification service. MailerSend has a built-in email verification tool that you can use to validate lists of email addresses regularly and in real-time with the API.  

7. Use a staging environment for testing

Testing any implementation is vital before you go to production, so you’ll want to use a test email server to try out your email workflows. It’ll prevent you from flooding any inboxes with test emails and will keep your domain safe from being blocklisted. 

As well as testing your workflows, testing in this way will allow you to validate the HTML/CSS in your emails and check for spam issues.

Flexible and customizable email sending at your fingertips

Python is a great language for sending emails thanks to its automation capabilities, built-in libraries, and superior customization options. 

Paired with a reliable ESP and you’ve got a simple, powerful solution for integrating email-sending into your Python apps. 

Remember, you can sign up to MailerSend for free and test the platform out with a trial domain to see how everything works. Give it a go!

Try MailerSend now

Sign up for free and test out MailerSend with a free trial domain—no credit card needed. You can upgrade at any time to a free plan and get up to 3,000 emails per month.

Tautvydas Tijūnaitis
I'm Tautvydas, Lead Developer at MailerSend. When I'm not busy coding, I live and breathe European football—whether watching games in stadiums or building my fantasy team online!