Site icon Mailtrap

How to Send Emails in Flask using SMTP or API

In this Flask send email tutorial, I’ll show you how to use the Flask-Mail extension and send emails via SMTP or API.

I’ll also demonstrate how you can test your email messages before sending them to make sure they arrive in your recipient’s inboxes pitch-perfect.

So, let’s cut to the chase.

Setting up Flask-Mail configuration

First things first, let’s set up Flask-Mail, a Flask extension for sending emails.

Have everything configured already? To skip ahead to the SMTP setup [click here], or for the API setup, [click here].

Install Flask and Flask-Mail

Let’s start by installing the latest version of Flask from the Python Package Index (PyPI) by running the following command:

pip install Flask

Then, let’s install Flask-Mail with:

pip install Flask-Mail

Or, you can also run this command to download the latest version directly from the source code:

git clone https://github.com/mattupstate/flask-mail.git
cd flask-mail
python setup.py install

Tip:

Add and configure virtualenv or venv (optional)

This is an optional step, but, if you want to isolate this environment, you can either use: 

To utilize Python’s built-in venv module, launch the terminal and navigate to the folder you’ll use for this project and, based on the OS you’re using, type in one of these two commands:

python -m venv new_environment (macOS or Linux)
py -m venv new_environment (Windows)

You should then replace new_environment with whatever you want to name the environment and activate it, like so:

source new_environment/bin/activate (macOS or Linux)
.\new_environment\Scripts\activate (Windows)

Ensure that you have the command prompt or terminal running as an administrator on the systems (particularly Windows) to avoid possible permission issues.

However, if you want to use virtualenv, you should install `pip` and ensure it is up to date beforehand. You can install it by running the following command:

pip install virtualenv

Notes:

Send emails in Flask using SMTP

Now that we have our setup ready, let’s start sending emails via SMTP!

1. Import the necessary classes

In Flask-Mail, email sending is handled by an instance of Mail class and Message class, which you need to import into your .py file with the following code:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)

mail = Mail(app)

2. Insert SMTP credentials

Next, you need to insert SMTP credentials by your mail provider into your Flask application. In this article, I’ll use the Mailtrap Email Delivery platform, as it provides me with a reliable SMTP service that offers robust sending capabilities.

To do this, create a Mailtrap account, log in, and navigate to Sending Domains where you can add and verify your domain in a matter of seconds.

Once your domain is verified, Mailtrap will take you to the page with SMTP credentials.

I’ll use the Transactional Stream now and show you how to put the Bulk Stream to use later on in the article.

Here’s what your code should look like with SMTP credentials inserted:

app.config['MAIL_SERVER']= 'live.smtp.mailtrap.io'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USERNAME'] = 'your_email@address.com'
app.config['MAIL_PASSWORD'] = 'your_password'
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False

3. Set up a Message object

Lastly, we need to set up a Message object, mapped by the URL rule (‘/’), and insert the base details of our message:

@app.route("/")
def index():
    msg = Message(subject='Hello from the other side!', sender='peter@mailtrap.io', recipients=['paul@mailtrap.io'])
    msg.body = "Hey Paul, sending you this email from my Flask app, lmk if it works"
    mail.send(msg)
    return "Message sent!"

And here’s what the code should look like in the end:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)

app.config['MAIL_SERVER']= 'live.smtp.mailtrap.io'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USERNAME'] = 'your_email@gmail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
mail = Mail(app)


@app.route("/")
def index():
    msg = Message(subject='Hello from the other side!', sender='peter@mailtrap.io', recipients=['paul@mailtrap.io'])
    msg.body = "Hey Paul, sending you this email from my Flask app, lmk if it works."
    mail.send(msg)
    return "Message sent!"


if __name__ == '__main__':
    app.run(debug=True)

Tips:

msg = Message('Hello from the other side!', sender=('Peter from Mailtrap', 'peter@mailtrap.io'), recipients=['paul@mailtrap.io'])
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')

Send HTML email

Sending HTML email in Flask is easy because you can use the html attribute of the Message object:

msg.body = "Hey Paul, sending you this email from my Flask app, lmk if it works"
msg.html = "<b>Hey Paul</b>, sending you this email from my <a href='https://google.com'>Flask app</a>, lmk if it works"

Send email to multiple recipients

Sending emails to multiple recipients in Flask is a breeze because you can add someone in cc and/or bcc, set a reply-to address, add extra headers, and so on.

Check out some of the parameters you can use:

flask_mail.Message(subject='', recipients=None, body=None, sender=None, cc=None, bcc=None, reply_to=None, date=None, charset=None, extra_headers=None, mail_options=None, rcpt_options=None)

For explanatory purposes, here’s an example of a code snippet for sending email to multiple recipients in Flask:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)

app.config['MAIL_SERVER']= 'live.smtp.mailtrap.io'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USERNAME'] = 'your_email@gmail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
mail = Mail(app)

@app.route("/send-email")
def send_email_to_multiple_recipients():
    # List of recipients
    recipients = ["user1@example.com", "user2@example.com", "user3@example.com"]

    # Creating the message
    msg = Message("Email to Multiple Recipients",
                  sender="your_email@example.com",
                  recipients=recipients)
    msg.body = "Hey Paul, sending you this email from my Flask app, lmk if it works."
    
    # Sending the email
    mail.send(msg)
    
    return "Email sent to multiple recipients!"

if __name__ == "__main__":
    app.run(debug=True)

Send email with attachments

To add an attachment to your message, simply load it with open_resource() method and then use a Python with statement to add the file to your email, like so:

with app.open_resource("invoice.pdf") as fp:  
    msg.attach("invoice.pdf", "application/pdf", fp.read()) 

Don’t forget to pick a proper MIME type for each file and upload each file to the same directory as your script.

However, if the file you want to attach isn’t in a resource directory managed by Flask (e.g., static folder), you can use Python’s built-in open() function to read the file.

Here’s an example:

with open("invoice.pdf", "rb") as fp:
    msg.attach("invoice.pdf", "application/pdf", fp.read())

Send email with an embedded image

To embed images in your emails, you can use the content_id parameter of the attach method and reference the content ID in the HTML body of your message with the cid:content_id syntax.

For instance:

@app.route('/send-email-with-image')
def send_email_with_image():
    msg = Message("Email with Embedded Image",
                  sender="your_email@example.com",
                  recipients=["recipient@example.com"])
    
    # The HTML body with the embedded image
    msg.html = """
    <html>
        <body>
            <p>Here is an embedded image:</p>
            <img src="cid:image1">
        </body>
    </html>
    """
    
    # Attach the image
    with app.open_resource("image.jpg") as fp:
        msg.attach("image.jpg", "image/jpeg", fp.read(), headers=[('Content-ID', '<image1>')])
    
    mail.send(msg)
    
    return "Email sent with an embedded image!"

Asynchronous email sending

For asynchronous email sending in Flask, you can either use:

Python’s Thread is a powerful method for multi-tasking that’s widely used for tasks such as downloading files, handling user input, and other I/O operations. However, it’s also very viable for asynchronous email sending.

Here’s a code snippet example:

```python
import threading

def send_async_email(app, msg):
with app.app_context():
mail.send(msg)

def send_email(subject, sender, recipients, body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = body
thr = threading.Thread(target=send_async_email, args=[app, msg])
thr.start()

```

Simply put, Celery is a tool for distributing work across threads and machines. 🥬

When an email is sent, a Flask app calls a Celery task that takes the ESP connection process on its shoulders so your app can quickly respond and display a “thank you” message to a user almost instantly.

To install Celery, run the following command:

pip install celery

Note that Celery is no longer supported on Windows, so we’ll have to configure the following variable, as suggested in the repository thread:

import os
os.environ.setdefault('FORKED_BY_MULTIPROCESSING', '1')

As Celery requires something to store your queue of tasks, we can use Redis. If you’re not sure whether you have it installed, you can use it through Docker with the following command:

docker run -d -p 6379:6379 redis

Now we need to install Python’s dependency to use Redis:

pip install redis

Then, we need to configure a Celery variable, which will take care of connecting to Redis.

Keep in mind that it needs to be available on the file where you will define your tasks to run in the background later. For example, you can keep it on the same file we’re already using for Flask routes.

celery = Celery(app.name, broker='redis://localhost:6379/0')

We must also add one more variable for cryptographic components (e.g., signing cookies) to complete our configuration:

app.config['SECRET_KEY'] = 'not_a_secret'

Additionally, you can pick a custom recipient from a submitted form:

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html', email=session.get('email', ''))
    email = request.form['email']

    email_data = {
        'subject': 'Hello from the other side!',
        'to': email,
        'body': 'Hey Paul, sending you this email from my Flask app, lmk if it works'
    }

    send_async_email.delay(email_data)
    flash(f'Sending email to {email}')

    return redirect(url_for('index'))

We send this email with the send_async_email method, invoked by delay(). As the last step, specify the background task Celery is expected to perform:

@celery.task
def send_async_email(email_data):
    msg = Message(email_data['subject'],
                  sender=app.config['MAIL_DEFAULT_SENDER'],
                  recipients=[email_data['to']])
    msg.body = email_data['body']
    with app.app_context():
        mail.send(msg)

If you make an immediate request, a task will be put on Celery’s queue to send an email. But, as there isn’t anything processing this queue yet, you need to run a separate Celery process, which will pick tasks from the queue and process them. 

To do this, run the following command on a separate terminal:

celery -A main.celery worker --loglevel=INFO

Note that main is the name of your Python file, and celery is the name of your Celery variable.

Bulk email sending

To send bulk email in Flask, we’ll use Python’s with statement, which keeps the connections to our email alive until all messages have been sent, at which point it will close automatically.

For this, you can use the following code:

users = [{"email": "paul@mailtrap.io"}, {"email": "peter@mailtrap.io"}]
with mail.connect() as conn:
    for user in users:
        sender = "test@mailtrap.io"
        message = "..."
        subject = "Hello from the other side!"
        msg = Message(recipients=[user["email"]],
                      body=message,
                      subject=subject,
                      sender=sender)

        conn.send(msg)

You should also utilize Mailtrap’s Bulk Stream I mentioned previously in the article, which is optimized for higher sending volumes.

Simply log into your Mailtrap account, navigate to the SMTP/API Settings tab, and copy the Bulk Stream credentials located on your right.

Here’s what the code should look like when put together:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)

# Example configuration - replace with your Mailtrap Bulk Stream credentials
app.config['MAIL_SERVER'] = 'bulk.smtp.mailtrap.io'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = 'your_mailtrap_username'
app.config['MAIL_PASSWORD'] = 'your_mailtrap_password'
app.config['MAIL_DEFAULT_SENDER'] = 'test@mailtrap.io'

mail = Mail(app)

# Dummy list of users
users = [{"email": "paul@mailtrap.io"}, {"email": "peter@mailtrap.io"}]

# Function to send bulk emails
def send_bulk_emails():
    with app.app_context():  # Ensure you're within an application context
        with mail.connect() as conn:
            for user in users:
                message = "..."
                subject = "Hello from the other side!"
                msg = Message(recipients=[user["email"]],
                              body=message,
                              subject=subject)
                conn.send(msg)

if __name__ == '__main__':
    send_bulk_emails()

And voila, you have a setup for bulk email sending!

Tip: If you want to specify the maximum number of emails to be sent, use MAIL_MAX_EMAILS from the configuration as no limit is set by default.

Send emails in Flask using API

If you’d rather automate your email-sending process, you can use the official Python client provided by Mailtrap Email Sending.

To integrate it, log into your Mailtrap account and navigate to SettingsAPI tokens. There, you’ll find your credentials, which you should copy across your verified sending domain.

Then, go back to your Flask app or project and install package version 2.0.0 or higher:

pip install mailtrap

Once you have the package installed, you can send a simple email with the following code snippet:

import mailtrap as mt

# create mail object
mail = mt.Mail(
    sender=mt.Address(email="mailtrap@example.com", name="Mailtrap Test"),
    to=[mt.Address(email="your@email.com")],
    subject="You are awesome!",
    text="Congrats for sending test email with Mailtrap!",
)

# create client and send
client = mt.MailtrapClient(token="your-api-key")
client.send(mail)

And don’t forget to replace your-api-key with the actual API key provided by Mailtrap and modify variables such as sender (email and name), to (recipient), subject, and text.

Send HTML email

To send HTML email, we’ll use the html parameter within the mt.Mail object:

