Python SDK

Official Python SDK for Posthawk. Built on httpx with typed dataclass responses and a simple result pattern. Works with Python 3.8+.

SDKpip install posthawk

Install the SDK from PyPI. The only runtime dependency is httpx for HTTP communication.

Example

bash
pip install posthawk
SDKPosthawk(api_key)Posthawk

Initialize the client and send your first email. Pass your API key as the first argument. For self-hosted instances, pass base_url as a keyword argument.

Example

python
from posthawk import Posthawk

# Cloud (default base URL: https://api.posthawk.dev)
client = Posthawk("ck_live_...")

# Self-hosted
client = Posthawk("ck_live_...", base_url="https://api.yourdomain.com")

result = client.emails.send(
    from_email="hello@yourdomain.com",
    to="user@example.com",
    subject="Welcome!",
    html="<h1>Hello!</h1>",
)

if result.error:
    print(result.error.message)
else:
    print("Sent!", result.data.job_id)
SDKclient.emails.send(**params)PosthawkResponse

Send an email immediately, or schedule it for later by including scheduled_for. Returns PosthawkResponse[SendEmailResponse]. At least one of html, text, or template_id is required. Note: use from_email instead of from (which is a Python reserved word).

ParameterTypeDescription
from_emailrequiredstrSender email address (maps to "from" in the API)
torequiredstr | list[str]Recipient email address(es)
ccstr | list[str]CC recipient(s)
bccstr | list[str]BCC recipient(s)
subjectrequiredstrEmail subject line
htmlstrHTML email body
textstrPlain text email body
template_idstrPosthawk template ID
variablesdict[str, str]Template variable substitution
headersdict[str, str]Custom email headers (e.g. List-Unsubscribe)
scheduled_forstr | datetimeSchedule for later (ISO 8601 string or datetime object)
timezonestrIANA timezone for scheduled time
metadatadict[str, Any]Custom metadata attached to the email
tagsdict[str, Any]Custom tags for filtering and search
reply_tostrReply-to email address

Example

python
result = client.emails.send(
    from_email="hello@yourdomain.com",
    to=["user@example.com", "other@example.com"],
    subject="Welcome to Acme",
    html="<h1>Welcome!</h1><p>Thanks for signing up.</p>",
    metadata={"user_id": "usr_123"},
    tags={"campaign": "onboarding"},
)

# Schedule for later
from datetime import datetime, timedelta, timezone

result = client.emails.send(
    from_email="hello@yourdomain.com",
    to="user@example.com",
    subject="Reminder",
    text="Don't forget your meeting!",
    scheduled_for=datetime.now(timezone.utc) + timedelta(hours=24),
)
SDKclient.emails.get(job_id)PosthawkResponse

Get the delivery status of a previously queued email by its job ID.

ParameterTypeDescription
job_idrequiredstrJob ID returned from the send method

Example

python
result = client.emails.get("abc-123-def")

if result.data:
    print(result.data.status)     # pending | processing | completed | failed
    print(result.data.result)     # EmailJobResult(message_id=..., email_log_id=...)
SDKclient.scheduled.list(**params)PosthawkResponse

List scheduled emails with optional filtering by status and pagination.

ParameterTypeDescription
statusstrFilter by status: scheduled, sent, cancelled, failed
limitintPagination limit
offsetintPagination offset

Example

python
result = client.scheduled.list(
    status="scheduled",
    limit=10,
)

# result.data.data -> list[ScheduledEmail]
# result.data.total -> int
SDKclient.scheduled.get(id)PosthawkResponse

Get the details of a specific scheduled email by its ID.

ParameterTypeDescription
idrequiredstrScheduled email UUID

Example

python
result = client.scheduled.get("scheduled-uuid")

if result.data:
    print(result.data.data.scheduled_for)  # ISO 8601 datetime
    print(result.data.data.status)         # scheduled | sent | cancelled
SDKclient.scheduled.cancel(id)PosthawkResponse

Cancel a scheduled email before it sends. Only works for emails that have not yet been processed.

ParameterTypeDescription
idrequiredstrScheduled email UUID

Example

python
result = client.scheduled.cancel("scheduled-uuid")
# result.data.message -> "Scheduled email cancelled successfully"
SDKclient.scheduled.reschedule(id, scheduled_for=...)PosthawkResponse

Change the send time of a scheduled email. Accepts a datetime object or ISO 8601 string.

ParameterTypeDescription
idrequiredstrScheduled email UUID
scheduled_forrequiredstr | datetimeNew send time (ISO 8601 or datetime object)

Example

python
client.scheduled.reschedule(
    "scheduled-uuid",
    scheduled_for="2026-04-01T10:00:00Z",
)
SDKresult.error / result.data

SDK methods never raise exceptions for API errors. Every call returns a PosthawkResponse with .data and .error attributes. Check .error first, then access .data safely. The only case where the SDK raises is a missing API key in the constructor.

Example

python
result = client.emails.send(
    from_email="hello@yourdomain.com",
    to="user@example.com",
    subject="Test",
    html="<p>Hello</p>",
)

if result.error:
    print(f"Error {result.error.status_code}: {result.error.message}")
    return

# result.data is guaranteed non-None here
print(result.data.job_id)
SDKclient.domains.*PosthawkResponse

Manage sending domains programmatically — add, verify, enable inbound receiving, configure webhooks, and set sending limits. Ideal for SaaS platforms that let their users add custom sending domains.

Example

python
# Add a domain
result = client.domains.create(
    domain="mail.customer.com",
    region="us-east-1",
)
# result.data.dns_records → DNS records to configure

# List all domains
result = client.domains.list()

# Trigger verification check
result = client.domains.verify("domain-uuid")
# result.data.sending_enabled → True when verified

# Enable inbound receiving
client.domains.enable_receiving("domain-uuid")

# Set a webhook for inbound emails
client.domains.update_webhook(
    "domain-uuid",
    webhook_url="https://yourapp.com/api/incoming-email",
)

# Test the webhook
client.domains.test_webhook("domain-uuid")

# Set per-domain sending limits
client.domains.update_limits(
    "domain-uuid",
    max_daily_emails=1000,
    max_monthly_emails=25000,
)

# Remove limits (None = unlimited)
client.domains.update_limits(
    "domain-uuid",
    max_daily_emails=None,
    max_monthly_emails=None,
)

# Delete a domain
client.domains.delete("domain-uuid")
SDKwith Posthawk(...) as client:

Use a context manager to automatically close the underlying httpx connection pool when done. This ensures clean resource cleanup.

Example

python
from posthawk import Posthawk

with Posthawk("ck_live_...") as client:
    result = client.emails.send(
        from_email="hello@yourdomain.com",
        to="user@example.com",
        subject="Hello",
        html="<h1>Hi!</h1>",
    )
    print(result.data.job_id)

# Connection pool is automatically closed here