Sendara — email API (transactional + marketing email, broadcasts, contacts, templates, inbound, webhooks) for Python.
Project description
Sendara — Python SDK
The official Python client for the Sendara email API: transactional and marketing email, broadcasts, contacts and lists, templates, domains, inbound email, and webhooks — with sync and async clients, typed models, a typed error hierarchy, automatic retries, and an auto-paginating message iterator.
pip install sendara
Requires Python 3.9+. Runtime dependency: httpx.
Quickstart
import os
from sendara import Sendara, SendaraError
client = Sendara(os.environ["SENDARA_API_KEY"]) # sk_live_... or sk_test_...
# Send an email
result = client.emails.send(
from_="hello@acme.com",
to="user@example.com",
subject="Welcome to Acme",
html="<h1>Welcome 🎉</h1>",
)
print(result.id, result.status)
# Paginate every message (cursor handled for you)
for message in client.messages.iter(limit=100):
print(message.id, message.status)
Async
import asyncio
from sendara import AsyncSendara
async def main():
async with AsyncSendara("sk_live_...") as client:
await client.emails.send(to="user@example.com", subject="Hi", html="<p>Hi</p>")
async for message in client.messages.iter(limit=100):
print(message.id)
asyncio.run(main())
Both clients are context managers (with / async with) and accept
base_url, timeout, max_retries, and an optional http_client.
client = Sendara(
"sk_live_...",
base_url="https://api.sendara.dev",
timeout=30.0,
max_retries=3,
)
Idempotency
Every send accepts an idempotency_key. The SDK generates one (UUIDv4)
automatically when you omit it, so retries are always safe. Pass your own to
deduplicate across processes:
client.emails.send(to="u@e.com", subject="s", text="t", idempotency_key="order-42")
Retries
Idempotent requests (all GET/PUT/DELETE and sends) are retried automatically on
429, 409, 5xx, and network/timeout errors, using exponential backoff with
jitter. A Retry-After header is honored when present. Attempts are bounded by
max_retries.
Errors
Every failure raises a subclass of SendaraError carrying status, code,
message, and request_id:
| HTTP | Exception |
|---|---|
| 400 / 422 | ValidationError |
| 401 | AuthenticationError |
| 403 | PermissionError_ |
| 404 | NotFoundError |
| 409 | ConflictError |
| 429 | RateLimitError (.retry_after) |
| 5xx | ServerError |
| network | APIConnectionError / APITimeoutError |
from sendara import RateLimitError, SendaraError
try:
client.emails.send(to="u@e.com", subject="s", html="<p>x</p>")
except RateLimitError as e:
print("retry after", e.retry_after)
except SendaraError as e:
print(e.status, e.code, e.message)
Verifying webhooks
webhooks.verify recomputes HMAC-SHA256(secret, "<timestamp>.<raw_body>")
(hex) and checks it in constant time against the Sendara-Signature header.
Pass the raw request body, never a re-serialized object.
from sendara import webhooks
# Flask example
@app.post("/webhooks/sendara")
def hook():
event = webhooks.verify(
os.environ["SENDARA_WEBHOOK_SECRET"],
request.get_data(), # raw bytes
request.headers, # case-insensitive mapping
)
print(event["event_type"], event["message_id"])
return "", 200
verify raises WebhookVerificationError on any mismatch (bad signature,
missing headers, or a timestamp outside the tolerance window, default 300s).
Method reference
client.emails.send(to, subject, html=, text=, from_=, message_type=,
template_id=, template_vars=, idempotency_key=, metadata=, test_send=)
client.emails.send_raw(request) # POST /v1/send (escape hatch)
client.emails.send_batch([req, ...]) # POST /v1/send/batch
client.emails.send_bulk(request) # POST /v1/send/bulk
client.broadcasts.create(**params) / .list(limit=, offset=) / .get(id)
.send(id) / .cancel(id) / .delete(id)
client.messages.list(channel=, status=, from_=, to=, limit=, cursor=)
client.messages.iter(channel=, status=, from_=, to=, limit=) # auto-paginating
client.messages.get(id)
client.suppressions.list(channel=) / .create(channel, recipient, reason=)
.delete(channel, recipient)
client.domains.list() / .create(domain) / .get(domain) / .verify(domain)
client.api_keys.list() / .create(scope=, test_mode=) / .rotate(id) / .revoke(id)
client.usage.get(period=)
client.usage.set_spend_cap(key_id=, soft_limit_micros=, hard_limit_micros=)
client.billing.get() / .checkout(plan=) / .portal()
client.templates.create(**params) / .list() / .get(id) / .update(id, **params)
.delete(id) / .render(id, vars=)
client.contacts.create(**params) / .list(limit=, offset=) / .get(id)
.update(id, **params) / .delete(id) / .import_(s3_key=, format=)
client.lists.create(name, list_type=, segment_rules=) / .list() / .get(id)
.update(id, **params) / .delete(id)
.add_member(id, contact_id) / .remove_member(id, contact_id) / .members(id)
client.webhooks.create(endpoint_url, event_types=) / .list() / .get(id)
.update(id, **params) / .delete(id)
.deliveries(id, limit=) / .rotate_secret(id)
client.uploads.create(file, filename=, content_type=) # multipart image
client.test_recipients.list() / .create(email) / .resend(id) / .delete(id)
webhooks.verify(secret, payload, headers=, signature=, timestamp=, tolerance=300)
The async client (AsyncSendara) exposes every method above with await;
messages.iter becomes an async for.
Development
cd sdk/python
pip install -e '.[dev]'
python -m pytest
MIT licensed.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sendara-0.3.0.tar.gz.
File metadata
- Download URL: sendara-0.3.0.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2958d67abbc02376fd0b7213468939e4d21a2913eba648591a54fc5e237da3c7
|
|
| MD5 |
c3e514bb98befef9bf2c6773941b490e
|
|
| BLAKE2b-256 |
d532fb8e6d1eeec0cb4c72df2403cc86e8a6d3478ad5f4a4eae6caf0743a1b3d
|
File details
Details for the file sendara-0.3.0-py3-none-any.whl.
File metadata
- Download URL: sendara-0.3.0-py3-none-any.whl
- Upload date:
- Size: 22.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbae34fcd7f3dea3aa71720f0e46771055f1d340d1c6f305b48e0b07394cc289
|
|
| MD5 |
bb5632c178562f6744106fb98e24c2cf
|
|
| BLAKE2b-256 |
fd11e82dc54cb8f452136dbf246b76129fe65e6803f9c3a3f94ef2e1345d03da
|