Sema Python SDK
Official Python SDK for Sema - Universal ingestion infrastructure for AI Agents.
Installation
pip install sema-sdk
Quick Start
API Client
from sema_sdk import SemaClient
with SemaClient(api_key="sk_live_...") as client:
# Create an inbox
inbox = client.create_inbox(
name="My Inbox",
webhook_url="https://example.com/webhooks/sema",
)
print(f"Created inbox: {inbox.id}")
# Upload content
item = client.upload_item(
inbox_id=inbox.id,
file=b"Hello, World!",
sender_address="sender@example.com",
subject="Test Upload",
)
print(f"Created item: {item.id}")
# Check deliveries
deliveries = client.get_item_deliveries(item.id)
for d in deliveries.deliveries:
print(f"Delivery {d.id}: {d.status}")
Async API Client
import asyncio
from sema_sdk import AsyncSemaClient
async def main() -> None:
async with AsyncSemaClient(api_key="sk_live_...") as client:
# Create an inbox
inbox = await client.create_inbox(
name="My Inbox",
webhook_url="https://example.com/webhooks/sema",
)
print(f"Created inbox: {inbox.id}")
# Upload content
item = await client.upload_item(
inbox_id=inbox.id,
file=b"Hello, World!",
sender_address="sender@example.com",
subject="Test Upload",
)
print(f"Created item: {item.id}")
# Check deliveries
deliveries = await client.get_item_deliveries(item.id)
for d in deliveries.deliveries:
print(f"Delivery {d.id}: {d.status}")
asyncio.run(main())
Webhook Verification
from sema_sdk import WebhookVerifier, WebhookVerificationError
verifier = WebhookVerifier(secret="whsec_...")
def handle_webhook(request):
try:
event = verifier.verify(
payload=request.body,
headers=request.headers,
)
# Use event.webhook_id as idempotency key
print(f"Received: {event.webhook_id}")
print(f"Item ID: {event.payload.item_id}")
print(f"Event type: {event.payload.event_type}")
return Response(status=200)
except WebhookVerificationError as e:
return Response(status=400, body=str(e))
API Reference
SemaClient
Synchronous client for the Sema API.
client = SemaClient(
api_key="sk_live_...",
base_url="https://api.withsema.com", # optional
timeout=30.0, # optional, in seconds
max_retries=3, # optional, set to 0 to disable retries
)
The client automatically retries requests on transient failures (5xx errors, network errors, rate limits) with exponential backoff. Rate-limited requests (429) respect the Retry-After header when present.
AsyncSemaClient
Async client for the Sema API.
from sema_sdk import AsyncSemaClient
async with AsyncSemaClient(
api_key="sk_live_...",
base_url="https://api.withsema.com", # optional
timeout=30.0, # optional, in seconds
max_retries=3, # optional, set to 0 to disable retries
) as client:
inboxes = await client.list_inboxes(limit=10)
Inbox Methods
create_inbox(name, *, description=None, webhook_url=None, ...)- Create a new inboxget_inbox(inbox_id)- Get an inbox by IDupdate_inbox(inbox_id, *, name=None, webhook_url=None, ...)- Update an inboxlist_inboxes(*, limit=None, offset=None)- List inboxes with optional pagination; returnsInboxList(inboxes,total)
Item Methods
upload_item(inbox_id, file, *, sender_address, ...)- Upload content to an inboxget_item(item_id)- Get an item by IDlist_items(inbox_id, *, limit=None, offset=None)- List items in an inbox
Delivery Methods
get_item_deliveries(item_id)- Get deliveries for an item
Attachment Methods
get_item_attachments(item_id)- Get attachments for an item with presigned download URLs
Pagination Iterators
For large result sets, use async iterators to automatically paginate:
# Iterate through all items in an inbox
async for item in client.iter_items(inbox_id):
print(item.id, item.status)
# Iterate through all inboxes
async for inbox in client.iter_inboxes():
print(inbox.id, inbox.name)
# Custom page size
async for item in client.iter_items(inbox_id, page_size=50):
print(".", end="")
Observability Hooks
Add optional hooks for logging, tracing, or metrics:
def before_request(ctx):
print(f"→ {ctx['method']} {ctx['url']} (attempt {ctx['attempt']})")
def after_response(ctx):
print(f"← {ctx['status']} in {ctx['duration_ms']:.1f}ms")
def on_error(ctx, error):
print(f"✗ {ctx['method']} {ctx['url']}: {error}")
client = SemaClient(
api_key="sk_live_...",
on_before_request=before_request,
on_after_response=after_response,
on_error=on_error,
)
Hook context includes:
method- HTTP method (GET, POST, etc.)url- Request URLattempt- Attempt number (1-based, increases on retries)status- HTTP status code (after_response only)duration_ms- Request duration in milliseconds (after_response only)
Hooks can be sync or async. Errors raised in hooks are silently ignored to prevent breaking SDK functionality.
WebhookVerifier
Verifies Standard Webhooks signatures.
verifier = WebhookVerifier(
secret="whsec_...",
tolerance_seconds=300, # optional, default 5 minutes
)
event = verifier.verify(payload, headers)
# Returns WebhookEvent with:
# - webhook_id: str (use as idempotency key)
# - timestamp: int
# - payload: WebhookPayload
Email Utilities
resolve_email_inline_images
HTML emails embed inline images using cid: references. This utility replaces them with download URLs:
from sema_sdk import SemaClient, WebhookVerifier, resolve_email_inline_images
client = SemaClient(api_key="sk_live_...")
verifier = WebhookVerifier(secret="whsec_...")
def handle_webhook(request):
event = verifier.verify(request.body, request.headers)
# Get the HTML body with cid: references
content_summary = event.payload.deliverable.content_summary
body_html = (content_summary.body_html or "") if content_summary else ""
# Fetch attachments with presigned download URLs
attachments = client.get_item_attachments(event.payload.item_id).attachments
# Replace cid: references with actual URLs
resolved_html = resolve_email_inline_images(body_html, attachments)
# resolved_html now has working image URLs
return resolved_html
partition_email_attachments
Email clients like Gmail set content_id on ALL attachments, not just inline ones. This utility correctly identifies which attachments are truly inline (embedded in HTML) vs which should be listed separately:
from sema_sdk import SemaClient, partition_email_attachments, resolve_email_inline_images
client = SemaClient(api_key="sk_live_...")
def handle_webhook(request):
# ... verify webhook ...
content_summary = event.payload.deliverable.content_summary
body_html = (content_summary.body_html or "") if content_summary else ""
attachments = client.get_item_attachments(event.payload.item_id).attachments
# Resolve inline images in HTML
resolved_html = resolve_email_inline_images(body_html, attachments)
# Partition: inline images vs files to list separately
inline, non_inline = partition_email_attachments(body_html, attachments)
# inline attachments are already displayed in resolved_html
# non_inline attachments should be listed separately
for att in non_inline:
print(f"Attachment: {att.filename} ({att.content_type})")
Exceptions
SemaError- Base exception for all SDK errorsSemaAPIError- Error returned by the API (hasstatus_codeandresponse_body)AuthenticationError- Invalid or missing API key (401)NotFoundError- Resource not found (404)RateLimitError- Rate limit exceeded (429)WebhookVerificationError- Signature or timestamp validation failed
Examples
See the examples/ directory for runnable examples:
send_content.py- Create inbox and upload contentreceive_webhook.py- Flask webhook receiver with signature verification
Requirements
- Python 3.10+
- httpx
- pydantic
License
MIT
Release files for sema-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sema_sdk-0.1.0.tar.gz | 17.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sema_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.4 kB
Release files / sema_sdk-0.1.0.tar.gz
| Download URL | sema_sdk-0.1.0.tar.gz |
|---|---|
| Size | 17.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bfea2ecdcc208090b3f63d4789fd3b44875beb7ac3ffbc6cffd919bd4a4218ea
|
|
BLAKE2b-256 checksum How to use checksums |
727495abf0ce9703bb2a5d9f18caf8813fcb66324465c8874541bfccde279ff2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|
Release files / sema_sdk-0.1.0-py3-none-any.whl
| Download URL | sema_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
59532faa08dfb053a6a5ac42df7a1b56bd99334aa3d2afe829ce322c26aed5bb
|
|
BLAKE2b-256 checksum How to use checksums |
73e518137d68d8b261962b9b357589558cfebbc0c8f63ddd63041f56dcf0923d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.1
|