> ## Documentation Index
> Fetch the complete documentation index at: https://docs.surge.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> Install the Python SDK, send messages with sync and async clients, paginate, handle typed errors, and verify webhooks.

The Python SDK wraps the Surge HTTP API with a typed client, sync and async variants, automatic pagination, and a webhook verifier. It's published as `surge-sdk` on PyPI and supports Python 3.9 and newer.

This page covers installing the package, sending your first request with both clients, paginating list endpoints, handling typed errors, and verifying webhook signatures in your handler.

## Install and authenticate

```bash theme={null}
pip install surge-sdk
```

Set your API key as an environment variable:

```bash theme={null}
export SURGE_API_KEY=sk_live_your_key_here
```

The client reads it automatically:

```python theme={null}
from surge import Surge

surge = Surge()
```

For async code, use `AsyncSurge`:

```python theme={null}
from surge import AsyncSurge

surge = AsyncSurge()
```

## Your first request

Send a message:

```python theme={null}
message = surge.messages.create(
    account_id="acct_01jrzhe8d9enptypyx360pcmxj",
    to="+18015551234",
    body="Your appointment is confirmed for Friday at 2pm.",
)

print(message.id)      # msg_01j...
print(message.status)  # "sent"
```

With `AsyncSurge`:

```python theme={null}
import asyncio

async def main():
    message = await surge.messages.create(
        account_id="acct_01j...",
        to="+18015551234",
        body="Your appointment is confirmed.",
    )
    print(message.status)

asyncio.run(main())
```

## Pagination

The SDK returns auto-paginating iterators for list endpoints. You don't need to manage cursors manually:

```python theme={null}
# Iterates through all messages automatically
for message in surge.messages.list(account_id="acct_01j..."):
    print(message.id, message.status)
```

To page manually, use the raw list method:

```python theme={null}
page = surge.messages.list_page(account_id="acct_01j...", limit=25)
print(page.data)            # list of messages
print(page.pagination.next_cursor)
```

## Retries and timeouts

The SDK retries transient failures (network errors, 5xx responses) with exponential backoff. Default: 3 retries.

Configure retries and timeout on the client:

```python theme={null}
surge = Surge(
    max_retries=5,
    timeout=30.0,  # seconds
)
```

To disable retries for a specific call:

```python theme={null}
message = surge.messages.create(
    account_id="acct_01j...",
    to="+18015551234",
    body="Hello",
    max_retries=0,
)
```

## Error handling

Errors raise typed exceptions:

```python theme={null}
from surge import SurgeError, APIStatusError

try:
    surge.messages.create(
        account_id="acct_01j...",
        to="+18015551234",
        body="Hello",
    )
except APIStatusError as e:
    print(e.status_code)  # e.g. 422
    print(e.error.type)   # e.g. "opted_out"
    print(e.error.message)
```

Common exception types:

| Exception             | When                               |
| --------------------- | ---------------------------------- |
| `APIStatusError`      | API returned a 4xx or 5xx response |
| `APIConnectionError`  | Network error or timeout           |
| `AuthenticationError` | Invalid or missing API key (401)   |
| `RateLimitError`      | Rate limit exceeded (429)          |

## Webhooks

Verify webhook signatures and parse events:

```python theme={null}
import os
from surge.webhooks import Webhook

wh = Webhook(os.environ["SURGE_WEBHOOK_SECRET"])

# In your webhook handler (Flask example):
@app.post("/webhooks/surge")
def surge_webhook():
    try:
        event = wh.verify(request.get_data(), request.headers)
    except Exception:
        return "", 400

    match event["type"]:
        case "message.received":
            handle_inbound(event["data"])
        case "contact.opted_out":
            handle_opt_out(event["data"])

    return "", 200
```

The `Webhook` class follows the Standard Webhooks spec and handles replay protection automatically.
