Skip to content

Python SDK

raclip is the official Python SDK. It's a thin, typed wrapper around the HTTP API — every SDK call corresponds to a documented endpoint.

Install

pip install raclip
# or
uv add raclip

Requires Python 3.10+. The only runtime dependencies are httpx and pydantic.

Authenticate

Pass an API key explicitly, or set RACLIP_API_KEY in your environment:

export RACLIP_API_KEY="raclip_prod_…"
from raclip import Client

# Reads RACLIP_API_KEY from the environment
client = Client()

# Or pass explicitly
client = Client(api_key="raclip_prod_…")

The SDK never sends your org_id on the wire — the gateway derives it from the key.

Quickstart

from raclip import Client

with Client() as client:
    devices = client.devices.list()
    print(f"{devices.total} devices in this org")

    for device in devices.devices:
        print(device.device_id, device.status, device.last_seen)

    if devices.devices:
        device_id = devices.devices[0].device_id
        calls = client.calls.list(device_id, per_page=5)
        for call in calls.calls:
            print(call.call_id, call.duration_seconds, "sec")
            print("  download:", call.download_url)
import asyncio
from raclip import AsyncClient

async def main():
    async with AsyncClient() as client:
        devices = await client.devices.list()
        print(f"{devices.total} devices")

asyncio.run(main())

AsyncClient mirrors the sync surface one-for-one — every method has an async twin under the same name.

Download a recording

download_url on a call response is a short-lived URL. You can fetch it with anything that speaks HTTP:

import httpx

httpx.get(call.download_url).content  # bytes

Or use the SDK's streaming helper, which avoids leaking your Authorization header on the redirect:

client.calls.download(device_id, call.call_id, dest="recording.mp3")

dest accepts a path (string or Path) or any binary file-like object.

Error handling

Every non-2xx response converts to a typed exception so you can catch the cases you care about:

from raclip import (
    Client, AuthenticationError, NotFoundError, RateLimitError
)

with Client() as client:
    try:
        call = client.calls.get("device-id", "call-id")
    except NotFoundError:
        print("call is gone")
    except RateLimitError as e:
        print(f"slow down; retry after {e.retry_after}s")
    except AuthenticationError:
        print("check your API key")

Hierarchy: RaclipError → AuthenticationError, PermissionError, NotFoundError, ValidationError, RateLimitError, APIError, APIConnectionError.


Clients

Client is the synchronous entry point. AsyncClient is the asyncio twin — same surface, same method names, every call awaitable. Both auto-read RACLIP_API_KEY from the environment if api_key is omitted.

Client

Synchronous raclip API client.

Example

from raclip import Client

with Client() as client: # RACLIP_API_KEY from env for device in client.devices.list().devices: print(device.device_id, device.status)

base_url property

base_url: str

devices instance-attribute

devices = DevicesResource(self)

calls instance-attribute

calls = CallsResource(self)

close

close() -> None

AsyncClient

Asynchronous raclip API client.

Example

import asyncio from raclip import AsyncClient

async def main(): async with AsyncClient() as client: result = await client.devices.list() for device in result.devices: print(device.device_id, device.status)

asyncio.run(main())

base_url property

base_url: str

devices instance-attribute

devices = AsyncDevicesResource(self)

calls instance-attribute

calls = AsyncCallsResource(self)

aclose async

aclose() -> None

Resources

Each Client exposes two resource groups: devices and calls. Both have sync and async variants. Methods are paired one-for-one — anything you can do synchronously you can await.

Devices

raclip.resources.devices.DevicesResource

DevicesResource(client: 'Client')

raclip.resources.devices.AsyncDevicesResource

AsyncDevicesResource(client: 'AsyncClient')

Calls

raclip.resources.calls.CallsResource

CallsResource(client: 'Client')

download

download(
    device_id: str,
    call_id: str,
    *,
    dest: DownloadDest,
    chunk_size: int = 64 * 1024,
) -> Path | IO[bytes]

Stream a call's MP3 to a file path or file-like.

Fetches metadata to resolve the presigned S3 URL, then downloads the bytes over a fresh httpx stream that does NOT carry the Authorization header — S3 rejects unexpected auth headers on presigned URLs.

raclip.resources.calls.AsyncCallsResource

AsyncCallsResource(client: 'AsyncClient')

Models

Every API response is parsed into a Pydantic v2 model.

Device

raclip.Device

Bases: _RaclipModel

raclip.DeviceListResponse

Bases: _RaclipModel

raclip.DeviceStats

Bases: _RaclipModel

raclip.DeviceStatusSnapshot

Bases: _RaclipModel

Call

raclip.Call

Bases: _RaclipModel

A call recording.

Shape is the union of what the list and retrieval endpoints return — fields present on one endpoint but not the other are marked optional, so the same model works in both contexts.

raclip.CallListResponse

Bases: _RaclipModel

raclip.CallStatistics

Bases: _RaclipModel


Exceptions

Every non-2xx response raises a typed exception in the RaclipError tree:

RaclipError
├── APIConnectionError       # network failure, DNS, TLS, etc.
├── APIError                 # 5xx from the gateway
├── AuthenticationError      # 401
├── PermissionError          # 403
├── NotFoundError            # 404
├── ValidationError          # 400 / 422
└── RateLimitError           # 429 — exposes .retry_after

RaclipError

RaclipError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: Exception

Base class for all raclip SDK errors.

APIConnectionError

APIConnectionError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

Network-level failure reaching the gateway.

APIError

APIError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

5xx response from the gateway or raclip-api.

AuthenticationError

AuthenticationError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

401 — missing or invalid API key.

PermissionError

PermissionError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

403 — caller is authenticated but not allowed.

NotFoundError

NotFoundError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

404 — resource (device, call) does not exist.

ValidationError

ValidationError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
)

Bases: RaclipError

400 / 422 — request did not pass validation.

RateLimitError

RateLimitError(
    message: str,
    *,
    status_code: int | None = None,
    response_body: Any = None,
    retry_after: float | None = None,
)

Bases: RaclipError

429 — per-key rate limit exceeded.