Automate Sending Emails with Python: A Complete Guide
Introduction
Email is still one of the most reliable ways to deliver notifications, reports, and alerts in production systems. Python developers often automate sending tasks such as server monitoring alerts, daily reports, invoices, and account notifications.
Python’s standard library already includes everything needed. The built-in smtplib module handles SMTP connections, and the email package builds properly formatted messages — no third-party libraries required.
In this tutorial, you will learn how to:
- Send plain text and HTML emails
- Attach files like PDFs or Excel reports
- Use Gmail SMTP with App Passwords
- Send to multiple recipients
- Handle common SMTP errors
All examples are tested on Python 3.12.
How Email Sending Works
When Python sends an email, it usually communicates with an SMTP server.
SMTP stands for Simple Mail Transfer Protocol. It is the standard protocol used to send emails across the internet.
There are a few important concepts to understand:
| Term | Meaning |
|---|---|
| SMTP Server | The mail server responsible for sending emails |
| Port | The network port used for communication |
| TLS/SSL | Encryption that protects passwords and email content in transit |
| Authentication | Logging in with an email account and password |
Common SMTP ports:
| Port | Encryption | Notes |
|---|---|---|
| 587 | TLS (STARTTLS) | Recommended for modern SMTP |
| 465 | SSL/TLS (implicit) | Still supported, less common today |
| 25 | None | Mostly blocked by ISPs, avoid |
Python’s role is straightforward:
- Connect to an SMTP server using
smtplib - Authenticate with an email account
- Build the email using the
emailpackage - Send the message
- Close the connection
A simplified workflow looks like this:
Python Script
↓
SMTP Server (Gmail / Outlook / Company Mail)
↓
Recipient Inbox
The email package creates the message structure, while smtplib handles the network communication.
Setting Up: Choosing an SMTP Server
Before sending emails, you need access to an SMTP server.
Gmail SMTP
SMTP Server: smtp.gmail.com
Port: 587
Encryption: TLS
Outlook SMTP
SMTP Server: smtp.office365.com
Port: 587
Encryption: TLS
Custom or Company Email
Many hosting providers and business email systems also provide SMTP access.
Typical examples:
smtp.yourcompany.com
mail.example.com
Your email provider usually documents:
- SMTP server address
- Port number
- Encryption type
- Authentication requirements
Gmail App Password Requirement
One of the biggest beginner problems is Gmail authentication.
Google no longer allows most applications to log in using your normal Gmail password. You must generate an App Password instead.
Steps to Create a Gmail App Password
- Log into your Google account
- Open Security settings
- Enable 2-Step Verification
- Search for App Passwords
- Create a new app password
- Copy the generated 16-character password
Example:
abcd efgh ijkl mnop
You can paste the App Password into your script with or without spaces — Python handles both. Just keep all 16 characters in order.
Use this App Password in your Python script instead of your normal Gmail password. If you skip this step, authentication will usually fail with SMTPAuthenticationError.
Sending a Plain Text Email
Now let’s send a real email.
This example:
- Connects to Gmail SMTP
- Logs into an account
- Sends a plain text message
- Closes the connection safely
Basic Email Example
import smtplib
from email.message import EmailMessage
sender_email = "yourname@gmail.com"
app_password = "your_app_password"
recipient_email = "yourname@gmail.com"
msg = EmailMessage()
msg["Subject"] = "Python SMTP Test"
msg["From"] = sender_email
msg["To"] = recipient_email
msg.set_content("""
Hello,
This email was sent automatically using Python.
Regards,
Python Script
""")
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(sender_email, app_password)
server.send_message(msg)
server.quit()
print("Email sent successfully")
Expected output:
Email sent successfully
This script creates a connection to Gmail’s SMTP server, upgrades it to a secure TLS connection, logs in, sends the email, and closes the session.
Understanding Each Step
1. Create the message object — EmailMessage() creates an empty message that will hold the subject, sender, recipient, and body.
2. Add headers — Assigning to msg["Subject"], msg["From"], and msg["To"] sets the visible email metadata.
3. Set the content — msg.set_content(...) adds the plain text body.
4. Connect to SMTP — smtplib.SMTP("smtp.gmail.com", 587) opens a network connection to Gmail’s mail server.
5. Enable encryption — server.starttls() upgrades the plain connection to a TLS-encrypted one before any credentials are sent.
6. Authenticate and send — server.login(...) authenticates the account, and server.send_message(msg) delivers the email.
When testing SMTP scripts for the first time, send emails to yourself. It makes debugging easier and avoids accidental spam behavior.
Sending an HTML Email
Plain text emails work fine for alerts, but HTML emails look more professional.
HTML emails allow you to:
- Add formatting
- Use clickable links
- Include branding
- Style reports or dashboards
To maximize compatibility, it’s best to include both:
- Plain text version
- HTML version
This is done with MIMEMultipart.
HTML Email Example
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
sender_email = "yourname@gmail.com"
app_password = "your_app_password"
recipient_email = "client@example.com"
msg = MIMEMultipart("alternative")
msg["Subject"] = "Weekly Sales Report"
msg["From"] = sender_email
msg["To"] = recipient_email
plain_text = """
Your weekly sales report is attached.
"""
html_content = """
<html>
<body>
<h2>Weekly Sales Report</h2>
<p>
The latest report is ready.<br>
Visit our dashboard:
<a href="https://example.com">
View Dashboard
</a>
</p>
</body>
</html>
"""
msg.attach(MIMEText(plain_text, "plain"))
msg.attach(MIMEText(html_content, "html"))
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(sender_email, app_password)
server.sendmail(sender_email, recipient_email, msg.as_string())
server.quit()
print("HTML email sent")
Expected output:
HTML email sent
The email client automatically chooses the best version it supports.
Sending Emails with Attachments
Many automation workflows need to deliver actual files, not just text.
Typical examples include:
- PDF invoices
- Excel reports
- Images
- CSV exports
Python handles attachments using MIMEBase.
Example: Send an Excel Report
Imagine a Python script generates a weekly sales spreadsheet named weekly_report.xlsx. This script attaches that file to an email.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
sender_email = "yourname@gmail.com"
app_password = "your_app_password"
recipient_email = "manager@example.com"
msg = MIMEMultipart()
msg["Subject"] = "Weekly Excel Report"
msg["From"] = sender_email
msg["To"] = recipient_email
body = """
Hello,
Please find the weekly sales report attached.
Regards,
Automation Script
"""
msg.attach(MIMEText(body, "plain"))
filename = "weekly_report.xlsx"
with open(filename, "rb") as attachment:
part = MIMEBase("application", "octet-stream")
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename={filename}",
)
msg.attach(part)
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(sender_email, app_password)
server.sendmail(
sender_email,
recipient_email,
msg.as_string()
)
server.quit()
print("Email with attachment sent")
Expected output:
Email with attachment sent
This script reads the Excel file in binary mode, encodes it safely for email transfer, and attaches it to the message.
For a more polished workflow, you can rename the report file automatically before attaching it — for example, adding a date prefix like 2026-05-15_weekly_report.xlsx so each report is clearly archived.
Sending PDFs or Images
You can attach almost any file type:
invoice.pdfsales_chart.pngmonthly_summary.csv
The process is the same regardless of file type.
Sending to Multiple Recipients
Sometimes a report must go to an entire team.
There are three common recipient fields:
| Field | Meaning |
|---|---|
| To | Main recipients |
| Cc | Visible copy recipients |
| Bcc | Hidden recipients |
Multiple Recipient Example
import smtplib
from email.message import EmailMessage
sender_email = "yourname@gmail.com"
app_password = "your_app_password"
to_emails = [
"alice@example.com",
"bob@example.com"
]
cc_emails = [
"manager@example.com"
]
msg = EmailMessage()
msg["Subject"] = "Team Update"
msg["From"] = sender_email
msg["To"] = ", ".join(to_emails)
msg["Cc"] = ", ".join(cc_emails)
msg.set_content("Weekly update attached.")
all_recipients = to_emails + cc_emails
server = smtplib.SMTP("smtp.gmail.com", 587)
server.starttls()
server.login(sender_email, app_password)
server.send_message(
msg,
from_addr=sender_email,
to_addrs=all_recipients
)
server.quit()
print("Email sent to multiple recipients")
Expected output:
Email sent to multiple recipients
Be careful with bulk sending. Gmail caps personal accounts at around 500 emails per day, and aggressive batch sends in tight loops can get your account temporarily suspended.
Common Errors and How to Fix Them
SMTP issues are common when setting up email automation for the first time.
SMTPAuthenticationError
SMTPAuthenticationError: Username and Password not accepted
Cause: Using your regular Gmail password instead of an App Password, an incorrect App Password, or 2-Step Verification not enabled.
Fix: Enable 2-Step Verification on your Google account, generate a fresh App Password, and use that 16-character password in your script.
SMTPSenderRefused
SMTPSenderRefused: Sender address rejected
Cause: The From address does not match the account you authenticated with, or the sender email format is invalid.
Fix: Make sure the From address exactly matches the Gmail account whose App Password you are using.
SMTP Connection Timeout
SMTPServerDisconnected
or:
Connection timed out
Cause: Wrong SMTP port, firewall restrictions, or unstable network.
Fix: Use port 587 with TLS, verify the SMTP hostname is correct, and confirm your network is not blocking outbound mail ports.
Gmail “Less Secure Apps” Warning
Older tutorials still mention enabling “Less Secure Apps.” That approach is outdated.
Since 2024, Gmail generally requires:
- 2-Step Verification
- App Password authentication
Use App Passwords instead of normal passwords.
Production Tips
Do Not Hardcode Passwords
Avoid this:
password = "mypassword123"
Use environment variables instead:
import os
password = os.getenv("EMAIL_PASSWORD")
This keeps credentials out of source code repositories.
Schedule Automatic Emails
Python email scripts become much more useful when automated.
Common options:
- Linux
cron - Windows Task Scheduler
- Python
schedulepackage
Typical examples:
- Daily sales reports
- Weekly backups
- Error monitoring alerts
A common production pattern is to chain multiple Python scripts together — for example, generate a report, rename it with a timestamp, then email it to the team. Future tutorials on this site will cover scheduling Python scripts with cron and Task Scheduler.
Add Logging and Retry Logic
Production systems should:
- Log failed sends
- Retry temporary failures
- Alert administrators if sending repeatedly fails
Even a simple retry system can improve reliability significantly.
For Large-Scale Email Sending
If you need to send thousands of emails, traditional SMTP accounts may become limiting.
Popular email delivery services include:
- SendGrid
- Mailgun
These platforms provide:
- Higher sending limits
- Better analytics
- Delivery monitoring
- Dedicated APIs
Wrap-Up
Python makes email automation surprisingly accessible. With only the standard library, you can send alerts, HTML reports, and file attachments through Gmail, Outlook, or company SMTP servers.
The next step is to adapt these examples to your own workflow and automate repetitive communication tasks. If you run into issues or have ideas for future tutorials, get in touch via the Contact page.
You may also be interested in how to rename hundreds of files in seconds with Python, which pairs naturally with email automation — many real workflows involve generating files, organizing them, and then sending them out by email.