Send email from Flask with Flask-Mail over SMTP

Flask-Mail configuration for SMTP: MAIL_SERVER, MAIL_PORT, MAIL_USE_TLS/SSL, credentials from the environment, a Message with HTML and attachments, and sending outside the request with a thread or task queue.

Install

shell
pip install Flask-Mail

Complete example

Flask — STARTTLS on 587, credentials from the environment
import os
from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
app.config.update(
    MAIL_SERVER=os.environ.get("SMTP_HOST", "smtp.queensmtp.com"),
    MAIL_PORT=int(os.environ.get("SMTP_PORT", "587")),
    MAIL_USE_TLS=True,            # STARTTLS on 587; set MAIL_USE_SSL=True + port 465 instead for implicit TLS
    MAIL_USERNAME=os.environ["SMTP_USER"],
    MAIL_PASSWORD=os.environ["SMTP_PASS"],
    MAIL_DEFAULT_SENDER=("Your App", "hello@yourdomain.com"),
)
mail = Mail(app)

@app.route("/signup", methods=["POST"])
def signup():
    msg = Message("Welcome", recipients=["customer@example.com"])
    msg.body = "Thanks for signing up."
    msg.html = "<p>Thanks for <b>signing up</b>.</p>"
    # with app.open_resource("terms.pdf") as f: msg.attach("terms.pdf", "application/pdf", f.read())
    mail.send(msg)
    return "ok"

What to know

  • Flask-Mail wraps smtplib; the same STARTTLS/SSL rules apply (TLS+587 or SSL+465, never both).
  • For many messages use mail.connect() as a context manager and send in a loop over one connection.
  • Move sending to a background thread or a task queue (RQ/Celery) so the HTTP response is not waiting on SMTP.

Same code, different provider

Only the host, port and credentials change. The rules per provider are on the SMTP settings hub.

ProviderHostPortUsernamePasswordNote
QueenSMTP smtp.queensmtp.com587 (STARTTLS) / 465 (SSL) / 2525SMTP username from the dashboardSMTP password$5/year + $0.10 per 1,000; 100 free/day
Gmail smtp.gmail.com587 / 465you@gmail.comApp Password500/day, From must be the account
Amazon SES email-smtp.<region>.amazonaws.com587 / 465 / 2587SES SMTP usernameSES SMTP passwordsandbox 200/day until approved
Office 365 smtp.office365.com587user@yourdomain.commailbox passwordSMTP AUTH must be enabled; 30/min
SendGrid smtp.sendgrid.net587 / 465 / 2525apikeyAPI keyno free plan since 2025

Credentials for the example above

Create a free account, verify your sending domain (SPF + DKIM shown in the dashboard), add an SMTP credential, and paste it into the code. 100 emails a day free; $5/year plus $0.10 per 1,000 after that. Prefer HTTPS? The same account has a REST email API.

Get SMTP credentials Test your settings first

Other languages and frameworks

Examples tested 2026-08-30. If a library changed its API, tell us.

Flask SMTP — questions

Wrong credentials for the host (Gmail needs an App Password; relays need the SMTP credential, not your dashboard login).