Quick Start
From install to sent email in under two minutes. This page covers the core patterns — basic sending, templates, attachments, bulk, and FastAPI integration.
Your First Email
Initialize MailBridge with your provider, then call send(). The examples below use SendGrid and SMTP — pick whichever you have credentials for:
from mailbridge import MailBridge
mailer = MailBridge(
provider='sendgrid',
api_key='SG.xxxxx',
from_email='noreply@yourdomain.com'
)
response = mailer.send(
to='user@example.com',
subject='Hello from MailBridge!',
body='It works!
Your first email via SendGrid.
'
)
print(f"Sent! Message ID: {response.message_id}")
from mailbridge import MailBridge
mailer = MailBridge(
provider='smtp',
host='smtp.gmail.com',
port=587,
username='you@gmail.com',
password='your-app-password', # Gmail App Password, not your login password
use_tls=True,
from_email='you@gmail.com'
)
response = mailer.send(
to='user@example.com',
subject='Hello from MailBridge!',
body='It works!
Your first email via Gmail SMTP.
'
)
print(f"Sent! Message ID: {response.message_id}")
Gmail requires an App Password — not your regular Google password. Enable 2FA on your account, then generate an App Password under Account Settings → Security.
from mailbridge import MailBridge
mailer = MailBridge(
provider='postmark',
server_token='xxxxx-xxxxx',
from_email='verified@yourdomain.com' # Must be verified in Postmark
)
response = mailer.send(
to='user@example.com',
subject='Hello from MailBridge!',
body='It works!
Your first email via Postmark.
'
)
print(f"Sent! Message ID: {response.message_id}")
from mailbridge import MailBridge
mailer = MailBridge(
provider='ses',
aws_access_key_id='AKIAXXXX',
aws_secret_access_key='xxxxx',
region_name='us-east-1',
from_email='verified@yourdomain.com' # Must be verified in SES
)
response = mailer.send(
to='user@example.com',
subject='Hello from MailBridge!',
body='It works!
Your first email via Amazon SES.
'
)
print(f"Sent! Message ID: {response.message_id}")
Response Object
Every send() call returns an EmailResponse — a normalized response regardless of which provider was used:
response = mailer.send(
to='user@example.com',
subject='Test',
body='Hello
'
)
print(response.message_id) # Provider-assigned message ID
print(response.success) # True / False
print(response.status_code) # HTTP status from the provider API
For send_bulk(), you get a BulkEmailResult:
result = mailer.send_bulk(messages)
print(result.total) # Total messages attempted
print(result.successful) # Successfully sent
print(result.failed) # Failed count
print(result.failures) # List of failures with details
Template Emails
All providers except SMTP support dynamic templates created in their dashboards. Pass template_id and template_data — subject and body come from the template:
# SendGrid template IDs start with 'd-'
response = mailer.send(
to='user@example.com',
subject='', # comes from the template
body='', # comes from the template
template_id='d-1234567890abcdef',
template_data={
'user_name': 'Alice Smith',
'activation_link': 'https://yourapp.com/activate/abc123',
'support_email': 'support@yourdomain.com'
}
)
print(f"Sent! Message ID: {response.message_id}")
# SES template IDs are names you define in the SES console
response = mailer.send(
to='user@example.com',
subject='',
body='',
template_id='WelcomeTemplate',
template_data={
'firstName': 'Alice',
'companyName': 'Acme Corp',
'activationUrl': 'https://yourapp.com/activate/abc123'
}
)
print(f"Sent! Message ID: {response.message_id}")
# Postmark template alias or numeric ID
response = mailer.send(
to='user@example.com',
subject='',
body='',
template_id='welcome-email',
template_data={
'product_name': 'My App',
'name': 'Alice Smith',
'action_url': 'https://yourapp.com/activate/abc123'
}
)
print(f"Sent! Message ID: {response.message_id}")
Common transactional email patterns using templates:
# Welcome email
mailer.send(
to=new_user.email,
subject='', body='',
template_id='welcome-email',
template_data={'name': new_user.name, 'activation_link': generate_link(new_user)}
)
# Password reset
mailer.send(
to=user.email,
subject='', body='',
template_id='password-reset',
template_data={'reset_link': generate_reset_link(user), 'expiry_hours': 24}
)
# Order confirmation
mailer.send(
to=order.customer_email,
subject='', body='',
template_id='order-confirmation',
template_data={
'order_number': order.id,
'total': order.total,
'items': order.items,
'tracking_url': order.tracking_url
}
)
Attachments
Pass a list of pathlib.Path objects to attachments — works on all providers:
from pathlib import Path
# Single attachment
response = mailer.send(
to='customer@example.com',
subject='Your Invoice',
body='Please find your invoice attached.
',
attachments=[Path('invoice.pdf')]
)
# Multiple attachments
response = mailer.send(
to='customer@example.com',
subject='Your Documents',
body='Please find your documents attached.
',
attachments=[
Path('invoice.pdf'),
Path('receipt.pdf'),
Path('terms.pdf')
]
)
CC, BCC, Reply-To & Headers
All standard email fields are supported with the same syntax across all providers:
# CC and BCC
response = mailer.send(
to='client@example.com',
subject='Project Update',
body='Here is the latest update...
',
cc=['manager@company.com', 'team@company.com'],
bcc=['archive@company.com']
)
# Multiple recipients
response = mailer.send(
to=['alice@example.com', 'bob@example.com', 'charlie@example.com'],
subject='Team Announcement',
body='Important update for everyone.
'
)
# Reply-To
response = mailer.send(
to='customer@example.com',
subject='Support Ticket Opened',
body='We received your inquiry. Reply to this email for updates.
',
reply_to='support@yourdomain.com'
)
# Custom headers and tags (for tracking/analytics)
response = mailer.send(
to='user@example.com',
subject='Summer Campaign',
body='Special offer just for you!
',
headers={'X-Campaign-ID': 'summer-2025', 'X-Priority': '1'},
tags=['marketing', 'summer-campaign']
)
Bulk Sending
Use send_bulk() to send to many recipients. Each provider uses its native batch API automatically — you don't need to manage batching yourself:
from mailbridge import MailBridge, EmailMessageDto
mailer = MailBridge(provider='sendgrid', api_key='SG.xxxxx', from_email='news@example.com')
# Build a message list — each recipient can have unique content
messages = [
EmailMessageDto(
to='alice@example.com',
subject='Your monthly report',
body='Hello Alice, here is your report.
'
),
EmailMessageDto(
to='bob@example.com',
subject='Your monthly report',
body='Hello Bob, here is your report.
'
),
]
result = mailer.send_bulk(messages)
print(f"Sent {result.successful}/{result.total}")
Bulk with personalized templates
# Personalized template for each subscriber
subscribers = [
{'email': 'alice@example.com', 'name': 'Alice', 'plan': 'Pro'},
{'email': 'bob@example.com', 'name': 'Bob', 'plan': 'Basic'},
# ... thousands more
]
messages = [
EmailMessageDto(
to=sub['email'],
subject='', body='',
template_id='d-newsletter-template',
template_data={'name': sub['name'], 'plan': sub['plan']}
)
for sub in subscribers
]
result = mailer.send_bulk(messages)
print(f"Sent: {result.successful}/{result.total}")
Bulk provider limits
| Provider | Batch size | Strategy |
|---|---|---|
| SendGrid | 1,000 / call | Native batch API |
| Amazon SES | 50 / call | Auto-batched by MailBridge |
| Postmark | 500 / call | Native batch API |
| Mailgun | Native | Native batch API |
| Brevo | Native | Native batch API |
| SMTP | — | Sends individually, reuses connection |
Context Manager
Use MailBridge as a context manager to ensure connections are properly closed — especially useful with SMTP where an open connection is maintained:
with MailBridge(
provider='smtp',
host='smtp.gmail.com',
port=587,
username='you@gmail.com',
password='your-app-password',
use_tls=True,
from_email='you@gmail.com'
) as mailer:
mailer.send(to='alice@example.com', subject='Hi Alice', body='Hello!
')
mailer.send(to='bob@example.com', subject='Hi Bob', body='Hello!
')
# Connection closed automatically here
FastAPI Integration
The recommended pattern for FastAPI is to initialize MailBridge once at app startup and inject it as a dependency:
# mail.py — configure once
from mailbridge import MailBridge
import os
mailer = MailBridge(
provider=os.getenv('MAIL_PROVIDER', 'smtp'),
api_key=os.getenv('MAIL_API_KEY'),
from_email=os.getenv('MAIL_FROM', 'noreply@yourdomain.com')
)
def get_mailer() -> MailBridge:
return mailer
# main.py — use in routes
from fastapi import FastAPI, Depends, BackgroundTasks
from mailbridge import MailBridge
from mail import get_mailer
app = FastAPI()
@app.post("/register")
async def register(data: UserCreate, mailer: MailBridge = Depends(get_mailer)):
user = create_user(data)
# Send welcome email
mailer.send(
to=user.email,
subject='', body='',
template_id='welcome-email',
template_data={'name': user.name}
)
return {"id": user.id}
@app.post("/register/bg")
async def register_with_background(
data: UserCreate,
background_tasks: BackgroundTasks,
mailer: MailBridge = Depends(get_mailer)
):
user = create_user(data)
# Send in background — don't block the response
background_tasks.add_task(
mailer.send,
to=user.email,
subject='', body='',
template_id='welcome-email',
template_data={'name': user.name}
)
return {"id": user.id} # Returns immediately
Use BackgroundTasks for welcome and notification emails — they shouldn't block the API response. Only block when confirmation of delivery is critical (e.g. password reset — user expects to see "email sent" before the page resolves).