Back to main menu

IT & Engineering

Send email using Python3 and the Mailgun API

Looking to streamline your email sending process using Python3? We’ve assembled our Python pros to bring you a guide. In this tutorial we’ll dissect how to send emails programmatically using Python3 and Sinch Mailgun’s email API.



If you’re looking to streamline your approach to programmatically sending email, you’re in good hands. Our developers have the experience you need to get started. Read on as we delve into the details of integrating Python3 with Mailgun’s robust email API, with insights, guidance, and optimizations along the way.

Getting started: What you’ll need for this tutorial

When sending email, you have two choices, manual, or programmatic. When we’re talking about transactional emails specifically, programmatic is the way to go. Instead of sending important email updates or notifications manually, you can use automated email scripts to save time and reduce errors.

Scripts — small programs that automate tasks — offer a more efficient way to send bulk emails, schedule messages, or get called directly from your application. Some use cases for sending emails through automated scripts include the following:

  • Sending welcome emails for user registration

  • Sending notifications and alerts from applications, such as informing users about their service usage or notifying admins when server resources are running low

  • Sending reports, such as a weekly new customer report

  • Sending marketing campaigns – for instance, to promote new products or services

In this tutorial, you'll learn how to send emails using Python3 scripts and the Mailgun API.


To follow along with this tutorial, you'll need the following:

  • Python 3.9 or higher. Python is often described as the Swiss Army knife of programming languages, and it offers a wide range of capabilities. In addition to its many other uses, Python is great for writing various scripts.

  • A Mailgun account. Mailgun is an email-sending platform that lets you send and track emails. If you aren't already a Mailgun user, you can follow along with this tutorial using our free trial.

Setting up a new Python project

Let’s dive in.

Start by creating a suitable directory for your Python project and add an empty .env file inside it. Before proceeding, consider creating and activating a new virtual environment before installing the Python libraries required for this tutorial. It isolates your project, which prevents any dependency conflicts.

To install the necessary libraries (requests and python-dotenv) using pip in your virtual environment, run the following command:

pip install requests python-dotenv

The requests library allows you to make API calls, while the python-dotenv library allows you to store the API key outside your code.

Obtaining an API key

Next, you'll obtain an API key from your Mailgun account, which you'll use to make API calls to Mailgun.

Sign in to your Mailgun account and create an API key from the API Security page, which is accessible via a drop-down below your name in the top right corner.

New API key location in Mailgun

Copy and paste this API key into your project's .env file.


While you're in Mailgun, note down your sending domains from the Mailgun dashboard.

If you're on a free plan, you will see a sandbox domain, which will be used in the API endpoint as well as for the from email address when sending emails through the Mailgun API. The sandbox domain follows the format <your-sandbox-id>, as shown below.

Sandbox domain in Mailgun

You will need to upgrade to a paid plan if you need to use a custom email domain for sending emails through Mailgun. The free plan only allows the sandbox domain, and you can send emails to a maximum of five verified addresses. However, this free plan is useful during the development phase when integrating the Mailgun API into your system.

Imports and initialization

Next, create the file in your project directory and copy and paste this code:

It uses Python's standard logging and configures it to INFO level with this code:

The load_dotenv() function comes from the python-dotenv library and is used to load environment variables from the .env file in your Python code. It is a good practice to store and read sensitive information such as credentials and API keys from the .env file.

MAILGUN_API_URL sets the API URL for your Python script, and indicates the sender's email ID. Both must use your email sending domain or the sandbox domain mentioned earlier.

Sending single emails using the Mailgun API

Let's first look at the most simple way of using Mailgun API: sending a single email. You can use it for one-off cases, like sending a reminder or a follow-up email to your high-value customer. This will allow you to track the email delivery as well.

Copy and paste the code below into your file after the imports and initialization:

This send_single_email(...) function takes three arguments: to_address, subject, and message. to_address is for a single email address, and the other two are for the email's subject and content.

The code reads the Mailgun API key from the .env file and uses it to make an API call to the specified API_URL. This API key acts as a unique identifier, allowing Mailgun to authenticate the API call and verify who is using its services.

You must use your unique API key to ensure proper authentication. The from email address used in this API call must also be associated with your valid domain or Mailgun's sandbox domain. If it doesn't, the call will fail with an error message.

The data parameters are sent via HTTP POST method to the API endpoint with the call.

When the API call is successful, your email will be queued for delivery, and the API will return an HTTP-200 (OK) status. In case of an error, the API will return an error message with an appropriate HTTP status code. Both cases are appropriately logged by this code snippet.

if __name__ == "__main__" shows how this function can be called in your script.

Running this script in your terminal will show the following:

Python script running in terminal

Sending bulk email with the Mailgun API

While you can use the send_single_email(...) function in a loop to send emails to multiple recipients, it's not the most efficient method due to network I/O delays. This approach may also encounter API rate limiting.

Instead, use Batch Sending for sending emails to multiple recipients. The code snippet below demonstrates how to use it in your Python script.

Copy and paste the send_batch_emails(...) function into your file after the send_single_email(...) function, and modify the __main__ part as shown below:

Batch Sending uses a special parameter called Recipient Variables that lets you send personalized emails to multiple recipients in a single API call. Using Recipient Variables with Batch Sending ensures Mailgun sends individual emails to each recipient in the to field. Without it, each recipient will see all recipients' email addresses in the to field.

Here is one such recipient variable, with email addresses as keys and the corresponding "name" and "id" as values:

{"": {"name": "Manish", "id": 1},

"": {"name": "Jakkie", "id": 2},

"": {"name": "Elzet", "id": 3}}

The send_batch_emails(...) function has three parameters: recipients, subject, and message. Note that recipients is a dictionary object representing the recipient variable discussed above.

The code first extracts email addresses from this dictionary using to_address = list(recipients.keys()), then converts the dictionary to JSON for API use with the recipients_json = json.dumps(recipients) call.

The recipients_json variable is passed in the API call for the recipient-variables field, which is used for personalizing the email subject and content. The subject is specified as follows:


The Mailgun API correctly substitutes each recipient's name from the JSON while sending emails with So, in the example above, Manish, Jakkie, and Elzet will receive personalized subject lines—"Hi, Manish!" "Hi, Jakkie!", and "Hi, Elzet!". Similarly, you can personalize the email content using any %recipient.KEY-NAME% value.

The rest of the send_batch_emails(...) code is similar to the send_single_email(...) function, except that it sends multiple email recipients in a single API call.

Running this script will show the following:

Python script running in terminal

Keep in mind: The maximum number of recipients allowed in a single Mailgun Batch Sending API call is one thousand. You can find more about the Batch Sending API here.

Here's an example of how this email will appear in the recipient's inbox:

Email sent using Mailgun API

Additional features

The Mailgun API offers several additional features to make your life easier.

Sending HTML emails with attachments

The Mailgun API lets you send emails with attachments, whether with text or HTML content. You can use the same API endpoint ( with an additional 'attachment' passed to the files parameter, as shown below:

Email delivery and tracking

Mailgun provides detailed email tracking, including when emails are delivered or opened, links are clicked, emails bounce, users unsubscribe, and emails are marked as spam. This data is made available via the control panel on the dashboard and through the API.

Mailgun also permanently saves emails if they can't be delivered (hard bounce) or if a recipient unsubscribes or marks the email as spam. In these cases, Mailgun won't try to send emails to those recipients again.

Email templates

The Mailgun API allows you to create HTML templates for standardizing your email layout and making them more appealing with predesigned layouts and standard content.

You can find templates in the left sidebar under the Sending menu:

Best practices

It's also important to keep some best practices in mind when you send emails programmatically.

Error handling

When your script uses a third-party API like Mailgun to send emails, you must handle potential errors related to network or API failures and API responses indicating errors to ensure your script works smoothly, can detect issues like invalid API keys or URLs, and knows when emails aren't sent by the Mailgun API. Without error handling, your script might fail silently, resulting in undelivered emails.

The code snippets above all provide for handling errors that might occur. Firstly, they check the response status code (resp.status_code). If it's not successful (HTTP 200), they log the error message so you can debug the issue.

Both the send_single_email(...) and send_batch_emails(...) functions also use a try-except block to ensure any exceptions are caught and logged correctly.

Email deliverability

Email deliverability means ensuring your emails land in the recipient's inbox, not their spam folder. You need a good sender reputation to achieve high deliverability. When we’re talking about building programmatic sending, there are a couple specific things we can look at to improve your email deliverability including authentication, and optimizing emails to be responsive. We’re talking about each below.

Authenticate your email

Use SPF, DKIM, and DMARC to validate your identity as a sender. Only send emails to verified subscribers who have opted in to receive them and regularly remove inactive subscribers and those who mark your emails as spam.

DMARC is becoming an industry requirements. Learn more about why, and what this authentication standard does in our post on the DMARC perspective.

Responsive emails

Most users access content on various devices like mobile phones, iPads, and tablets, and they expect your emails to be visually appealing and easy to read across different screens.

This requires that you use responsive design techniques, such as fluid layouts, CSS media queries, and optimized images. You should also test your emails on various devices, email clients, and screen sizes to verify that they display correctly.

Responsive design is particularly important if your emails contain a lot of HTML content with images or if you're using email templates. Mailgun's predefined templates are responsive by default.

Wrapping up

In this article, you learned how to send emails using a Python script and the Mailgun API. You also learned about Mailgun features like email tracking and templates and the importance of error handling, optimizing deliverability, and sending responsive emails.

Email geeks help other email geeks. You can find the code discussed in this tutorial in this GitHub repo.

Was this helpful? If so, be sure to subscribe to our newsletter for more tutorials, announcements, and industry insights.

Keep me posted! Get the latest from Mailgun delivered to your inbox.

Send me the newsletter. I expressly agree to receive the newsletter and know that I can easily unsubscribe at any time.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Related readings

8 types of transactional emails your platform needs

Transactional emails sometimes get a reputation for being boring when compared to carefully crafted marketing emails. And while that may be true to an extent, transactional...

Read more

Snack delivery machine: Part 2, the electronics and tech

Before we get technical, if you haven’t read our previous post about our wonderous snack machine, pop over and check that out first...

Read more

Python tutorial: How ImgPage lets you upload 25 MB photos to S3 and Cloud files with just an email

This post is written by Paul Finn. Paul is a Portsmouth, NH-based Python developer, business owner ( and...

Read more

Popular posts

Email inbox.

Build Laravel 10 email authentication with Mailgun and Digital Ocean

When it was first released, Laravel version 5.7 added a new capability to verify user’s emails. If you’ve ever run php artisan make:auth within a Laravel app you’ll know the...

Read more

Mailgun statistics.

Sending email using the Mailgun PHP API

It’s been a while since the Mailgun PHP SDK came around, and we’ve seen lots of changes: new functionalities, new integrations built on top, new API endpoints…yet the core of PHP...

Read more

Statistics on deliverability.

Here’s everything you need to know about DNS blocklists

The word “blocklist” can almost seem like something out of a movie – a little dramatic, silly, and a little unreal. Unfortunately, in the real world, blocklists are definitely something you...

Read more

See what you can accomplish with the world's best email delivery platform. It's easy to get started.Let's get sending
CTA icon