Skip to main content

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 inbox
  • get_inbox(inbox_id) - Get an inbox by ID
  • update_inbox(inbox_id, *, name=None, webhook_url=None, ...) - Update an inbox
  • list_inboxes(*, limit=None, offset=None) - List inboxes with optional pagination; returns InboxList (inboxes, total)

Item Methods

  • upload_item(inbox_id, file, *, sender_address, ...) - Upload content to an inbox
  • get_item(item_id) - Get an item by ID
  • list_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 URL
  • attempt - 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 errors
  • SemaAPIError - Error returned by the API (has status_code and response_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 content
  • receive_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)

Source distribution for sema-sdk 0.1.0
File Size Uploaded
sema_sdk-0.1.0.tar.gz 17.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sema-sdk 0.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page