Skip to content
Search documentation titles, headings, and guides
API v1Updated 2026-09-21

SDKs & Tools

Use the official OpenAI and Anthropic SDKs unchanged — just point them at the zrouter gateway with your API key.

zrouter is a drop-in gateway: you keep using the official OpenAI and Anthropic SDKs, and the only changes are the base URL and the API key. An zr_<public-id>_<secret> key authenticates both OpenAI-compatible and Anthropic-compatible clients.

Drop-in quickstart#

curl -X POST "https://api.zrouter.dev/v1/chat/completions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<public-name>",
    "messages": [{"role": "user", "content": "Say hello!"}]
  }'

Streaming#

Streaming works with the same SDKs — set stream and iterate. You are billed only for the tokens actually delivered. See Streaming & SSE for the underlying wire protocol.

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.zrouter.dev/v1",
)

stream = client.chat.completions.create(
    model="<public-name>",
    messages=[{"role": "user", "content": "Say hello!"}],
    stream=True,
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

Cookbook#

Copy-paste recipes for the most common tasks. Each recipe is self-contained; follow the links for the full background on streaming and error handling.

Minimal chat call#

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.zrouter.dev/v1")

response = client.chat.completions.create(
    model="<public-name>",
    messages=[{"role": "user", "content": "Say hello!"}],
)
print(response.choices[0].message.content)

Minimal streaming#

Set stream and iterate. See Streaming & SSE for the wire protocol and SDK examples with usage tracking.

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.zrouter.dev/v1")

stream = client.chat.completions.create(
    model="<public-name>",
    messages=[{"role": "user", "content": "Say hello!"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

Error handling with retry#

Retry only 429 and 5xx failures that happen before the first byte, with exponential backoff and jitter, capped at 3 attempts. Never retry a 402 (top up your balance instead). See Error Handling.

import random, time
from openai import OpenAI, APIStatusError

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.zrouter.dev/v1")
RETRYABLE = {429, 500, 502, 503}

def chat(prompt: str, model: str = "<public-name>") -> str:
    for attempt in range(3):
        try:
            resp = client.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": prompt}],
            )
            return resp.choices[0].message.content or ""
        except APIStatusError as err:
            if err.status_code == 402:
                raise  # insufficient balance — never retry
            if err.status_code in RETRYABLE and attempt < 2:
                time.sleep(min(2 ** attempt, 8) + random.random())
                continue
            raise

List available models#

List the models your key can access, with their public names — use these as the model argument everywhere. Live pricing is on the pricing page and the public Model Catalog.

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.zrouter.dev/v1")

for model in client.models.list():
    print(model.id)