Learn how to send your first email with the Transactional Python SDK. Includes sync and async examples.
Beginner
5 minutes
email
getting-started
sdk
python
Prerequisites
Python 3.8+ installed
pip package manager
Transactional account with API key
Verified sending domain
CODE
Full Example
import os
from usetransactional import Transactional
client = Transactional(api_key=os.environ['TRANSACTIONAL_API_KEY'])
def send_welcome_email(to_email: str, user_name: str) -> dict:
"""Send a welcome email to a new user."""
result = client.emails.send(
from_email='hello@yourdomain.com',
to=to_email,
subject=f'Welcome, {user_name}!',
html_body=f'''
<h1>Welcome to Our Platform!</h1>
<p>Hi {user_name},</p>
<p>Thanks for signing up. We're excited to have you on board.</p>
<a href="https://app.example.com/get-started">Get Started</a>
''',
text_body=f'''
Welcome to Our Platform!
Hi {user_name},
Thanks for signing up. We're excited to have you on board.
Get started: https://app.example.com/get-started
''',
)
if result.error:
print(f'Error: {result.error.code} - {result.error.message}')
return {'success': False, 'error': result.error.message}
print(f'Email sent! ID: {result.data.id}')
return {'success': True, 'id': result.data.id}
if __name__ == '__main__':
response = send_welcome_email(
to_email='user@example.com',
user_name='Alice'
)
print(response)
Understanding the Code
Client Initialization
from usetransactional import Transactionalclient = Transactional(api_key=os.environ['TRANSACTIONAL_API_KEY'])
Always use environment variables for your API key. Never hardcode credentials in your source code.
Send Options
The send method accepts these parameters:
Parameter
Type
Required
Description
from_email
str
Yes
Sender email address
to
str | list[str]
Yes
Recipient(s)
subject
str
Yes
Email subject line
html_body
str
No
HTML content
text_body
str
No
Plain text content
cc
str | list[str]
No
CC recipients
bcc
str | list[str]
No
BCC recipients
reply_to
str
No
Reply-to address
attachments
list[dict]
No
File attachments
metadata
dict
No
Custom metadata for tracking
tags
list[str]
No
Tags for categorization
Result Pattern
The SDK uses a result pattern instead of exceptions:
result = client.emails.send(...)if result.error: # Handle the error print(f'{result.error.code}: {result.error.message}')else: # Use the data print(f'Sent: {result.data.id}') print(f'Status: {result.data.status}')
Error Handling
Common Error Codes
Code
Description
How to Handle
INVALID_API_KEY
API key is invalid or missing
Check your API key
INVALID_EMAIL
Email address format is invalid
Validate email before sending
DOMAIN_NOT_VERIFIED
Sending domain not verified
Verify domain in dashboard
RATE_LIMITED
Too many requests
Implement exponential backoff
QUOTA_EXCEEDED
Monthly quota exceeded
Upgrade plan or wait for reset
Robust Error Handling Example
def send_email_safely(to: str, subject: str, html_body: str) -> dict: """Send email with comprehensive error handling.""" try: result = client.emails.send( from_email='hello@yourdomain.com', to=to, subject=subject, html_body=html_body, ) if result.error: match result.error.code: case 'INVALID_EMAIL': return {'success': False, 'error': 'Invalid email address', 'retry': False} case 'RATE_LIMITED': return {'success': False, 'error': 'Rate limited', 'retry': True} case 'QUOTA_EXCEEDED': return {'success': False, 'error': 'Quota exceeded', 'retry': False} case _: return {'success': False, 'error': result.error.message, 'retry': True} return {'success': True, 'id': result.data.id} except Exception as e: # Network errors, timeouts, etc. return {'success': False, 'error': str(e), 'retry': True}
Best Practices
1. Always Include Plain Text
Some users prefer plain text emails, and having a text version improves deliverability:
result = client.emails.send( from_email='hello@yourdomain.com', to=to_email, subject=subject, html_body='<h1>Hello World</h1>', text_body='Hello World', # Always include this)
2. Use Metadata for Tracking
Add metadata to correlate emails with your application data: