Transactional

Send Email with Python

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 transactional import Transactional

# Initialize the client
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=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=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}


# Example usage
if __name__ == '__main__':
    response = send_welcome_email(
        to_email='user@example.com',
        user_name='Alice'
    )
    print(response)

Understanding the Code

Client Initialization

from transactional import Transactional
 
client = 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:

ParameterTypeRequiredDescription
from_emailstrYesSender email address
tostr | list[str]YesRecipient(s)
subjectstrYesEmail subject line
htmlstrNoHTML content
textstrNoPlain text content
ccstr | list[str]NoCC recipients
bccstr | list[str]NoBCC recipients
reply_tostrNoReply-to address
attachmentslist[dict]NoFile attachments
metadatadictNoCustom metadata for tracking
tagslist[str]NoTags 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

CodeDescriptionHow to Handle
INVALID_API_KEYAPI key is invalid or missingCheck your API key
INVALID_EMAILEmail address format is invalidValidate email before sending
DOMAIN_NOT_VERIFIEDSending domain not verifiedVerify domain in dashboard
RATE_LIMITEDToo many requestsImplement exponential backoff
QUOTA_EXCEEDEDMonthly quota exceededUpgrade plan or wait for reset

Robust Error Handling Example

def send_email_safely(to: str, subject: str, html: str) -> dict:
    """Send email with comprehensive error handling."""
 
    try:
        result = client.emails.send(
            from_email='hello@yourdomain.com',
            to=to,
            subject=subject,
            html=html,
        )
 
        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='<h1>Hello World</h1>',
    text='Hello World',  # Always include this
)

2. Use Metadata for Tracking

Add metadata to correlate emails with your application data:

result = client.emails.send(
    from_email='orders@yourdomain.com',
    to=customer.email,
    subject='Order Shipped',
    html=html_content,
    metadata={
        'order_id': order.id,
        'customer_id': customer.id,
        'email_type': 'shipping_notification',
    },
)

3. Validate Before Sending

Catch invalid emails before they waste API calls:

from transactional import Transactional
 
client = Transactional(api_key=os.environ['TRANSACTIONAL_API_KEY'])
 
# Validate first
validation = client.emails.validate(email=user_input)
if not validation.data.valid:
    print(f'Invalid email: {validation.data.reason}')
    return
 
# Then send
result = client.emails.send(...)

Ready to Build?

Sign up for free and start sending emails today.