Python's smtplib module lets you send emails via SMTP servers. Combined with the email module for message construction, you can send plain text, HTML, and attachments.
Basic Email
import smtplib
from email.message import EmailMessage
# Create message
msg = EmailMessage()
msg['Subject'] = 'Hello from Python'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('This is a test email.')
# Send via SMTP
with smtplib.SMTP('smtp.example.com', 587) as server:
server.starttls()
server.login('username', 'password')
server.send_message(msg)Using Gmail
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Test from Gmail'
msg['From'] = 'you@gmail.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Sent via Python!')
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
server.login('you@gmail.com', 'app-password')
server.send_message(msg)Note: Gmail requires an "App Password" (not your main password) when 2FA is enabled.
HTML Email
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'HTML Email'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Plain text fallback
msg.set_content('Your email client does not support HTML.')
# HTML version
msg.add_alternative("""
<html>
<body>
<h1>Hello!</h1>
<p>This is an <b>HTML</b> email.</p>
</body>
</html>
""", subtype='html')Attachments
from email.message import EmailMessage
import mimetypes
msg = EmailMessage()
msg['Subject'] = 'Document attached'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Please see the attached file.')
# Attach a file
filename = 'report.pdf'
with open(filename, 'rb') as f:
file_data = f.read()
mime_type, _ = mimetypes.guess_type(filename)
maintype, subtype = mime_type.split('/')
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=filename
)Multiple Recipients
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Team Update'
msg['From'] = 'sender@example.com'
msg['To'] = 'alice@example.com, bob@example.com'
msg['Cc'] = 'manager@example.com'
msg['Bcc'] = 'archive@example.com'
msg.set_content('Meeting at 3pm.')SMTP Connection Options
import smtplib
# Plain SMTP (port 25) - rarely used, often blocked
with smtplib.SMTP('mail.example.com', 25) as server:
server.send_message(msg)
# STARTTLS (port 587) - starts plain, upgrades to TLS
with smtplib.SMTP('mail.example.com', 587) as server:
server.starttls()
server.login('user', 'pass')
server.send_message(msg)
# SSL/TLS (port 465) - encrypted from start
with smtplib.SMTP_SSL('mail.example.com', 465) as server:
server.login('user', 'pass')
server.send_message(msg)Error Handling
import smtplib
try:
with smtplib.SMTP('smtp.example.com', 587) as server:
server.starttls()
server.login('user', 'pass')
server.send_message(msg)
except smtplib.SMTPAuthenticationError:
print("Login failed")
except smtplib.SMTPConnectError:
print("Could not connect to server")
except smtplib.SMTPRecipientsRefused:
print("All recipients refused")
except smtplib.SMTPException as e:
print(f"SMTP error: {e}")Debug Mode
import smtplib
with smtplib.SMTP('smtp.example.com', 587) as server:
server.set_debuglevel(1) # Print SMTP conversation
server.starttls()
server.login('user', 'pass')
server.send_message(msg)Email Builder Function
import smtplib
from email.message import EmailMessage
import mimetypes
from pathlib import Path
def send_email(
to: str | list,
subject: str,
body: str,
html: str = None,
attachments: list = None,
smtp_host: str = 'smtp.example.com',
smtp_port: int = 587,
username: str = None,
password: str = None,
):
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = username
msg['To'] = to if isinstance(to, str) else ', '.join(to)
msg.set_content(body)
if html:
msg.add_alternative(html, subtype='html')
if attachments:
for filepath in attachments:
path = Path(filepath)
mime_type, _ = mimetypes.guess_type(str(path))
mime_type = mime_type or 'application/octet-stream'
maintype, subtype = mime_type.split('/')
msg.add_attachment(
path.read_bytes(),
maintype=maintype,
subtype=subtype,
filename=path.name
)
with smtplib.SMTP(smtp_host, smtp_port) as server:
server.starttls()
server.login(username, password)
server.send_message(msg)
# Usage
send_email(
to=['alice@example.com', 'bob@example.com'],
subject='Weekly Report',
body='Please see attached.',
attachments=['report.pdf', 'data.csv'],
username='me@example.com',
password='secret'
)Local Testing
Use Python's built-in debug server:
# Terminal 1: Start debug server
python -m smtpd -c DebuggingServer -n localhost:1025# Terminal 2: Send to debug server
with smtplib.SMTP('localhost', 1025) as server:
server.send_message(msg)Environment Variables for Credentials
import os
import smtplib
SMTP_HOST = os.environ.get('SMTP_HOST', 'smtp.example.com')
SMTP_USER = os.environ['SMTP_USER']
SMTP_PASS = os.environ['SMTP_PASS']
with smtplib.SMTP(SMTP_HOST, 587) as server:
server.starttls()
server.login(SMTP_USER, SMTP_PASS)
server.send_message(msg)Summary
smtplib handles the SMTP protocol:
SMTP()for plain/STARTTLS connectionsSMTP_SSL()for SSL connectionsstarttls()to upgrade to encryptedlogin()for authenticationsend_message()to send
Use the email module (EmailMessage) to construct messages with HTML and attachments. Always use TLS in production.
React to this post: