smtplib module
Overview
smtplib is Python’s standard library for sending email over SMTP (Simple Mail Transfer Protocol). It handles the protocol-level details — connecting to a mail server, authenticating, sending the envelope, and closing the connection. Pair it with the email module to construct properly formatted messages with headers and attachments.
Sending a Simple Email
With a local SMTP server
If you have an SMTP server running locally (common on Linux servers):
import smtplib
with smtplib.SMTP("localhost", 587) as server:
server.sendmail(
"alice@example.com",
["bob@example.com"],
"Subject: Hello\n\nBody of the message."
)
With an external SMTP server
For Gmail, AWS SES, Mailgun, or any external provider:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "alice@example.com"
msg["To"] = "bob@example.com"
msg["Subject"] = "Hello from smtplib"
msg.set_content("This is a plain text email sent with Python.")
with smtplib.SMTP("smtp.example.com", 587) as server:
server.starttls() # upgrade to TLS
server.login("alice@example.com", "app_password")
server.send_message(msg)
SMTP Connection Classes
smtplib provides three connection classes:
| Class | Description |
|---|---|
SMTP | Plain SMTP, unencrypted |
SMTP_SSL | SMTP over SSL, encrypted from the start |
SMTP_TLS (context manager) | Starts unencrypted, upgrades via STARTTLS |
import smtplib
# Plain SMTP on port 25 (unencrypted, rarely used)
server = smtplib.SMTP("smtp.example.com", 25)
# SMTP over SSL on port 465
server = smtplib.SMTP_SSL("smtp.example.com", 465)
# SMTP with STARTTLS on port 587 (most common)
server = smtplib.SMTP("smtp.example.com", 587)
server.starttls()
Authentication
Use login() with your username and password or app password:
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("your-email@gmail.com", "your-app-password")
server.sendmail("your-email@gmail.com", ["recipient@example.com"], msg.as_string())
Never put passwords in source code. Use environment variables or a secrets manager:
import os
import smtplib
password = os.environ.get("SMTP_PASSWORD")
if not password:
raise ValueError("SMTP_PASSWORD environment variable not set")
server.login("alice@example.com", password)
Using TLS (STARTTLS)
STARTTLS upgrades an unencrypted connection to TLS. Always call starttls() after connecting and before authenticating:
import smtplib
with smtplib.SMTP("smtp.example.com", 587) as server:
server.ehlo() # identify yourself to the server
server.starttls(context=None) # upgrade to TLS
server.ehlo() # re-identify over TLS
server.login("alice@example.com", password)
server.sendmail("alice@example.com", ["bob@example.com"], msg.as_string())
The context parameter accepts an ssl.SSLContext if you need specific TLS versions or certificate verification settings.
Constructing Messages with email
Use the email.message.EmailMessage class for properly formatted messages with headers and attachments:
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "alice@example.com"
msg["To"] = "bob@example.com, carol@example.com"
msg["Cc"] = "dave@example.com"
msg["Subject"] = "Report attached"
msg.set_content("Please find the monthly report attached.")
# Add an attachment
with open("report.pdf", "rb") as f:
msg.add_attachment(
f.read(),
maintype="application",
subtype="pdf",
filename="monthly_report.pdf"
)
with smtplib.SMTP("smtp.example.com", 587) as server:
server.starttls()
server.login("alice@example.com", password)
server.send_message(msg)
Sending HTML Email
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "alice@example.com"
msg["To"] = "bob@example.com"
msg["Subject"] = "HTML Email"
msg.add_alternative(
"<html><body><h1>Hello</h1><p>This is an <strong>HTML</strong> email.</p></body></html>",
subtype="html"
)
with smtplib.SMTP("smtp.example.com", 587) as server:
server.starttls()
server.login("alice@example.com", password)
server.send_message(msg)
Sending to Multiple Recipients
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "alice@example.com"
msg["To"] = "bob@example.com, carol@example.com"
msg["Subject"] = "Group email"
msg.set_content("Hello everyone.")
with smtplib.SMTP("smtp.example.com", 587) as server:
server.starttls()
server.login("alice@example.com", password)
# send_message handles To/Cc/Bcc automatically
server.send_message(msg)
# Or manually with separate recipients
recipients = ["bob@example.com", "carol@example.com", "dave@example.com"]
server.sendmail("alice@example.com", recipients, msg.as_string())
Error Handling
import smtplib
from email.message import EmailMessage
try:
with smtplib.SMTP("smtp.example.com", 587, timeout=10) as server:
server.starttls()
server.login("alice@example.com", password)
server.send_message(msg)
print("Email sent successfully")
except smtplib.SMTPAuthenticationError:
print("Authentication failed. Check your username and password.")
except smtplib.SMTPException as e:
print(f"SMTP error: {e}")
except TimeoutError:
print("Connection timed out. The SMTP server may be unreachable.")
Common exceptions:
SMTPAuthenticationError— wrong username/password or app passwordSMTPRecipientsRefused— all recipients were rejected by the serverSMTPSenderRefused— the sender address was rejectedSMTPConnectError— couldn’t connect to the serverSMTPDataError— the server rejected the message data
SMTP Settings for Common Providers
Gmail
import smtplib
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("your-email@gmail.com", "your-16-char-app-password")
# Gmail requires an App Password, not your regular password
AWS SES
import smtplib
with smtplib.SMTP("email-smtp.us-east-1.amazonaws.com", 587) as server:
server.starttls()
server.login("AWS_ACCESS_KEY", "AWS_SECRET_KEY")
Office 365 / Outlook
import smtplib
with smtplib.SMTP("smtp.office365.com", 587) as server:
server.starttls()
server.login("your-email@outlook.com", "your-password")
Connection Context Manager
Always use a context manager to ensure the connection closes:
import smtplib
# Correct — connection is closed even if an error occurs
with smtplib.SMTP("smtp.example.com", 587) as server:
server.starttls()
server.login("alice@example.com", password)
server.send_message(msg)
# Wrong — connection stays open if an error occurs
server = smtplib.SMTP("smtp.example.com", 587)
server.send_message(msg) # connection never closed if this raises
Gotchas
Port 587 uses STARTTLS, port 465 uses SSL from the start. These are different protocols. Port 587 is more common for modern deployments. If you use SMTP_SSL on port 587 or plain SMTP on port 465, the connection will fail or behave unexpectedly.
Passwords aren’t enough for most providers. Gmail, Microsoft, and most cloud providers require an app password or OAuth2. Plain username/password authentication over TLS is increasingly deprecated.
send_message() is preferred over sendmail(). send_message() parses the To, Cc, and Bcc headers correctly, while sendmail() requires you to manage the envelope separately from the message headers.
SMTP has a strict line length limit. The SMTP protocol limits lines to 998 octets. Python’s email module handles this automatically when you use EmailMessage and send_message(). If you use sendmail() with a raw string, you need to ensure lines don’t exceed the limit.
The timeout parameter prevents hanging. Set a timeout when connecting to avoid blocking indefinitely on unreachable servers:
server = smtplib.SMTP("smtp.example.com", 587, timeout=30)
See Also
- /reference/modules/email-module/ — parsing and constructing email messages
- /guides/python-string-templates/ — formatting email body content with string templates
- /tutorials/python-and-data/working-with-apis/ — connecting to external services including SMTP providers