Sendry Docs
SDKs

Python SDK

Official Python SDK for the Sendry email API.

Installation

pip install sendry
# or with poetry
poetry add sendry
# or with uv
uv add sendry

Requires Python 3.9+.

Initialization

from sendry import Sendry

# Simple initialization
client = Sendry(api_key="sn_live_your_api_key")

# Or from environment variable
import os
client = Sendry(api_key=os.environ["SENDRY_API_KEY"])

# With configuration
client = Sendry(
    api_key=os.environ["SENDRY_API_KEY"],
    base_url="https://api.sendry.online",
    timeout=30,       # seconds
    max_retries=2,
)

Emails

# Send a single email
email = client.emails.send(
    from_="Acme <hello@acme.com>",
    to="user@example.com",
    subject="Welcome!",
    html="<h1>Welcome to Acme!</h1>",
)
print(email.id)      # em_abc123
print(email.status)  # queued

# Send to multiple recipients
client.emails.send(
    from_="hello@acme.com",
    to=["alice@example.com", "bob@example.com"],
    subject="Team announcement",
    html="<p>Big news...</p>",
)

# Send with attachment
import base64

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

client.emails.send(
    from_="billing@acme.com",
    to="user@example.com",
    subject="Your invoice",
    html="<p>Please find your invoice attached.</p>",
    attachments=[
        {
            "filename": "invoice.pdf",
            "content": content,
            "content_type": "application/pdf",
        }
    ],
)

# Send using a template
client.emails.send(
    from_="hello@acme.com",
    to="user@example.com",
    template_id="tmpl_abc123",
    variables={"name": "Alice", "plan": "Pro"},
)

# Batch send (up to 100 at once)
result = client.emails.send_batch(
    from_="hello@acme.com",
    subject="Order update",
    emails=[
        {"to": "alice@example.com", "html": "<p>Alice, your order shipped!</p>"},
        {"to": "bob@example.com", "html": "<p>Bob, your order shipped!</p>"},
    ],
)
for item in result.data:
    print(item.id, item.status)

# Get email status
email = client.emails.get("em_abc123")
print(email.status)  # delivered

# List emails
page = client.emails.list(limit=25, status="delivered")
for email in page.data:
    print(email.id, email.subject)

# Paginate
while page.has_more:
    page = client.emails.list(cursor=page.next_cursor)
    for email in page.data:
        print(email.id)

# Cancel a scheduled email
client.emails.cancel("em_abc123")

Async support

import asyncio
import os
from sendry import AsyncSendry

async def main():
    client = AsyncSendry(api_key=os.environ["SENDRY_API_KEY"])

    email = await client.emails.send(
        from_="hello@acme.com",
        to="user@example.com",
        subject="Hello!",
        html="<p>Async email!</p>",
    )
    print(email["id"])

asyncio.run(main())

Domains

# Add a domain
domain = client.domains.create(name="acme.com")
for record in domain.dns_records:
    print(f"{record.type} {record.host}{record.value}")

# Verify after DNS propagation
result = client.domains.verify("dom_abc123")
print(result.status)  # verified

client.domains.list()
client.domains.get("dom_abc123")
client.domains.remove("dom_abc123")

Segments

seg = client.segments.create({"name": "High-Value Users"})
client.segments.add_contacts(seg["id"], {"contact_ids": ["ct_1", "ct_2"]})
client.segments.list_contacts(seg["id"])

Topics

topic = client.topics.create({"name": "product-updates", "opt_in_required": True})
client.topics.subscribe(topic["id"], "user@example.com")

Contact properties

client.contact_properties.create({"name": "plan", "label": "Plan", "type": "string"})
client.contact_properties.set_values("ct_123", {"plan": "pro"})

Deliverability insights

insights = client.deliverability_insights.for_email("em_abc123")
# insights["score"] — 0-100; insights["checks"] — per-rule feedback
link = client.email_sharing.create("em_abc123")
print(link["url"], link["expires_at"])

Attachment download

attach = client.attachments.download("em_abc123", 0)
with open(attach.filename or "attachment.bin", "wb") as f:
    f.write(attach.content)

Per-domain tracking toggles

client.tracking.set_for_domain("dom_abc123", {"opens": True, "clicks": False})

Exports (CSV)

job = client.exports.create({"resource": "contacts"})
# Poll client.exports.get(job["id"]) until status == "ready"
csv_bytes = client.exports.download(job["id"])

Postmaster (Gmail Postmaster Tools)

metrics = client.postmaster.metrics({"domain_id": "dom_abc"})
client.postmaster.sync()

Dead-letter queue (admin)

failed = client.dlq.list({"queue": "email-sender"})
client.dlq.retry(failed["data"][0]["id"])

Error handling

from sendry import (
    ApiError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
)
import time

try:
    client.emails.send(
        from_="hello@acme.com",
        to="user@example.com",
        subject="Hello",
        html="<p>Hi</p>",
    )
except AuthenticationError:
    print("Invalid API key — check SENDRY_API_KEY")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
    time.sleep(e.retry_after)
except ValidationError as e:
    print("Validation failed:", e.details)
except ApiError as e:
    print(f"API error {e.status_code}: {e.code}{e.message}")

Context manager

with Sendry(api_key=os.environ["SENDRY_API_KEY"]) as client:
    email = client.emails.send(
        from_="hello@acme.com",
        to="user@example.com",
        subject="Hello",
        html="<p>Hi</p>",
    )
# Connection pool closed on exit

Source

github.com/sendry-dev/sendry-python — MIT licensed. Published as sendry on PyPI.

On this page