routerai
Python wrapper for the RouterAI API — unified access to 450+ AI models (OpenAI, Anthropic, Google, DeepSeek, Qwen, ...) with ruble pricing.
Features
- OpenAI-compatible chat completions with sync + async support (one client instance supports both; transports are kept separate)
- Parsed responses: content, reasoning, tool calls, alternatives, token usage and cost
in rubles (
Decimal) - Streaming (SSE) with per-chunk deltas; once a successful response stream is opened
no automatic retries happen (even if 0 chunks arrived), mid-stream failures raise a
typed
StreamInterruptedError - Models catalog: listing, client-side search, grouping by capabilities (text, reasoning, vision, image/video/audio generation, speech, transcription, embeddings, rerank, tools)
- Post-hoc cost lookup by generation id (
X-Generation-Id) - Multiple API keys via a per-instance
Registry(contextvar, thread/async safe) - Images, video (polling), audio (TTS/STT), embeddings, rerank, API-key and team management
- Zero-effort logging via the standard
loggingmodule (namespacerouterai, keys masked)
Install
pip install routerai
Quickstart
from routerai import RouterAI
client = RouterAI(api_key="sk-...") # or set ROUTERAI_API_KEY env var
result = client.chat.complete("deepseek/deepseek-v4-pro", "Привет!")
print(result.content)
print(result.cost_rub) # Decimal, in rubles
Model catalog
client.models.all() # full catalog (cached, TTL by default 10 min)
client.models.search("claude", capabilities=["reasoning"], min_context=100_000)
client.models.by_capability("image") # image generation models
client.models.grouped() # dict[Capability, list[Model]]
client.models.get("deepseek/deepseek-v4-pro").pricing.per_million("prompt")
client.models.endpoints("anthropic/claude-sonnet-5") # providers + prices
Several API keys
from routerai import RouterAI, Registry
registry = Registry(main=RouterAI(api_key=A), personal=RouterAI(api_key=B))
registry["personal"].chat.complete(...)
with registry.using("main"):
...
Streaming
for chunk in client.chat.stream("openai/gpt-5.6-sol", "Расскажи сказку"):
print(chunk.content, end="")
Speech-to-text
client.audio.transcribe("openai/whisper-large-v3", "voice.wav") # format from suffix
client.audio.transcribe("openai/whisper-large-v3", raw_bytes, format="mp3")
for chunk in client.audio.speech_stream("x-ai/grok-voice-tts-1.0", "текст", voice="eve"):
...
Video lifecycle
from routerai import FrameImage, ImageReference
task = client.videos.create(
"bytedance/seedance-2.0",
"Персонаж идёт через осенний лес",
frame_images=[FrameImage(url="https://example.com/first.png", frame_type="first_frame")],
# or reference-to-video: input_references=[ImageReference(url=...)]
)
task.wait(timeout=600, interval=5) # deadline includes sleeps and retries
task.save("video.mp4", index=0) # streaming download, atomic rename
await task.asave("video.mp4") # async variant, cancellation-safe
# webhooks: verify HMAC over the raw body with your api key
from routerai.webhooks import verify_video
data = verify_video(raw_body, signature, api_key, timestamp, max_age_seconds=300)
Async
result = await client.chat.acomplete("deepseek/deepseek-v4-pro", "Привет!")
async for chunk in client.chat.astream(...):
...
await client.aclose()
Sync and async transports live in separate slots, so one instance can serve both
modes. Note the lifecycle: close() closes the sync connection pool, await aclose() closes the async one. If a single instance was used from both modes,
call both. External transports injected via http_client/async_http_client
are never closed by the library.
Configuration
| Option | Description |
|---|---|
api_key / ROUTERAI_API_KEY |
API key (env var used when argument is None) |
base_url / ROUTERAI_BASE_URL |
base URL; precedence: explicit argument > env var > https://routerai.ru/api/v1 |
timeout |
per-operation network inactivity timeout in seconds (default 60) |
max_retries |
retry attempts with exponential backoff + jitter (default 2) |
max_retry_after |
upper bound for an upstream Retry-After header, seconds (default 60) |
retry_unsafe_methods |
retry POST/PATCH/DELETE on 5xx too (default False; RouterAI already |
| does provider fallback, a client-side POST retry may start a new billed generation) | |
http_client / async_http_client |
inject external httpx transports (never closed by the library) |
Retries honour the Retry-After header. Safe methods (GET/HEAD) are retried on
429/5xx; unsafe methods only on 429 by default.
Video polling propagates one deadline through sleeps, attempts and retry
backoff. Async polling actively cancels an in-flight refresh at the deadline.
For sync polling, HTTPX can only interrupt an in-flight socket operation using
its connect/read/write/pool inactivity timeouts; if that operation returns just
after the deadline, the SDK raises DeadlineExceededError before processing or
retrying the response.
The extra parameter is an escape hatch for provider-specific request fields:
it can never override library-managed keys (model, messages, stream, ...)
— colliding keys raise ValueError.
Errors
| Exception | HTTP |
|---|---|
AuthenticationError |
401 |
InsufficientFundsError |
402 |
PermissionDeniedError |
403 |
NotFoundError |
404 |
RateLimitError |
429 |
NoProviderError |
503 with "no provider available" |
APIStatusError |
other 4xx/5xx (has .status_code, .body) |
RequestError |
transport failure after retries |
DeadlineExceededError |
an absolute polling deadline passed (video wait()) |
StreamInterruptedError |
SSE broke after the response stream was opened (.chunks_received may be 0) |
VideoGenerationError |
a video task reached failed/cancelled/expired |
WebhookVerificationError |
video webhook failed signature or freshness checks |
Logging
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger("routerai").setLevel(logging.DEBUG) # API keys are masked
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file routerai-0.1.1.tar.gz.
File metadata
- Download URL: routerai-0.1.1.tar.gz
- Upload date:
- Size: 129.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76b70c2eb5548b24eccc5db945161b729ec4393080e89a706e0474bcf566738a
|
|
| MD5 |
fb1a587e4ba7dec2768067162af86139
|
|
| BLAKE2b-256 |
a38690a68e5e15d99e4903b7ee57ce7271e61782767135235b761cabc969dbe2
|
Provenance
The following attestation bundles were made for routerai-0.1.1.tar.gz:
Publisher:
release.yml on alcovegan/routerai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
routerai-0.1.1.tar.gz -
Subject digest:
76b70c2eb5548b24eccc5db945161b729ec4393080e89a706e0474bcf566738a - Sigstore transparency entry: 2456733384
- Sigstore integration time:
-
Permalink:
alcovegan/routerai@d91d800b24cd4ee6402174a63d8367ed882b470a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/alcovegan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d91d800b24cd4ee6402174a63d8367ed882b470a -
Trigger Event:
push
-
Statement type:
File details
Details for the file routerai-0.1.1-py3-none-any.whl.
File metadata
- Download URL: routerai-0.1.1-py3-none-any.whl
- Upload date:
- Size: 48.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a85042ba4178bee89c638944031f0cf6d02440f2425c33ef166655d784991bc
|
|
| MD5 |
90ce098c2bef0007e666f460eb4693e4
|
|
| BLAKE2b-256 |
e495faa817ba94754ab13777206cd970729019472813b0e87774202ca40abfc4
|
Provenance
The following attestation bundles were made for routerai-0.1.1-py3-none-any.whl:
Publisher:
release.yml on alcovegan/routerai
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
routerai-0.1.1-py3-none-any.whl -
Subject digest:
5a85042ba4178bee89c638944031f0cf6d02440f2425c33ef166655d784991bc - Sigstore transparency entry: 2456733858
- Sigstore integration time:
-
Permalink:
alcovegan/routerai@d91d800b24cd4ee6402174a63d8367ed882b470a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/alcovegan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d91d800b24cd4ee6402174a63d8367ed882b470a -
Trigger Event:
push
-
Statement type: