Skip to main content

Official Python SDK for the Lettr Email API

Project description

Lettr for Python SDK

The official Python SDK for the Lettr Email API. Send transactional emails with tracking, attachments, templates, and personalization.

Installation

pip install lettr

Quick Start

import lettr

client = lettr.Lettr("lttr_your_api_key")

client.emails.send(
    from_email="you@example.com",
    to=["user@example.com"],
    subject="Hello from Lettr!",
    html="<h1>Welcome!</h1>",
)

Usage

Health & Authentication

# Check API health (no authentication required)
health = client.health()
print(health.status)  # "ok"

# Validate your API key
auth = client.auth_check()
print(f"Team: {auth.team_name} (ID: {auth.team_id})")

Sending Emails

import lettr

client = lettr.Lettr("lttr_your_api_key")

# Simple email
response = client.emails.send(
    from_email="sender@example.com",
    to=["recipient@example.com"],
    subject="Hello!",
    html="<h1>Hello</h1><p>Welcome to our service!</p>",
    text="Hello\n\nWelcome to our service!",
)

print(response.request_id)  # "12345678901234567890"
print(response.accepted)    # 1

With Options

response = client.emails.send(
    from_email="sender@example.com",
    from_name="Acme Inc",
    to=["recipient@example.com"],
    cc=["manager@example.com"],
    bcc=["archive@example.com"],
    reply_to="support@example.com",
    subject="Your Order Confirmation",
    html="<h1>Order Confirmed</h1>",
    text="Order Confirmed",
    tag="order-confirmation",
    headers={"X-Order-Id": "12345"},
    options=lettr.EmailOptions(
        click_tracking=True,
        open_tracking=True,
        transactional=True,
    ),
    metadata={"order_id": "12345"},
)

With Attachments

import base64

with open("invoice.pdf", "rb") as f:
    pdf_data = base64.b64encode(f.read()).decode()

response = client.emails.send(
    from_email="billing@example.com",
    to=["customer@example.com"],
    subject="Your Invoice",
    html="<p>Please find your invoice attached.</p>",
    attachments=[
        lettr.Attachment(
            name="invoice.pdf",
            type="application/pdf",
            data=pdf_data,
        )
    ],
)

Using Templates

response = client.emails.send(
    from_email="hello@example.com",
    to=["user@example.com"],
    template_slug="welcome-email",
    substitution_data={
        "first_name": "John",
        "company": "Acme Inc",
    },
)

Scheduling Emails

# Schedule an email for future delivery
response = client.emails.schedule(
    from_email="sender@example.com",
    to=["recipient@example.com"],
    subject="Scheduled Message",
    html="<p>This was scheduled!</p>",
    scheduled_at="2025-12-01T10:00:00Z",
)
print(f"Scheduled: {response.request_id}")

# Get scheduled email details
detail = client.emails.get_scheduled(response.request_id)
print(f"{detail.subject} ({detail.state}) -> {detail.recipients}")

# Cancel a scheduled email
client.emails.cancel_scheduled("transmission_id")

Listing & Retrieving Emails

# List sent emails
email_list = client.emails.list(per_page=10)
for email in email_list.results:
    print(f"{email.subject} -> {email.rcpt_to}")

# Paginate with cursor
next_page = client.emails.list(cursor=email_list.next_cursor)

# Filter by recipient
filtered = client.emails.list(recipients="user@example.com")

# Filter by date range
filtered = client.emails.list(from_date="2026-01-01", to_date="2026-01-31")

# Get email details (all events for a transmission)
detail = client.emails.get("request_id_here")
print(f"{detail.subject} -> {detail.recipients} ({detail.state})")
for event in detail.events:
    print(f"{event.type}: {event.timestamp}")

Email Events

# List email events with filtering
events = client.emails.list_events(
    events=["delivery", "bounce"],
    recipients=["user@example.com"],
    from_date="2025-01-01",
    to_date="2025-12-31",
    per_page=50,
)

for event in events.results:
    print(f"{event.type}: {event.rcpt_to} at {event.timestamp}")
    if event.geo_ip:
        print(f"  Location: {event.geo_ip.city}, {event.geo_ip.country}")
    if event.user_agent_parsed:
        print(f"  Client: {event.user_agent_parsed.agent_family}")

Domains

# List all domains
domains = client.domains.list()
for domain in domains:
    print(f"{domain.domain} - {domain.status_label}")

# Add a new sending domain
domain = client.domains.create("example.com")
print(domain.dkim)  # DKIM config for DNS setup

# Get domain details (includes DMARC, SPF, DNS provider info)
domain = client.domains.get("example.com")
print(f"DMARC: {domain.dmarc_status}, SPF: {domain.spf_status}")

# Verify DNS records
verification = client.domains.verify("example.com")
print(f"DKIM: {verification.dkim_status}")
print(f"CNAME: {verification.cname_status}")
print(f"DMARC: {verification.dmarc_status}")
print(f"SPF: {verification.spf_status}")

# Delete a domain
client.domains.delete("example.com")

Templates

# List templates
template_list = client.templates.list(per_page=10)
for template in template_list.templates:
    print(f"{template.name} ({template.slug})")

# Create a template
template = client.templates.create(
    name="Welcome Email",
    html="<h1>Hello {{NAME}}!</h1><p>Welcome aboard.</p>",
)

# Get a template
template = client.templates.get("welcome-email")
print(template.html)

# Get template HTML
html = client.templates.get_html(project_id=1, slug="welcome-email")

# Update a template (creates a new version)
template = client.templates.update(
    "welcome-email",
    html="<h1>Hi {{NAME}}!</h1><p>Updated content.</p>",
)

# Get merge tags
merge_tags = client.templates.get_merge_tags("welcome-email")
for tag in merge_tags.merge_tags:
    print(f"{tag.key} (required: {tag.required})")

# Delete a template
client.templates.delete("welcome-email")

Webhooks

# List webhooks
webhooks = client.webhooks.list()
for webhook in webhooks:
    print(f"{webhook.name}: {webhook.url} (enabled: {webhook.enabled})")

# Create a webhook
webhook = client.webhooks.create(
    name="My Webhook",
    url="https://example.com/webhook",
    auth_type="none",
    events_mode="selected",
    events=["message.delivery", "message.bounce", "message.spam_complaint"],
)

# Create with authentication
webhook = client.webhooks.create(
    name="Secure Webhook",
    url="https://example.com/webhook",
    auth_type="basic",
    events_mode="all",
    auth_username="user",
    auth_password="secret",
)

# Update a webhook
webhook = client.webhooks.update(
    "webhook-abc123",
    name="Renamed Webhook",
    active=False,
)

# Get webhook details
webhook = client.webhooks.get("webhook-abc123")

# Delete a webhook
client.webhooks.delete("webhook-abc123")

Projects

# List projects
project_list = client.projects.list()
for project in project_list.projects:
    print(f"{project.emoji} {project.name}")

Campaigns

Campaigns are authored in the Lettr app; the SDK lists them, reads them, inspects their engagement events, and triggers delivery.

# List campaigns (optionally filter by status)
page = client.campaigns.list(status="sent", per_page=50)
for campaign in page.campaigns:
    print(f"{campaign.name}: {campaign.status}{campaign.stats.unique_opens} opens")

# Get a single campaign, including rendered HTML
campaign = client.campaigns.get(page.campaigns[0].id)
print(campaign.html_content)

# List engagement events (cursor-based pagination)
events = client.campaigns.list_events(campaign.id, event_type="open")
for event in events.events:
    print(f"{event.timestamp} {event.event_type} {event.email}")
if events.next_cursor:
    more = client.campaigns.list_events(campaign.id, cursor=events.next_cursor)

# Send now
client.campaigns.send(campaign.id)

# Schedule for later (ISO 8601, must be in the future)
client.campaigns.schedule(campaign.id, scheduled_at="2026-06-01T09:00:00+00:00")

# Cancel a scheduled send
client.campaigns.unschedule(campaign.id)

Error Handling

The SDK raises typed exceptions for all API errors:

import lettr

client = lettr.Lettr("lttr_your_api_key")

try:
    client.emails.send(
        from_email="sender@example.com",
        to=["recipient@example.com"],
        subject="Hello",
        html="<p>Hello!</p>",
    )
except lettr.ValidationError as e:
    print(f"Validation failed: {e.message}")
    print(f"Field errors: {e.errors}")
except lettr.AuthenticationError:
    print("Invalid API key")
except lettr.ForbiddenError:
    print("Access forbidden")
except lettr.NotFoundError as e:
    print(f"Not found: {e.message}")
except lettr.RateLimitError as e:
    print(f"Rate limited: {e.message} (code: {e.error_code})")
except lettr.BadRequestError as e:
    print(f"Bad request: {e.message} (code: {e.error_code})")
except lettr.ServerError as e:
    print(f"Server error: {e.message}")
except lettr.LettrError as e:
    print(f"Unexpected error: {e.message}")

Exception Hierarchy

Exception HTTP Status Description
LettrError -- Base exception for all errors
AuthenticationError 401 Missing or invalid API key
ForbiddenError 403 Access forbidden
ValidationError 422 Request validation failed
NotFoundError 404 Resource not found
ConflictError 409 Resource already exists
BadRequestError 400 Invalid domain or request
RateLimitError 429 Quota or rate limit exceeded
ServerError 500, 502 Server-side error

Context Manager

Use the client as a context manager to automatically close connections:

with lettr.Lettr("lttr_your_api_key") as client:
    client.emails.send(
        from_email="sender@example.com",
        to=["recipient@example.com"],
        subject="Hello!",
        html="<p>Hello!</p>",
    )
# Connection pool is automatically closed

Requirements

  • Python 3.8+
  • httpx (installed automatically)

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lettr-1.3.0.tar.gz (41.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lettr-1.3.0-py3-none-any.whl (31.5 kB view details)

Uploaded Python 3

File details

Details for the file lettr-1.3.0.tar.gz.

File metadata

  • Download URL: lettr-1.3.0.tar.gz
  • Upload date:
  • Size: 41.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lettr-1.3.0.tar.gz
Algorithm Hash digest
SHA256 73e5bbec422bdcad5279f24a4e6a9fed1e343171a93820c4e867397d30f9bb1c
MD5 958156acc7217ba65eabfb58966ed5f4
BLAKE2b-256 c8a27291c3a16722089a2d3bd8b7e0efc662ecee31e22771fd4815ba749c4d42

See more details on using hashes here.

Provenance

The following attestation bundles were made for lettr-1.3.0.tar.gz:

Publisher: publish.yml on lettr-com/lettr-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lettr-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: lettr-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 31.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lettr-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f4858317a8b1a81bd8cae3069e151635f2b52e751cb8e3f9ed8df9ebb0bcd1e8
MD5 cae91218ff741ed6a2ea08642c4f1822
BLAKE2b-256 c36a1f3464e4e6d1818001b1426631946487d8ce8a2a39b25bbbe80d7fb7dab5

See more details on using hashes here.

Provenance

The following attestation bundles were made for lettr-1.3.0-py3-none-any.whl:

Publisher: publish.yml on lettr-com/lettr-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page