Flask + MailKite
Flask has no swappable mail-transport interface — Flask-Mail wraps
smtplib directly. That means there's no MailKite package to
install: point Flask-Mail's existing config at MailKite's SMTP relay and
every mail.send() call goes through, with the same
DKIM-signed delivery and dashboard logs as the API.
What you need
- A verified domain with SPF + DKIM published
- Your API key (
mk_live_…) — it doubles as the SMTP password - Flask 2+ and Flask-Mail
A runnable starter
Rather than a snippet, MailKite ships a full working Flask app: a send-mail
form (SMTP relay + API mode side by side), a README, and env-var docs —
starters/flask/ in the MailKite repo. This page walks through
the same pieces.
Configure Flask-Mail
Set your env vars, then point Flask-Mail's config at MailKite's relay. These are the real connection details — see the SMTP relay reference for the full spec.
# .env
MAILKITE_API_KEY=mk_live_...
MAIL_DEFAULT_SENDER=hello@yourdomain.com
FLASK_SECRET_KEY=change-me # app.py
import os
from flask import Flask
from flask_mail import Mail
app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
# Flask-Mail wraps smtplib directly — no adapter needed, just config.
app.config["MAIL_SERVER"] = "smtp.mailkite.dev"
app.config["MAIL_PORT"] = 587 # STARTTLS (465 = implicit TLS also works)
app.config["MAIL_USE_TLS"] = True
app.config["MAIL_USERNAME"] = "mailkite" # literal string — MailKite only checks the password
app.config["MAIL_PASSWORD"] = os.environ["MAILKITE_API_KEY"] # the password IS your API key
app.config["MAIL_DEFAULT_SENDER"] = os.environ["MAIL_DEFAULT_SENDER"]
mail = Mail(app) Send email
Use Flask-Mail's normal Message + mail.send() — nothing MailKite-specific:
# app.py — a route that sends mail
from flask import request, flash, redirect, url_for
from flask_mail import Message
@app.post("/send")
def send():
msg = Message(
subject=request.form["subject"],
recipients=[request.form["to"]],
html=request.form["body"],
)
mail.send(msg)
flash("Sent!", "success")
return redirect(url_for("index")) API mode (alternative)
Once you need something SMTP can't express — templates, batch sends,
scheduled sends — call the MailKite Python SDK
directly instead. Same MAILKITE_API_KEY, a different transport:
# mailkite_api.py — API mode alternative (no SMTP)
# pip install mailkite-dev
import os
from mailkite import MailKite
mk = MailKite(os.environ["MAILKITE_API_KEY"])
def send_via_api(to, subject, body, sender):
return mk.send({
"from": sender,
"to": to,
"subject": subject,
"html": body,
}) Test it
# Verify the app boots and the form renders
python -m py_compile app.py mailkite_api.py
flask --app app run --debug
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:5000/ Troubleshooting
smtplib.SMTPAuthenticationError—MAIL_PASSWORDmust be yourmk_live_…API key, not a separate SMTP credential.- Connection refused — check
MAIL_PORT; MailKite's relay listens on 587 (STARTTLS) or 465 (implicit TLS), never 25. 403on send — theFrom:address isn't on a verified domain yet.- API-mode call raises something other than a MailKite error — wrap SDK calls in a broad
except Exceptionin production code; transport-layer failures (timeouts, proxies, WAFs) don't always surface as a clean SDK exception.
See the SMTP relay docs for the full connection reference, or Send API for the Python SDK's complete surface (templates, batch sends, attachments).