Skip to main content

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 logging module (namespace routerai, 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

routerai-0.1.1.tar.gz (129.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

routerai-0.1.1-py3-none-any.whl (48.2 kB view details)

Uploaded Python 3

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

Hashes for routerai-0.1.1.tar.gz
Algorithm Hash digest
SHA256 76b70c2eb5548b24eccc5db945161b729ec4393080e89a706e0474bcf566738a
MD5 fb1a587e4ba7dec2768067162af86139
BLAKE2b-256 a38690a68e5e15d99e4903b7ee57ce7271e61782767135235b761cabc969dbe2

See more details on using hashes here.

Provenance

The following attestation bundles were made for routerai-0.1.1.tar.gz:

Publisher: release.yml on alcovegan/routerai

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for routerai-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5a85042ba4178bee89c638944031f0cf6d02440f2425c33ef166655d784991bc
MD5 90ce098c2bef0007e666f460eb4693e4
BLAKE2b-256 e495faa817ba94754ab13777206cd970729019472813b0e87774202ca40abfc4

See more details on using hashes here.

Provenance

The following attestation bundles were made for routerai-0.1.1-py3-none-any.whl:

Publisher: release.yml on alcovegan/routerai

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page