html="""
<!doctype html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body style="font-family: sans-serif;">
    <div style="display: block; margin: auto; max-width: 600px;" class="main">
      <h1 style="font-size: 18px; font-weight: bold; margin-top: 20px">
        Congrats for sending test email with Mailtrap!
      </h1>
      <p>Inspect it using the tabs you see above and learn how this email can be improved.</p>
      <img alt="Inspect with Tabs" src="cid:welcome.png" style="width: 100%;">
      <p>Now send your email using our fake SMTP server and integration of your choice!</p>
      <p>Good luck! Hope it works.</p>
    </div>
    <!-- Example of invalid for email html/css, will be detected by Mailtrap: -->
    <style>
      .main { background-color: white; }
      a:hover { border-left-width: 1em; min-height: 2em; }
    </style>
  </body>
</html>
""",

For compatibility and accessibility reasons, I suggest you provide your recipients with both HTML and plain text versions of your email when possible. This way, you make sure that recipients who use clients that do not render HTML content or who prefer plain text emails can still read your mail.

To provide the plain text version of the email along with HTML, simply add the text parameter:

text="Congrats for sending test email with Mailtrap!",

Send email to multiple recipients

For sending email to multiple recipients, you can use the following parameters:

to=[mt.Address(email="your@email.com", name="Your name")],
cc=[mt.Address(email="cc@email.com", name="Copy to")],
bcc=[mt.Address(email="bcc@email.com", name="Hidden Recipient")],

Parameters explanation:

Send email with attachments

To send email with attachments, we’ll use the attachments parameter within the mt.Mail object, like so:

attachments=[
    mt.Attachment(
        content=base64.b64encode(welcome_image),
        filename="welcome.png",
        disposition=mt.Disposition.INLINE,
        mimetype="image/png",
        content_id="welcome.png",
    )
],

Code breakdown:

Send email with an embedded image

Sending emails with embedded images is similar to adding attachments because we’ll use the same attachments parameter:

attachments=[
    mt.Attachment(
        content=base64.b64encode(welcome_image),
        filename="welcome.png",
        disposition=mt.Disposition.INLINE,
        mimetype="image/png",
        content_id="welcome.png",
    )
],

But, we’ll have to reference the embedded image within the HTML body of the email with an <img> tag, where the src attribute is set to cid:welcome.png, which tells the email client to display the image inline.

Here’s the line of code you can use:

<img alt="Inspect with Tabs" src="cid:welcome.png" style="width: 100%;">

Bulk email sending

For bulk email sending in Flask, we’ll use Mailtrap’s Bulk Stream again, which you can find under Sending Domains → SMTP/API Settings. 

Be sure to select API and use the Python code sample located under API credentials. Pasted into your Flask configuration, it should look something like this:

import requests

url = "https://bulk.api.mailtrap.io/api/send"

payload = "{\"from\":{\"email\":\"mailtrap@mailtrapdemo.com\",\"name\":\"Mailtrap Test\"},\"to\":[{\"email\":\"demo@mailtrap.io\"}],\"subject\":\"You are awesome!\",\"text\":\"Congrats for sending test email with Mailtrap!\",\"category\":\"Integration Test\"}"
headers = {
# 
"Authorization": "Bearer ba12312312312312312312312312312c",
  "Content-Type": "application/json"
}


try:
    response = requests.request("POST", url, headers=headers, data=payload)
except requests.exceptions.HTTPError as errh:
    print(f”HTTP Error: {errh})

print(response.text)

Additionally, you can define the payload as a Python dictionary and let the requests library convert it to JSON, and add some basic error handling, like so:

import requests

url = "https://bulk.api.mailtrap.io/api/send"
payload = {
    "from": {"email": "mailtrap@mailtrapdemo.com", "name": "Mailtrap Test"},
    "to": [{"email": "demo@mailtrap.io"}],
    "subject": "You are awesome!",
    "text": "Congrats for sending test email with Mailtrap!",
    "category": "Integration Test"
}
headers = {
    "Authorization": "Bearer your_real_token_here",  # Replace with your actual Mailtrap API token
    "Content-Type": "application/json"
}

response = requests.post(url, headers=headers, json=payload)

if response.status_code == 200:
    print("Email sent successfully!")
else:
    print(f"Failed to send email. Status code: {response.status_code}, Response: {response.text}")

Asynchronous email sending

Similarly to sending asynchronous emails via SMTP, we’ll use Celery again to do it through an API.

Simply modify the send_async_email function to make an HTTP request to the API endpoint instead of using Flask-Mail. Here’s an example:

from flask import Flask
import requests
from celery import Celery

app = Flask(__name__)
# Configure your Flask app here, including MAIL_DEFAULT_SENDER and MAIL_API_KEY

