Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Modellen Tools & Services
Oplossingen
Organisaties Ontwikkelaars Internet Service Providers Managed Service Providers AI-in-a-Box
Kenniscentrum
Ondersteuning Documentation Blog Downloads
Bedrijf
Over ons Onderzoek Vacatures Investeren Contact
Inloggen

Python SDK

Official Python client for ScaiGrid. Sync and async clients, first-class streaming for chat completions, typed errors, and dedicated sub-clients for ScaiBot, ScaiSpeak, and ScaiDial.

Install#

bash
1
pip install scailabs-scaigrid

The top-level import name is scaigrid (the distribution name carries the ScaiLabs brand prefix, but the package itself stays short).

Python ≥ 3.10 supported. The only required dependencies are httpx and pydantic.

Quickstart#

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from scaigrid import ScaiGrid

with ScaiGrid(api_key="sgk_...") as client:
    # List models
    for m in client.models.list(modality="chat"):
        print(m["slug"])

    # Streaming chat
    for chunk in client.inference.chat_stream(
        model="anthropic/claude-haiku-4-5",
        messages=[{"role": "user", "content": "Hello!"}],
        metadata={"agent_id": "..."},   # flows into accounting
    ):
        print(chunk["delta"].get("content", ""), end="", flush=True)

Async variant — same API, different prefix:

python
1
2
3
4
5
6
7
from scaigrid import AsyncScaiGrid

async with AsyncScaiGrid(api_key="sgk_...") as client:
    resp = await client.inference.chat(
        model="anthropic/claude-haiku-4-5",
        messages=[{"role": "user", "content": "Hi"}],
    )

Authentication#

Pick exactly one of three modes:

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. Static API key (most common)
client = ScaiGrid(api_key="sgk_...")

# 2. Pre-minted JWT
client = ScaiGrid(access_token=jwt)

# 3. OAuth client_credentials against ScaiKey — cached + auto-refresh
client = ScaiGrid.from_client_credentials(
    client_id="...",
    client_secret="...",
    token_url="https://scaikey.scailabs.ai/api/v1/platform/oauth/token",
)

For full control (custom auth, refresh, token caching) implement the Auth protocol and pass it as auth=:

python
1
2
3
4
5
6
7
from scaigrid import Auth, ScaiGrid

class MyAuth:
    def header(self) -> str: ...
    async def header_async(self) -> str: ...

client = ScaiGrid(auth=MyAuth())

Resources#

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Models — list, get
client.models.list(modality="chat")
client.models.get("anthropic/claude-haiku-4-5")

# Inference — chat (sync), chat_stream (Iterator), embed
client.inference.chat(model=..., messages=...)
client.inference.chat_stream(model=..., messages=...)
client.inference.embed(model="text-embedding-3-small", input=["..."])

# Modules — list/get/enable/disable, generic proxy
client.modules.list()
client.modules.proxy("scaimatrix", "POST", "/collections", json_body={...})

Module sub-clients#

First-class wrappers for the most-used module routes:

ScaiBot#

python
1
2
3
4
5
6
7
8
9
bot = client.scaibot.bots.create(
    name="support",
    model="anthropic/claude-haiku-4-5",
    display_name="Support Bot",
)

calls = client.scaibot.voice.list_calls(limit=20)
transcript = client.scaibot.voice.get_transcript(call_id)
cost = client.scaibot.voice.get_cost(call_id)

ScaiSpeak#

python
1
2
3
4
5
6
7
audio = client.scaispeak.synthesize(
    voice_id="voice_...",
    text="Hello, world!",
)
# audio["audio"] is decoded bytes; audio["audio_base64"] is the raw payload.

voices = client.scaispeak.voices.list(language="nl")

ScaiDial#

python
1
2
3
4
5
6
7
8
extensions = client.scaidial.extensions.list()
client.scaidial.extensions.create(number="1042", type="bot", target_ref=bot.id)

matches = client.scaidial.directory.lookup("billing")
client.scaidial.dialplans.add_rule(
    dialplan_id, action_type="goto_extension",
    action_params={"extension_number": "1042"},
)

For modules without a dedicated wrapper (ScaiMatrix, ScaiMind, ScaiPersona, etc.), reach for client.modules.proxy(module_id, method, path, ...).

Streaming#

chat_stream returns an iterator of typed chunk dicts. Each chunk is the native ScaiGrid shape — delta, finish_reason, usage, model — not the OpenAI-compat envelope.

python
1
2
3
4
5
6
7
8
buffer = []
for chunk in client.inference.chat_stream(model=..., messages=...):
    delta = chunk.get("delta", {}).get("content")
    if delta:
        buffer.append(delta)
    if chunk.get("finish_reason"):
        print(f"finished: {chunk['finish_reason']}")
print("".join(buffer))

The async client yields the same shape via async for.

Errors#

Every exception is a subclass of ScaiGridError with the same code string as the server. Catch the specific subclass you care about:

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from scaigrid import (
    ScaiGridError, NotFoundError, RateLimitedError,
    BudgetExceededError, ValidationError,
)

try:
    client.inference.chat(...)
except RateLimitedError as exc:
    print(f"retry after {exc.retry_after}s")
except BudgetExceededError:
    # Tenant or user budget cap hit
    ...
except ScaiGridError as exc:
    # Catch-all — exc.code, exc.http_status, exc.request_id all populated.
    ...

Full taxonomy: AuthError, AuthTokenInvalidError, AuthTokenExpiredError, PermissionDeniedError, NotFoundError, ConflictError, ValidationError, RateLimitedError, BudgetExceededError, BackendError, BackendTimeoutError, BackendRateLimitedError, InternalServerError, TimeoutError, TransportError.

Pagination#

Paginated endpoints expose a cursor in the response. The SDK lets you iterate manually:

python
1
2
3
4
5
6
7
8
cursor = None
while True:
    page = client.scaibot.voice.list_calls(limit=100, cursor=cursor)
    for call in page["items"]:
        process(call)
    cursor = page.get("pagination", {}).get("next_cursor")
    if not cursor:
        break

Request IDs + observability#

Every method accepts an optional request_id= kwarg. The SDK forwards it as X-Request-ID and ScaiGrid propagates it through logs, audit records, and downstream module calls.

python
1
2
3
4
client.inference.chat(
    model=..., messages=...,
    request_id="req-batch-job-7",
)

Server-side exceptions surface the request_id on the raised exception so you can join client- and server-side traces.

Retries#

The HTTP layer retries 429 and idempotent 5xx up to three times (exponential backoff with jitter, honours Retry-After). Tune via the constructor:

python
1
client = ScaiGrid(api_key="sgk_...", max_retries=5, timeout=120)

POST requests (the inference path) are not retried by default; the user-side retry policy lives in the caller.

Download#

The current release is available on /downloads under ScaiGrid → Python, with SHA-256 verification.

See also#

Updated 2026-06-15 13:16:10 View source (.md) rev 2