OpenRouter Provider
Unified gateway to 300+ models from OpenAI, Anthropic, Google, Meta, Mistral,
DeepSeek, and more — all through a single OpenAI-compatible API. One key,
every model. Use openrouter/auto to let OpenRouter pick the best
model per request.
| Provider Name | openrouter |
| Module | shared.plugins.model_provider.openrouter |
| SDK | openai (OpenAI-compatible API) |
| Auth | API key (sk-or-... from openrouter.ai/settings/keys) |
| Default endpoint | https://openrouter.ai/api/v1 |
Highlights
- 300+ models — One API key for every major model provider
- Auto model —
openrouter/autopicks the best available model per request - Cross-model fallback —
api_params.modelslist walks candidates on failure - Provider routing — Fine-grained control over which upstream hosts a request
- Prompt caching — Explicit
cache_controlbreakpoints for Anthropic/Gemini upstreams - Extended thinking — Reasoning extraction from DeepSeek-R1, Claude, o-series
- Context auto-detect — Catalog queried at
connect()for per-modelcontext_length
# Set your API key
export JAATO_OPENROUTER_API_KEY=sk-or-your-key-here
export JAATO_PROVIDER=openrouter
export JAATO_OPENROUTER_MODEL=anthropic/claude-sonnet-4-5
from jaato import JaatoClient
client = JaatoClient(provider_name="openrouter")
client.connect(
project=None,
location=None,
model="anthropic/claude-sonnet-4-5"
)
client.configure_tools(registry)
response = client.send_message(
"Hello from OpenRouter!",
on_output=on_output
)
# Let OpenRouter pick the best model
client.connect(None, None, "openrouter/auto")
Authentication
Get Your API Key
- Visit openrouter.ai/settings/keys
- Create a new key (
sk-or-...) - Set it as
JAATO_OPENROUTER_API_KEYor useopenrouter-auth key
Credential Priority
| Priority | Source |
|---|---|
| 1 (highest) | JAATO_OPENROUTER_API_KEY environment variable |
| 2 | Stored credentials (openrouter-auth key <api_key> — validates against GET /api/v1/key and stores securely) |
# .env file
JAATO_OPENROUTER_API_KEY=sk-or-your-key-here
JAATO_PROVIDER=openrouter
JAATO_OPENROUTER_MODEL=anthropic/claude-sonnet-4-5
# Validates key against /api/v1/key and stores securely
openrouter-auth key sk-or-your-key-here
# Check status
openrouter-auth status
# Clear stored credentials
openrouter-auth logout
Environment Variables
| Variable | Default | Description |
|---|---|---|
JAATO_OPENROUTER_API_KEY |
— | API key (sk-or-...) |
JAATO_OPENROUTER_BASE_URL |
https://openrouter.ai/api/v1 |
API endpoint override |
JAATO_OPENROUTER_MODEL |
— | Default model name (e.g. anthropic/claude-sonnet-4-5) |
JAATO_OPENROUTER_CONTEXT_LENGTH |
Auto-detected from catalog | Override context window size |
JAATO_OPENROUTER_HTTP_REFERER |
https://github.com/Jaato-framework-and-examples/jaato |
HTTP-Referer header for OpenRouter app rankings |
JAATO_OPENROUTER_APP_TITLE |
jaato |
X-OpenRouter-Title header (display name) |
JAATO_OPENROUTER_APP_CATEGORIES |
cli-agent |
X-OpenRouter-Categories header (comma-separated marketplace categories) |
JAATO_OPENROUTER_REQUEST_TIMEOUT |
600 |
Byte-level per-request deadline in seconds (0 disables) |
JAATO_OPENROUTER_STREAM_IDLE_TIMEOUT |
300 |
Streaming payload idle deadline in seconds (0 disables) |
# .env
JAATO_PROVIDER=openrouter
JAATO_OPENROUTER_API_KEY=sk-or-your-key-here
JAATO_OPENROUTER_MODEL=anthropic/claude-sonnet-4-5
JAATO_OPENROUTER_HTTP_REFERER=https://myapp.example.com
JAATO_OPENROUTER_APP_TITLE=MyApp
anthropic/claude-sonnet-4-5
anthropic/claude-opus-4
openai/gpt-4o
openai/gpt-5-mini
google/gemini-2.5-flash
google/gemini-2.5-pro
meta-llama/llama-3.3-70b-instruct
deepseek/deepseek-r1
mistralai/mistral-large
openrouter/auto # OpenRouter picks per-request
Profile Configuration Layers
OpenRouter's profile config is organized into four named layers under
plugin_configs.openrouter. Each layer controls a distinct
aspect of the request.
Layer 1: Auth / Identity (top-level)
| Key | Type | Description |
|---|---|---|
api_key | str | Override env / stored credentials |
http_referer | str | HTTP-Referer header for app rankings |
app_title | str | X-OpenRouter-Title header |
app_categories | list[str] | X-OpenRouter-Categories header. Default: ["cli-agent"]. Pass [] to opt out of category attribution. |
extra_headers | dict[str,str] | Arbitrary additional headers. Primary use: OpenRouter's provider-specific beta header passthrough (e.g. x-anthropic-beta) |
Layer 2: api_params — Request Body Fields
| Key | Type | Description |
|---|---|---|
temperature | float | Sampling temperature (0.0–1.0) |
top_p | float | Nucleus sampling |
top_k | int | Top-K sampling |
max_tokens | int | Cap on response size |
models | list[str] | Cross-model fallback list; OpenRouter walks candidates on failure of the primary |
enable_thinking | bool | Extended reasoning (DeepSeek-R1, Claude, o-series) |
thinking_budget | int | Max reasoning tokens (→ reasoning.max_tokens) |
thinking_level | str | low/medium/high (→ reasoning.effort); wins over thinking_budget when both set |
cache_prompt | str/bool | "auto" (default) stamps cache_control breakpoints on system block and last tool definition for Anthropic/Gemini upstreams |
cache_ttl | str | "5m" (default) or "1h" (2x write premium, no mid-session cache miss) |
strict_tools | bool | Emit "strict": true alongside each tool definition for grammar-constrained tool-arg sampling on supported upstreams |
Layer 3: routing — Provider Routing
Opaque pass-through to OpenRouter's provider routing extension. Any field from the docs works. Common keys:
| Key | Values / Notes |
|---|---|
sort | "price", "throughput", "latency", or {"by":"...", "partition":"model"|"none"} |
data_collection | "allow" (default) | "deny" |
ignore | Provider slugs to skip (list) |
only | Provider allowlist (list); mutually exclusive with ignore |
order | Try these providers first, then fall back |
allow_fallbacks | false = fail rather than try alternatives |
quantizations | ["fp8"], ["int4"], etc. |
zdr | true = Zero Data Retention endpoints only |
Layer 4: framework_overrides — Escape Hatches
| Key | Type | Description |
|---|---|---|
context_length | int | Override catalog-reported context window |
base_url | str | Endpoint override |
connect_timeout | float | TCP + TLS handshake deadline, seconds (default 15) |
request_timeout | float | Byte-level deadline — httpx read/write/pool, seconds (default 600; 0 disables) |
stream_idle_timeout | float | Payload idle deadline for streaming turns, seconds (default 300; 0 disables). Raises StallTimeoutError — a retryable InfrastructureError — when a stream produces no chunks for this long. httpx cannot see this: OpenRouter’s : OPENROUTER PROCESSING keep-alive comments reset the byte clock while no chunk is yielded. |
name: openrouter-cascade
model: anthropic/claude-sonnet-4-5
provider: openrouter
plugins:
- cli
- file_edit
plugin_configs:
openrouter:
# Auth / identity
api_key: "sk-or-..."
app_categories: ["cli-agent"]
# API params
api_params:
temperature: 0.55
max_tokens: 8192
cache_prompt: "auto"
# Cross-model fallback list
models:
- anthropic/claude-sonnet-4-5
- openai/gpt-5-mini
- google/gemini-3-flash-preview
# Provider routing
routing:
sort: "throughput"
data_collection: "deny"
allow_fallbacks: true
# Framework overrides (rare)
framework_overrides:
context_length: 200000
stream_idle_timeout: 300 # payload idle deadline (0 disables)
plugin_configs:
openrouter:
api_params:
enable_thinking: true
thinking_level: "high" # wins over thinking_budget when both set
thinking_budget: 16384
plugin_configs:
openrouter:
extra_headers:
x-anthropic-beta: >-
fine-grained-tool-streaming-2025-05-14,
interleaved-thinking-2025-05-14
Model Catalog
list_models() queries GET /api/v1/models (no auth required)
and returns the full catalog. connect() looks up the selected model's
context_length from the catalog response and caches it.
Models use the vendor/model-name format. Use
openrouter/auto to let OpenRouter select the best model per request.
from shared.plugins.model_provider.openrouter.provider import (
OpenRouterProvider
)
from shared.plugins.model_provider.base import ProviderConfig
provider = OpenRouterProvider()
provider.initialize(ProviderConfig())
# List all models (no auth required)
models = provider.list_models()
print(len(models)) # 300+
# Filter by vendor
claude_models = provider.list_models(prefix="anthropic/")
# Connect and check context limit (auto-detected)
provider.connect("anthropic/claude-sonnet-4-5")
print(provider.get_context_limit()) # e.g. 200000
Error Handling
| Exception | Cause |
|---|---|
APIKeyNotFoundError |
No API key in env or stored credentials |
AuthenticationError |
API key rejected (401/403) |
RateLimitError |
Rate limit or credits exhausted (429) |
ModelNotFoundError |
Model not in OpenRouter catalog |
ContextLimitError |
Prompt exceeds the model's context window |
InfrastructureError |
Upstream server error (5xx) or connection failure |
from shared.plugins.model_provider.openrouter.errors import (
APIKeyNotFoundError,
AuthenticationError,
RateLimitError,
ContextLimitError,
)
try:
provider.initialize(config)
provider.connect("anthropic/claude-sonnet-4-5")
result = provider.complete(messages, tools=tools)
except APIKeyNotFoundError:
print("Set JAATO_OPENROUTER_API_KEY or run: openrouter-auth key ")
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
if e.retry_after:
print(f"Rate limited; retry in {e.retry_after}s")
except ContextLimitError:
print("Prompt too long for this model")