celery = Celery(app.name, broker='your_broker_url')
celery.conf.update(app.config)

@celery.task
def send_async_email(email_data):
    with app.app_context():
        url = "https://bulk.api.mailtrap.io/api/send"
        payload = {
            "from": {"email": app.config['MAIL_DEFAULT_SENDER'], "name": "Your App Name"},
            "to": [{"email": email_data['to']}],
            "subject": email_data['subject'],
            "text": email_data['body'],
            "category": "Your Category"
        }
        headers = {
            "Authorization": f"Bearer {app.config['MAIL_API_KEY']}",
            "Content-Type": "application/json"
        }
        response = requests.post(url, headers=headers, json=payload)
        
        if response.status_code != 200:
            # Log error or handle it accordingly
            print(f"Failed to send email: {response.text}")

# Remember to update your Celery worker command according to your project structure

However, regardless of whether you’re sending emails via SMTP or API, you need to run a Celery worker to process the tasks in the queue with the following command:

celery -A main.celery worker --loglevel=INFO

Important:

Test emails and email sending in production

Before you deploy any email functionality, testing whether your emails are actually sent without spamming users is pretty much a must.

To do this, you can block sending in Flask with two simple settings:

Then, you can use the record_messages method to see what’s being sent from your Flask app (make sure you have the blinker package installed). You’ll see the list of Message instances under outbox.

with mail.record_messages() as outbox:

    mail.send_message(subject='testing',
                      body='test',
                      recipients=emails)

    assert len(outbox) == 1
    assert outbox[0].subject == "testing"

However, if you wish to see emails that your app sends, you’ll need a different solution.

I personally recommend Mailtrap Email Testing, a part of Mailtrap Email Delivery Platform, which provides you with a safe environment where you can preview and test your emails before sending them out.

With Mailtrap Email Testing you can preview how your HTML emails look before you send them out to your recipients, see them in their source HTML, text, raw, and more.

You can also use the HTML Check feature to analyze the HTML/CSS of your emails and easily spot any faulty lines of code. This way, you can fix potential errors in code and make sure your emails reach your recipients’ inboxes looking flawless.

Additionally, with the Spam Analysis feature, you can check your spam score, which, if you keep under 5, you effectively solve a significant number of potential email deliverability issues when your app moves to production.

On top of these and other advanced features, Mailtrap Email Testing is super easy to set up. Check it out!

SMTP

To start testing your emails, all you have to do is:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)

# Mailtrap configuration
app.config['MAIL_SERVER'] = 'sandbox.smtp.mailtrap.io'
app.config['MAIL_PORT'] = 2525
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = 'your_mailtrap_username'
app.config['MAIL_PASSWORD'] = 'your_mailtrap_password'
app.config['MAIL_DEFAULT_SENDER'] = 'test@mailtrap.io'

# Initialize Flask-Mail
mail = Mail(app)

@app.route("/send_email")
def send_email():
    msg = Message('Hello', sender='you@example.com', recipients=['test@example.com'])
    msg.body = "This is a test email from Flask using Mailtrap."
    mail.send(msg)
    return "Email sent!"

if __name__ == "__main__":
    app.run(debug=True)

API

If you’d rather integrate Mailtrap API for testing, automation, and testing automated sequences, simply run the following command:

pip install mailtrap

And then use the following code snippet:

import mailtrap as mt

# create mail object
mail = mt.Mail(
    sender=mt.Address(email="mailtrap@example.com", name="Mailtrap Test"),
    to=[mt.Address(email="your@email.com")],
    subject="You are awesome!",
    text="Congrats for sending test email with Mailtrap!",
)

# create client and send
client = mt.MailtrapClient(token="your-api-key")
client.send(mail)

Of course, you should modify the variables such as sender, subject, text, and most importantly, your-api-key.

For more information, be sure to check out the official Mailtrap API documentation or the dedicated Python repository on GitHub.

Wrapping up

And with that, we’ve arrived at the end of our Flask tutorial!

I’ve shown you how to leverage the Flask-Mail extension and Mailtrap so you can seamlessly add email-sending functionality to your web application, and don’t forget to test your messages! 

However, if you wish to explore other methods for sending emails in Flask, like Python’s smtplib, be sure to check out our dedicated Python tutorial. Or, check out our other Python-related articles, such as:

Exit mobile version