Skip to main content

How to Automate Birthday Emails with Python and Pandas

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
Date Format

This script uses the DD-MM format (e.g., 15-05 for May 15th). Ensure your CSV matches this format exactly.

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.
Security Warning

Never hardcode this password into your script. We will use Environment Variables to keep it safe.

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

# Load credentials from Environment Variables
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:
# Connect to Gmail's SSL port
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():
# 1. Load Data
try:
df = pd.read_csv("birthdays.csv")
except FileNotFoundError:
print("Error: birthdays.csv not found.")
return

# 2. Get Today's Date (DD-MM)
today = datetime.now().strftime("%d-%m")
current_year = datetime.now().year

email_sent_count = 0

# 3. Iterate through rows
for i, row in df.iterrows():
# Clean data types
bday_date = str(row['birthday']).strip()
last_year = int(row['last_sent_year']) if pd.notna(row['last_sent_year']) else 0

# Logic: Match Date AND Ensure not sent this year
if bday_date == today and last_year != current_year:

success = send_email(row['email'], row['name'])

if success:
# Update DataFrame in memory
df.at[i, 'last_sent_year'] = current_year
email_sent_count += 1

# 4. Save updates back to CSV (Only if changes were made)
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

ComponentTechnologyPurpose
Data Storagepandas + CSVStores dates and tracks state (last_sent_year).
EmailsmtplibHandles secure connection to Gmail.
SecurityApp PasswordAvoids using real Google passwords in code.
AutomationCron / Task SchedulerEnsures the script runs daily without intervention.