sendgrid-email-service
A modern, production-ready Python SDK for sending transactional and marketing emails via SMTP (including SendGrid SMTP relays) with built-in Jinja2 templating, structured logging, secure credential handling, and comprehensive exception management.
Features
- 🚀 Production-Grade Delivery: Reliable SMTP transport supporting TLS encryption, authentication, and timeouts.
- 🎨 Built-in & Custom Jinja2 Templates: Comes with responsive pre-built templates (
welcome,reset_password,verify_account) and allows custom template directories. - 🪵 Enterprise Logging: PEP 282 compliant logging with
NullHandlerby default, granular log levels (DEBUG,INFO,ERROR), and automatic password masking. - 🛡️ Comprehensive Exception Hierarchy: Clear, actionable exceptions for validation, templates, authentication, connection timeouts, and delivery errors.
- ⚙️ Flexible Configuration: Seamlessly configure via
.envfiles, environment variables, or typedEmailConfigobjects. - 📦 PEP 561 Compliant: Fully typed (
py.typed) for autocomplete and static analysis with MyPy and IDEs.
Installation
Install via pip:
pip install sendgrid-email-service
Quick Start
1. Environment Configuration
Create a .env file in your project root or set environment variables:
# SMTP Configuration (SendGrid, Mailpit, Amazon SES, or custom SMTP)
EMAIL_SMTP_HOST=smtp.sendgrid.net
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USERNAME=apikey
EMAIL_SMTP_PASSWORD=your-sendgrid-api-key
# Sender Defaults
EMAIL_FROM=notifications@yourdomain.com
EMAIL_FROM_NAME=Your Company
# Optional Settings
EMAIL_USE_TLS=true
EMAIL_TIMEOUT=30
2. Send Plain Text or HTML Email
from sendgrid_email_service import EmailClient
# Automatically loads configuration from .env / environment variables
client = EmailClient()
# Send a simple email
client.send(
to="recipient@example.com",
subject="Welcome to Our Platform",
body="Hello! Thank you for joining us.",
)
Working with Templates
sendgrid-email-service includes responsive, production-ready HTML templates out of the box.
Built-in Templates
| Template Name | Description | Available Context Variables |
|---|---|---|
welcome |
Onboarding & welcome message | name, company_name, action_url, year |
reset_password |
Password recovery with secure action button | name, reset_url, company_name, expiry_hours, support_email, year |
verify_account |
Email verification & activation link | name, verification_url, company_name, expiry_hours, support_email, year |
Example: Sending a Template Email
from sendgrid_email_service import EmailClient
client = EmailClient()
client.send(
to="user@example.com",
subject="Welcome to FireCompass!",
template="welcome",
data={
"name": "Jane Doe",
"company_name": "FireCompass",
"action_url": "https://app.firecompass.com/dashboard",
"year": 2026,
},
)
Example: Using Custom Templates
You can point EmailClient to your own Jinja2 template directory:
from pathlib import Path
from sendgrid_email_service import EmailClient
client = EmailClient(template_directory=Path("./my_custom_templates"))
client.send(
to="customer@example.com",
subject="Your Invoice is Ready",
template="monthly_invoice", # Looks for monthly_invoice.html in ./my_custom_templates
data={
"customer_name": "Acme Corp",
"invoice_number": "INV-2026-001",
"amount_due": "$149.00",
},
)
Programmatic Configuration
Instead of environment variables, you can configure the client directly using EmailConfig:
from sendgrid_email_service import EmailClient, EmailConfig
config = EmailConfig(
smtp_host="smtp.sendgrid.net",
smtp_port=587,
smtp_username="apikey",
smtp_password="your-api-key-here",
from_email="no-reply@company.com",
from_name="My Company",
use_tls=True,
timeout=30,
)
client = EmailClient(config=config)
Exception Handling
The SDK provides a clean exception hierarchy inheriting from EmailError:
EmailError (Base)
├── EmailConfigurationError
├── EmailValidationError
├── EmailTemplateError
└── EmailSendError
├── EmailConnectionError
├── EmailAuthenticationError
├── EmailTimeoutError
└── EmailRecipientsRefusedError
Example: Robust Error Handling
import logging
from sendgrid_email_service import (
EmailClient,
EmailAuthenticationError,
EmailConnectionError,
EmailRecipientsRefusedError,
EmailTimeoutError,
EmailValidationError,
EmailTemplateError,
EmailSendError,
EmailError,
)
client = EmailClient()
try:
client.send(
to="client@example.com",
subject="Important Update",
template="welcome",
data={"name": "Alice", "company_name": "Acme Inc."},
)
except EmailValidationError as e:
print(f"Invalid email input on field '{e.field}': {e}")
except EmailTemplateError as e:
print(f"Template error ({e.template_name}): {e}")
except EmailAuthenticationError as e:
print(f"SMTP Auth failure for user '{e.username}': {e}")
except EmailConnectionError as e:
print(f"Could not connect to SMTP server ({e.host}:{e.port}): {e}")
except EmailTimeoutError as e:
print(f"Operation timed out after {e.timeout}s: {e}")
except EmailRecipientsRefusedError as e:
print(f"Server refused recipients {e.recipients}: {e}")
except EmailSendError as e:
print(f"Failed to send email: {e}")
except EmailError as e:
print(f"General email SDK error: {e}")
Logging Configuration
sendgrid-email-service adheres to Python library best practices (PEP 282). By default, it emits no logs unless your application configures logging.
Example: Enabling Logs in Your Application
import logging
import sys
# Configure root or package logger
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[logging.StreamHandler(sys.stdout)],
)
# Enable DEBUG logging specifically for sendgrid_email_service
logging.getLogger("sendgrid_email_service").setLevel(logging.DEBUG)
Security Guarantee: Passwords and sensitive credentials are automatically masked (e.g.
***) and are never written to log files.
Local Development with Mailpit
You can test email sending locally using Mailpit included via Docker Compose:
- Start Mailpit:
docker compose up -d
- Set
.envto point to localhost:EMAIL_SMTP_HOST=localhost EMAIL_SMTP_PORT=1025 EMAIL_USE_TLS=false
- View sent emails in your browser at
http://localhost:8025.
Running Tests
Install dev dependencies and run pytest:
pip install -e ".[dev]"
pytest tests/ -v
Publishing to PyPI
-
Build distribution archives:
python -m build
-
Verify archives with Twine:
twine check dist/*
-
Upload to TestPyPI (Optional):
twine upload --repository testpypi dist/*
-
Upload to PyPI:
twine upload dist/*
License
Distributed under the MIT License.
sendgrid-stmp-email-service
Release files for sendgrid-email-service 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sendgrid_email_service-0.0.1.tar.gz | 22.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sendgrid_email_service-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.8 kB
Release files / sendgrid_email_service-0.0.1.tar.gz
| Download URL | sendgrid_email_service-0.0.1.tar.gz |
|---|---|
| Size | 22.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6b073976904e178a21cf56bb4886b2e2df356d96635d3b95f1d48338641298c9
|
|
BLAKE2b-256 checksum How to use checksums |
4f17eea3543bb56252b726af9f61c7d7b8d6853ec7db4ef65f547a3e88ae08f4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / sendgrid_email_service-0.0.1-py3-none-any.whl
| Download URL | sendgrid_email_service-0.0.1-py3-none-any.whl |
|---|---|
| Size | 19.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
712beb576c155025af3b9025b2d1057c5f2ed9ba4bf5d7870d582f83d0bafb28
|
|
BLAKE2b-256 checksum How to use checksums |
6ffcf9a9f5cbfa884258d013c2dcd7a9d9a5645acd5f730414273ca76c1c3980
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|