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.
| Provider | Host | Port | Username | Password | Note |
|---|---|---|---|---|---|
| QueenSMTP | smtp.queensmtp.com | 587 (STARTTLS) / 465 (SSL) / 2525 | SMTP username from the dashboard | SMTP password | $5/year + $0.10 per 1,000; 100 free/day |
| Gmail | smtp.gmail.com | 587 / 465 | you@gmail.com | App Password | 500/day, From must be the account |
| Amazon SES | email-smtp.<region>.amazonaws.com | 587 / 465 / 2587 | SES SMTP username | SES SMTP password | sandbox 200/day until approved |
| Office 365 | smtp.office365.com | 587 | user@yourdomain.com | mailbox password | SMTP AUTH must be enabled; 30/min |
| SendGrid | smtp.sendgrid.net | 587 / 465 / 2525 | apikey | API key | no 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 firstOther languages and frameworks
PHPsend email over SMTP
Laravelsend email over SMTP
WordPresssend email over SMTP
Pythonsend email over SMTP
Djangosend email over SMTP
Node.jssend email over SMTP
Next.jssend email over SMTP
Javasend email over SMTP
C# / .NETsend email over SMTP
Gosend email over SMTP
Ruby on Railssend email over SMTP
Postfix (Linux relayhost)send email over SMTP
curl / Bashsend email over SMTP
Flask SMTP — questions
Wrong credentials for the host (Gmail needs an App Password; relays need the SMTP credential, not your dashboard login).
Related Guides
Continue learning with these related articles