DEV Community

sopher250
sopher250

Posted on • Originally published at byteleveltech.com

Send Emails with Python — Automate Notifications, Reports, and Alerts (Beginner Guide)

Sending emails manually is fine when you send one or two. But what about daily reports to your team? Getting alerted when your web scraper finds something? Emailing yourself when a backup finishes?

That's where Python email automation comes in. No third-party services, no paid APIs — just Python's built-in smtplib.

Setup: Gmail App Password (2 Minutes)

Before writing code, you need an App Password. Google blocks "less secure apps" from using regular passwords.

  1. Go to Google Account Security
  2. Enable 2-Step Verification if you haven't
  3. Search for "App Passwords" → Select "Mail" → "Other" → name it "Python Script"
  4. Copy the 16-character password

Then set it as an environment variable so it never touches your code:

export GMAIL_APP_PASSWORD="xxxx xxxx xxxx xxxx"
Enter fullscreen mode Exit fullscreen mode

Your First Automated Email in 10 Lines

import smtplib
from email.mime.text import MIMEText

SENDER = "yourname@gmail.com"
PASSWORD = "xxxx xxxx xxxx xxxx"  # App Password
RECEIVER = "yourname@gmail.com"

msg = MIMEText("Hey! This email was sent by a Python script.")
msg["Subject"] = "My First Automated Email"
msg["From"] = SENDER
msg["To"] = RECEIVER

with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
    server.login(SENDER, PASSWORD)
    server.send_message(msg)

print("Email sent!")
Enter fullscreen mode Exit fullscreen mode

Keeping Secrets Safe

Hardcoding passwords = bad. If you commit it to GitHub, bots scrape it within minutes. Use environment variables:

import os

SENDER = os.environ["GMAIL_ADDRESS"]
PASSWORD = os.environ["GMAIL_APP_PASSWORD"]
Enter fullscreen mode Exit fullscreen mode

Now run with:

GMAIL_ADDRESS="you@gmail.com" \
GMAIL_APP_PASSWORD="your-app-password" \
python send_email.py
Enter fullscreen mode Exit fullscreen mode

HTML Emails That Look Professional

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

msg = MIMEMultipart("alternative")
msg["Subject"] = "Weekly Price Report"
msg["From"] = SENDER
msg["To"] = RECEIVER

text = "Plain text fallback for email clients that block HTML"
html = """
<html><body>
  <h2>Weekly Price Report</h2>
  <table border="1" cellpadding="8">
    <tr style="background:#f0f0f0"><th>Product</th><th>Price</th><th>Change</th></tr>
    <tr><td>Coffee Maker</td><td style="color:green">$39.99</td><td>↓ $10.00</td></tr>
  </table>
</body></html>"""

msg.attach(MIMEText(text, "plain"))
msg.attach(MIMEText(html, "html"))

with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
    server.login(SENDER, PASSWORD)
    server.send_message(msg)
Enter fullscreen mode Exit fullscreen mode

Adding File Attachments

from email.mime.base import MIMEBase
from email import encoders

def attach_file(msg, filepath):
    with open(filepath, "rb") as f:
        part = MIMEBase("application", "octet-stream")
        part.set_payload(f.read())
        encoders.encode_base64(part)
        part.add_header(
            "Content-Disposition",
            f'attachment; filename="{os.path.basename(filepath)}"',
        )
        msg.attach(part)

# Usage
msg = MIMEMultipart()
msg["Subject"] = "Monthly Backup Report"
msg["From"] = SENDER
msg["To"] = RECEIVER
msg.attach(MIMEText("Backup completed. Log attached."))
attach_file(msg, "/var/log/backup.log")
Enter fullscreen mode Exit fullscreen mode

Gmail Sending Limits

Limit Free Account
Daily quota 500 emails
Rate ~1 email/second

For personal automation, 500/day is more than enough. For bulk newsletters, use Mailgun or SendGrid.

Real Use Case: Price Drop Alert

"""price-alert.py — Check price and email if it drops."""
import os, json, smtplib
from email.mime.text import MIMEText
from pathlib import Path

PRICE_FILE = Path.home() / ".price-history.json"
SENDER = os.environ["GMAIL_ADDRESS"]
PASSWORD = os.environ["GMAIL_APP_PASSWORD"]

def get_price():
    # Your web scraping logic here
    return 39.99

def send_alert(current, previous):
    drop = previous - current
    msg = MIMEText(f"Price dropped from ${previous:.2f} to ${current:.2f} (↓ ${drop:.2f})")
    msg["Subject"] = f"💰 Price Drop Alert: ${previous} → ${current}"
    msg["From"] = SENDER
    msg["To"] = SENDER
    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
        server.login(SENDER, PASSWORD)
        server.send_message(msg)

history = json.loads(PRICE_FILE.read_text()) if PRICE_FILE.exists() else {}
current = get_price()
previous = history.get("last_price", current)

if current < previous:
    send_alert(current, previous)

history.update({"last_price": current})
PRICE_FILE.write_text(json.dumps(history, indent=2))
Enter fullscreen mode Exit fullscreen mode

Schedule with cron:

0 8 * * * /usr/bin/python3 /home/user/price-alert.py
Enter fullscreen mode Exit fullscreen mode

Now every morning at 8 AM, it checks the price and emails you if it drops. Wake up to savings.

What's Next?

You now have the foundation for a complete automation pipeline: collect data → process it → email results → schedule with cron.

The full guide on my blog includes alternate email providers (Outlook, Yahoo, QQ Mail), more attachment examples, and debugging tips: Send Emails with Python — Full Guide


What's the first automated email you'd build? A backup notification? A price tracker? Let me know in the comments!

Top comments (0)