Automate Sending Emails with Python: A Complete Guide

· 9 min read

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:

TermMeaning
SMTP ServerThe mail server responsible for sending emails
PortThe network port used for communication
TLS/SSLEncryption that protects passwords and email content in transit
AuthenticationLogging in with an email account and password

Common SMTP ports:

PortEncryptionNotes
587TLS (STARTTLS)Recommended for modern SMTP
465SSL/TLS (implicit)Still supported, less common today
25NoneMostly blocked by ISPs, avoid

Python’s role is straightforward:

  1. Connect to an SMTP server using smtplib
  2. Authenticate with an email account
  3. Build the email using the email package
  4. Send the message
  5. 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

  1. Log into your Google account
  2. Open Security settings
  3. Enable 2-Step Verification
  4. Search for App Passwords
  5. Create a new app password
  6. 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 objectEmailMessage() 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 contentmsg.set_content(...) adds the plain text body.

4. Connect to SMTPsmtplib.SMTP("smtp.gmail.com", 587) opens a network connection to Gmail’s mail server.

5. Enable encryptionserver.starttls() upgrades the plain connection to a TLS-encrypted one before any credentials are sent.

6. Authenticate and sendserver.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.pdf
  • sales_chart.png
  • monthly_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:

FieldMeaning
ToMain recipients
CcVisible copy recipients
BccHidden 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 schedule package

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.