Python SDK

The official shipmail package provides both synchronous and asynchronous clients. Requires Python 3.10+.

Installation

pip install shipmailuv add shipmail

Quick start

from shipmail import ShipMail client = ShipMail("sm_live_...") message = client.messages.send({ "from": "hello@yourdomain.com", "to": ["recipient@example.com"], "subject": "Hello from shipmail", "text": "It works.", }) print(message["id"]) # msg_...

Async client

Use AsyncShipMail for async/await workflows. Works as a context manager for automatic cleanup.

from shipmail import AsyncShipMail async with AsyncShipMail("sm_live_...") as client: message = await client.messages.send({ "from": "hello@yourdomain.com", "to": ["recipient@example.com"], "subject": "Hello", "text": "Hi there", })

Configuration

client = ShipMail( "sm_live_...", base_url="https://shipmail.to/api/v1", # default max_retries=2, # default timeout=30.0, # seconds, default )
OptionDefaultDescription
api_keyrequiredYour sm_live_... key.
base_urlhttps://shipmail.to/api/v1API base URL.
max_retries2Retry count for 429 and 5xx responses.
timeout30.0Request timeout in seconds.

Resources

The client exposes each API resource as a property:

PropertyMethods
client.domainscreate, list, get, update, delete, verify, search, register
client.mailboxescreate, list, get, update, delete, update_auto_reply
client.messagessend, get
client.threadslist, get, reply
client.webhookscreate, list, get, update, delete, rotate_secret, test, list_deliveries
client.suppressionslist, remove
client.statusget

Error handling

API errors raise typed subclasses of ShipMailError. Each error exposes status, type, request_id, and retryable.

from shipmail import ( ShipMailError, ValidationError, RateLimitError, APIConnectionError, ) try: client.messages.send({...}) except ValidationError as err: print(err.details) # field-level errors except RateLimitError as err: print(err.retry_after) # seconds to wait except ShipMailError as err: print(err.status) # HTTP status code print(err.request_id) # request ID for support
ClassStatus
AuthenticationError401
AuthorizationError403
ValidationError422
NotFoundError404
ConflictError409
RateLimitError429
InternalServerError500
APIConnectionErrorNetwork failure

Pagination

Manual cursor-based pagination:

page = client.domains.list({"limit": 10}) for domain in page["data"]: print(domain["name"]) if page["pagination"]["has_more"]: next_page = client.domains.list({ "cursor": page["pagination"]["next_cursor"], })

Auto-pagination iterates all pages automatically:

# Sync for domain in client.domains.list_auto_paginating(limit=25): print(domain["name"]) # Async async for domain in client.domains.list_auto_paginating(limit=25): print(domain["name"])

Webhook verification

The SDK exports a standalone verify_webhook function. No client instance needed.

from shipmail import verify_webhook, WebhookVerificationError try: event = verify_webhook(raw_body, headers, webhook_secret) print(event["event_type"]) # "message.received" except WebhookVerificationError: pass # invalid signature

Throws WebhookVerificationError if the signature is invalid or the timestamp is outside the 5-minute tolerance window. Pass tolerance_in_seconds=600 to customize.

Per-request options

Every method accepts an optional options argument for request-level overrides:

client.domains.create( {"name": "example.com"}, {"idempotency_key": "550e8400-...", "timeout": 10.0}, )

Links