Skip to content

Python Sending Email

The Simple Mail Transfer Protocol (SMTP) is the standard internet protocol for sending electronic mail (email). Python’s built-in smtplib module provides client-side functionality to connect to an SMTP server and send emails.

To construct email messages robustly (handling headers, different content types, attachments), the standard email.message module is highly recommended over manual string formatting.

You create an SMTP client session object using smtplib.SMTP or smtplib.SMTP_SSL (for servers requiring an SSL connection from the start). It’s crucial to use encrypted connections (SSL/TLS) whenever possible.

import smtplib
# Option 1: Connect to standard SMTP port, then upgrade to TLS
# smtp_server = smtplib.SMTP(host, port)
# smtp_server.starttls() # Secure the connection
# Option 2: Connect directly using SSL
# smtp_server = smtplib.SMTP_SSL(host, port)
# Example for Gmail (requires App Password if 2FA is enabled)
# host = 'smtp.gmail.com'
# port = 465 # For SSL
# smtp_server = smtplib.SMTP_SSL(host, port)
# Example for local debugging server (no encryption)
# python -m smtpd -c DebuggingServer -n localhost:1025
# smtp_server = smtplib.SMTP('localhost', 1025)

Parameters for SMTP() / SMTP_SSL():

After connecting, you typically need to authenticate:

# smtp_server.login(sender_email, password)

Security Note: Avoid hardcoding credentials directly in your script. Use environment variables, configuration files, or secure secret management tools.

Use the email.message.EmailMessage class to build the email structure.

import smtplib
import os
from email.message import EmailMessage
# --- Configuration (Load securely, e.g., from environment variables) ---
SMTP_HOST = 'smtp.example.com' # Replace with your SMTP host
SMTP_PORT = 587 # Replace with your SMTP port (TLS)
SENDER_EMAIL = 'your_email@example.com' # Replace
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD') # Example: Get from env var
RECEIVER_EMAIL = 'recipient@example.com' # Replace
# --------------------------------------------------------------------
if not SENDER_PASSWORD:
print("Error: Email password not set in environment variable EMAIL_PASSWORD")
exit()
# Create the email message object
msg = EmailMessage()
msg['Subject'] = 'Test Email from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
msg.set_content('This is a plain text email sent using Python.')
try:
# Connect, login, and send
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls() # Secure the connection (if using port 587)
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("Email sent successfully!")
except smtplib.SMTPAuthenticationError:
print("Authentication failed. Check email/password or app-specific password setting.")
except smtplib.SMTPException as e:
print(f"An SMTP error occurred: {e}")
except Exception as e:
print(f"An error occurred: {e}")

Key steps:

To send HTML content, use msg.add_alternative().

import smtplib
import os
from email.message import EmailMessage
# --- Configuration (as before) ---
SMTP_HOST = 'smtp.example.com'
SMTP_PORT = 587
SENDER_EMAIL = 'your_email@example.com'
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD')
RECEIVER_EMAIL = 'recipient@example.com'
# ----------------------------------
if not SENDER_PASSWORD:
print("Error: Email password not set.")
exit()
msg = EmailMessage()
msg['Subject'] = 'HTML Email Test from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
# Set plain text content (fallback for clients that don't support HTML)
msg.set_content('This is the plain text version.')
# Add HTML alternative
html_content = """
<html>
<body>
<p>This is an **HTML** email message.</p>
<h1>Headline</h1>
<p>Sent using Python's `smtplib` and `email.message` modules.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html')
try:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("HTML email sent successfully!")
except Exception as e:
print(f"An error occurred: {e}")

add_alternative() adds a different representation of the same content. Email clients will typically display the richest format they support (HTML in this case). Providing a plain text fallback is good practice.

Use msg.add_attachment() to include files.

import smtplib
import os
from email.message import EmailMessage
import mimetypes # To guess file type
# --- Configuration (as before) ---
SMTP_HOST = 'smtp.example.com'
SMTP_PORT = 587
SENDER_EMAIL = 'your_email@example.com'
SENDER_PASSWORD = os.environ.get('EMAIL_PASSWORD')
RECEIVER_EMAIL = 'recipient@example.com'
# ----------------------------------
if not SENDER_PASSWORD:
print("Error: Email password not set.")
exit()
ATTACHMENT_PATH = 'files/document.pdf' # Replace with your file path
if not os.path.exists(ATTACHMENT_PATH):
print(f"Error: Attachment file not found at {ATTACHMENT_PATH}")
exit()
msg = EmailMessage()
msg['Subject'] = 'Email with Attachment from Python'
msg['From'] = SENDER_EMAIL
msg['To'] = RECEIVER_EMAIL
msg.set_content('Please find the attached document.')
# --- Add Attachment ---
# Guess the MIME type
mime_type, _ = mimetypes.guess_type(ATTACHMENT_PATH)
if mime_type is None:
# Default to generic binary if type cannot be guessed
mime_type = 'application/octet-stream'
maintype, subtype = mime_type.split('/', 1)
with open(ATTACHMENT_PATH, 'rb') as fp:
msg.add_attachment(fp.read(),
maintype=maintype,
subtype=subtype,
filename=os.path.basename(ATTACHMENT_PATH))
# ---------------------
try:
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
server.starttls()
server.login(SENDER_EMAIL, SENDER_PASSWORD)
server.send_message(msg)
print("Email with attachment sent successfully!")
except Exception as e:
print(f"An error occurred: {e}")

Key steps for attachments: