How to Send Transactional Email in Python with smtplib, STARTTLS & Failover (2026)
Learn how to build production-grade transactional email in Python using the built-in smtplib and email.message modules. Includes STARTTLS, HTML templates with plaintext fallbacks, and connection pooling.
Executive Key Takeaways
- •Always send MIME multipart/alternative emails with both HTML and plain-text versions to maximize deliverability and accessibility.
- •Use port 587 with STARTTLS or port 465 with SSL/TLS; never transmit SMTP credentials over unencrypted port 25.
- •In high-concurrency Python applications (FastAPI, Celery, Django), reuse SMTP connections or dispatch tasks asynchronously to avoid blocking the event loop.
- •CentricSell Mail provides a drop-in ESMTP relay on port 587 that works with standard smtplib without installing third-party SDKs.
Table of Contents
1. Python smtplib Fundamentals
Python includes robust email capabilities in its standard library through smtplib and email.message. You don't need heavy third-party vendor SDKs to send reliable transactional email.
A production SMTP client must always fulfill three core requirements: authenticated login (AUTH LOGIN or AUTH PLAIN), encrypted transport (STARTTLS or SSL), and properly formatted RFC 5322 MIME messages.
2. Building Modern HTML & Plaintext MIME Messages
Spam filters heavily penalize emails that contain only HTML without a corresponding plain-text alternative. Use Python 3.6+'s modern EmailMessage class to construct multipart messages seamlessly:
3. Complete Production-Ready Python Script
import smtplib
from email.message import EmailMessage
def send_transactional_email(recipient: str, subject: str, html_body: str, text_body: str):
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = "CentricSell Mail <notifications@yourdomain.com>"
msg["To"] = recipient
# Set plain text fallback first
msg.set_content(text_body)
# Add HTML alternative
msg.add_alternative(html_body, subtype="html")
# Connect to CentricSell Mail SMTP relay
with smtplib.SMTP("smtp.centricsell.com", 587, timeout=15) as server:
server.starttls()
server.login("your-api-key-id", "your-api-key-secret")
server.send_message(msg)
# Usage
send_transactional_email(
recipient="user@example.com",
subject="Your Secure Verification Code",
html_body="<h1>Welcome!</h1><p>Your login code is <strong>849201</strong>.</p>",
text_body="Welcome! Your login code is 849201."
)
4. Async & Background Dispatch in FastAPI and Celery
Because SMTP connections involve a network handshake, TLS negotiation, and data transmission (typically taking 200-800ms), you should always dispatch sends in the background:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
@app.post("/signup")
async def signup(email: str, background_tasks: BackgroundTasks):
# Process user signup...
background_tasks.add_task(
send_transactional_email,
recipient=email,
subject="Welcome to our platform",
html_body="<p>Account confirmed!</p>",
text_body="Account confirmed!"
)
return {"status": "ok"}
5. Robust Error Handling (SMTPException, SMTPServerDisconnected)
Wrap your SMTP sends with proper exception handling to catch transient network drops and provider throttles:
smtplib.SMTPAuthenticationError: Invalid API key or credentials.smtplib.SMTPRecipientsRefused: Recipient address rejected (add to suppression list).smtplib.SMTPServerDisconnected: Connection timed out; safe to retry.
Automate Developer Integration in 5 Minutes
Stop manually wrestling with DNS records and server configs. CentricSell Mail runs automated pre-flight deliverability checks, provisions dedicated servers over SSH, and routes traffic across Amazon SES, SendGrid, and Mailgun.
Frequently Asked Questions
Should I use port 587 or 465 in Python smtplib?
Port 587 with server.starttls() is the modern standard (RFC 6409). Port 465 uses implicit SSL (smtplib.SMTP_SSL). Both are secure; CentricSell Mail supports both 587 and 465.
How do I prevent smtplib from blocking my FastAPI web server?
Never call smtplib.SMTP synchronously in an async route. Run it inside a background task (FastAPI BackgroundTasks), a thread pool (asyncio.to_thread), or an asynchronous queue like Celery or RQ.
Ready to scale your transactional & bulk email?
Get started with CentricSell Mail in minutes. Bring your existing AWS SES, SendGrid, or Mailgun credentials, or deploy onto your own dedicated VPS with verified SPF, DKIM, and rDNS.