A lightweight, production-style email automation API built with FastAPI β supports single SMTP sends, bulk CSV campaigns, environment-based config, structured logging, and unit tests.
- π¨ Single email sending via SMTP (TLS/SSL support)
- π Bulk email campaigns from a CSV file
- π Environment variable config β no secrets in code
- π Structured logging with log levels and file output
- π§ͺ Unit tests with
pytest - π Modular architecture β clean separation of concerns
- β‘ FastAPI with automatic OpenAPI docs at
/docs
email-automation/
βββ app/
β βββ api/
β β βββ __init__.py
β β βββ routes/
β β βββ __init__.py
β β βββ email.py # Single email endpoint
β β βββ bulk.py # Bulk CSV email endpoint
β βββ core/
β β βββ __init__.py
β β βββ config.py # Settings from .env via Pydantic
β β βββ logging.py # Logger setup
β βββ models/
β β βββ __init__.py
β β βββ email.py # Pydantic request/response models
β βββ services/
β β βββ __init__.py
β β βββ email_service.py # SMTP logic (single + bulk)
β βββ utils/
β βββ __init__.py
β βββ csv_parser.py # CSV reading and validation
βββ tests/
β βββ __init__.py
β βββ test_email_service.py # Unit tests for email service
β βββ test_csv_parser.py # Unit tests for CSV utility
βββ sample_data/
β βββ recipients.csv # Example bulk email CSV
βββ logs/ # Auto-created at runtime
βββ .env.example # Environment variable template
βββ .gitignore
βββ requirements.txt
βββ main.py # App entrypoint
βββ README.md
git clone https://github.com/yourusername/email-automation.git
cd email-automationpython -m venv venv
# macOS / Linux
source venv/bin/activate
# Windows
venv\Scripts\activatepip install -r requirements.txtcp .env.example .envOpen .env and fill in your SMTP credentials:
# SMTP Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USERNAME=your_email@gmail.com
SMTP_PASSWORD=your_app_password
SMTP_USE_TLS=true
# Sender Info
DEFAULT_SENDER_NAME=Your Name
DEFAULT_SENDER_EMAIL=your_email@gmail.com
# App Settings
APP_ENV=development
LOG_LEVEL=INFO
LOG_FILE=logs/app.logGmail users: Use an App Password, not your account password.
uvicorn main:app --reloadAPI is now live at: http://localhost:8000
Interactive docs: http://localhost:8000/docs
Send a single email.
Request Body:
{
"to": "recipient@example.com",
"subject": "Hello from FastAPI",
"body": "This is a test email.",
"html": false
}Response:
{
"status": "success",
"message": "Email sent to recipient@example.com"
}Send emails to multiple recipients from an uploaded CSV file.
Form Data:
fileβ CSV file upload (see format below)subjectβ Email subject linebodyβ Email body text (supports{name}placeholder)
Example recipients.csv:
name,email
Alice Johnson,alice@example.com
Bob Smith,bob@example.com
Carol White,carol@example.comResponse:
{
"status": "success",
"total": 3,
"sent": 3,
"failed": 0,
"errors": []
}Health check endpoint.
{ "status": "ok", "smtp_connected": true }pytest tests/ -vExample output:
tests/test_email_service.py::test_build_message_plain PASSED
tests/test_email_service.py::test_build_message_html PASSED
tests/test_email_service.py::test_send_raises_on_bad_config PASSED
tests/test_csv_parser.py::test_parse_valid_csv PASSED
tests/test_csv_parser.py::test_parse_missing_email_column PASSED
tests/test_csv_parser.py::test_parse_empty_file PASSED
6 passed in 0.42s
requirements.txt
fastapi==0.111.0
uvicorn[standard]==0.29.0
pydantic==2.7.0
pydantic-settings==2.2.1
python-multipart==0.0.9
python-dotenv==1.0.1
pytest==8.2.0
pytest-asyncio==0.23.6
httpx==0.27.0
Uses pydantic-settings to load and validate all environment variables at startup. Raises a clear error if required variables are missing.
Handles all SMTP logic:
- Opens a single connection per request (context manager)
- Supports
text/plainandtext/htmlcontent types - Bulk sends iterate recipients with per-email error capture β one failure won't abort the batch
- Validates CSV has required
emailcolumn - Strips whitespace from all fields
- Returns a typed list of
Recipientobjects
- Logs to both console and
logs/app.log - Log level controlled by
LOG_LEVELenv var - Format:
[timestamp] [LEVEL] module: message
- Never commit
.envto version control β it's included in.gitignore - Use App Passwords or OAuth2 tokens instead of raw SMTP passwords
- For production, consider rate-limiting the
/bulkendpoint - Validate and sanitize all CSV inputs before sending
docker build -t email-automation .
docker run -p 8000:8000 --env-file .env email-automation- Connect your GitHub repo
- Set environment variables in the platform dashboard
- Set start command:
uvicorn main:app --host 0.0.0.0 --port 8000
- Fork the repo
- Create a branch:
git checkout -b feat/your-feature - Commit:
git commit -m 'feat: describe your change' - Push:
git push origin feat/your-feature - Open a Pull Request
MIT Β© [Ashish Yadav]
Built with β‘ FastAPI Β· π§ SMTP Β· π Python