Skip to content

How to Automate Birthday Emails with Python and Pandas

Build a secure Python script using Pandas and SMTP to automatically send personalized birthday emails daily while avoiding duplicates.

Building a script to wish your friends, colleagues, or customers "Happy Birthday" is an excellent automation project. It combines three essential Python skills: Data manipulation with Pandas, email dispatching with SMTP, and task scheduling.

This guide walks you through creating a secure, automated system that checks dates and sends personalized emails daily.

1. Setting Up the Data

First, we need a data source. A simple CSV file works best for this. We will track the last_sent_year to ensure we don't send duplicate emails if the script runs multiple times in one day.

Create a file named birthdays.csv:

name,email,birthday,last_sent_year
Alice,alice@example.com,15-05,2023
Bob,bob@test.com,01-02,2022
Charlie,charlie@company.org,20-11,0

2. Google Security: App Passwords

If you use Gmail, you cannot use your standard password for scripts due to modern security protocols (2FA). You must generate an App Password.

  1. Go to Google Account > Security.
  2. Enable 2-Step Verification (if not active).
  3. Search for "App Passwords".
  4. Create a new app named "Python Script".
  5. Copy the 16-character code generated.

3. The Python Automation Script

This script performs the following actions:

  1. Loads the CSV using pandas.
  2. Checks if today matches any birthday.
  3. Checks last_sent_year to avoid duplicates.
  4. Sends the email using smtplib.
  5. Updates the CSV so the script knows the email was sent for the current year.
import smtplib
import os
import pandas as pd
from datetime import datetime
from email.message import EmailMessage

EMAIL_ADDR = os.getenv('MY_EMAIL')
EMAIL_PASS = os.getenv('MY_EMAIL_PASS')  # The 16-char App Password


def send_email(to_email: str, name: str) -> bool:
    """Sends a formatted birthday email via Gmail SMTP."""
    msg = EmailMessage()
    msg.set_content(f"Hi {name},\n\nHappy Birthday! Have a fantastic day! 🎉\n\nBest,\nAutomated Bot")
    msg['Subject'] = "Happy Birthday! 🎂"
    msg['From'] = EMAIL_ADDR
    msg['To'] = to_email

    try:
        with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
            server.login(EMAIL_ADDR, EMAIL_PASS)
            server.send_message(msg)
            print(f"✅ Sent email to {name}")
            return True
    except Exception as e:
        print(f"❌ Failed to send to {name}: {e}")
        return False


def main():
    try:
        df = pd.read_csv("birthdays.csv")
    except FileNotFoundError:
        print("Error: birthdays.csv not found.")
        return

    today = datetime.now().strftime("%d-%m")
    current_year = datetime.now().year
    
    email_sent_count = 0

    for i, row in df.iterrows():
        bday_date = str(row['birthday']).strip()
        last_year = int(row['last_sent_year']) if pd.notna(row['last_sent_year']) else 0
        
        if bday_date == today and last_year != current_year:
            
            success = send_email(row['email'], row['name'])
            
            if success:
                df.at[i, 'last_sent_year'] = current_year
                email_sent_count += 1

    if email_sent_count > 0:
        df.to_csv("birthdays.csv", index=False)
        print(f"Updated CSV. Total emails sent: {email_sent_count}")
    else:
        print("No birthdays found today (or already sent).")


if __name__ == "__main__":
    if not EMAIL_ADDR or not EMAIL_PASS:
        print("❌ Error: Environment variables MY_EMAIL or MY_EMAIL_PASS are missing.")
    else:
        main()

4. Scheduling the Script

A script is only useful if it runs automatically. You don't want to run this manually every morning.

Windows (Task Scheduler)

  1. Open Task Scheduler.
  2. Click Create Basic Task.
  3. Trigger: Daily at 09:00 AM.
  4. Action: Start a Program.
    • Program: python.exe (Use the full path, e.g., C:\Python39\python.exe)
    • Arguments: C:\Path\To\your_script.py
  5. Environment: You may need to set the environment variables in the user profile or pass them in a .bat wrapper.

Linux / macOS (Cron)

Open your crontab config:

crontab -e

Add the following line to run every day at 9:00 AM:

0 9 * * * export MY_EMAIL="me@gmail.com"; export MY_EMAIL_PASS="xxxx"; /usr/bin/python3 /home/user/birthday_script.py >> /tmp/cron_log.txt 2>&1

Summary

Component Technology Purpose
Data Storage pandas + CSV Stores dates and tracks state (last_sent_year).
Email smtplib Handles secure connection to Gmail.
Security App Password Avoids using real Google passwords in code.
Automation Cron / Task Scheduler Ensures the script runs daily without intervention.
Navigation