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 Nameopenrouter
Moduleshared.plugins.model_provider.openrouter
SDKopenai (OpenAI-compatible API)
AuthAPI key (sk-or-... from openrouter.ai/settings/keys)
Default endpointhttps://openrouter.ai/api/v1

Highlights

  • 300+ models — One API key for every major model provider
  • Auto modelopenrouter/auto picks the best available model per request
  • Cross-model fallbackapi_params.models list walks candidates on failure
  • Provider routing — Fine-grained control over which upstream hosts a request
  • Prompt caching — Explicit cache_control breakpoints for Anthropic/Gemini upstreams
  • Extended thinking — Reasoning extraction from DeepSeek-R1, Claude, o-series
  • Context auto-detect — Catalog queried at connect() for per-model context_length
Quick start
# 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
Python quick start
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
)
Auto model selection
# Let OpenRouter pick the best model
client.connect(None, None, "openrouter/auto")

Authentication

Get Your API Key

  1. Visit openrouter.ai/settings/keys
  2. Create a new key (sk-or-...)
  3. Set it as JAATO_OPENROUTER_API_KEY or use openrouter-auth key

Credential Priority

PrioritySource
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)
Environment variable
# .env file
JAATO_OPENROUTER_API_KEY=sk-or-your-key-here
JAATO_PROVIDER=openrouter
JAATO_OPENROUTER_MODEL=anthropic/claude-sonnet-4-5
Store credentials interactively
# 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

VariableDefaultDescription
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)
Full .env example
# .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
Popular model names
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)

KeyTypeDescription
api_keystrOverride env / stored credentials
http_refererstrHTTP-Referer header for app rankings
app_titlestrX-OpenRouter-Title header
app_categorieslist[str]X-OpenRouter-Categories header. Default: ["cli-agent"]. Pass [] to opt out of category attribution.
extra_headersdict[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

KeyTypeDescription
temperaturefloatSampling temperature (0.0–1.0)
top_pfloatNucleus sampling
top_kintTop-K sampling
max_tokensintCap on response size
modelslist[str]Cross-model fallback list; OpenRouter walks candidates on failure of the primary
enable_thinkingboolExtended reasoning (DeepSeek-R1, Claude, o-series)
thinking_budgetintMax reasoning tokens (→ reasoning.max_tokens)
thinking_levelstrlow/medium/high (→ reasoning.effort); wins over thinking_budget when both set
cache_promptstr/bool"auto" (default) stamps cache_control breakpoints on system block and last tool definition for Anthropic/Gemini upstreams
cache_ttlstr"5m" (default) or "1h" (2x write premium, no mid-session cache miss)
strict_toolsboolEmit "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:

KeyValues / Notes
sort"price", "throughput", "latency", or {"by":"...", "partition":"model"|"none"}
data_collection"allow" (default) | "deny"
ignoreProvider slugs to skip (list)
onlyProvider allowlist (list); mutually exclusive with ignore
orderTry these providers first, then fall back
allow_fallbacksfalse = fail rather than try alternatives
quantizations["fp8"], ["int4"], etc.
zdrtrue = Zero Data Retention endpoints only

Layer 4: framework_overrides — Escape Hatches

KeyTypeDescription
context_lengthintOverride catalog-reported context window
base_urlstrEndpoint override
connect_timeoutfloatTCP + TLS handshake deadline, seconds (default 15)
request_timeoutfloatByte-level deadline — httpx read/write/pool, seconds (default 600; 0 disables)
stream_idle_timeoutfloatPayload 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.
Full profile config example (YAML)
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)
Extended thinking example
plugin_configs:
  openrouter:
    api_params:
      enable_thinking: true
      thinking_level: "high"   # wins over thinking_budget when both set
      thinking_budget: 16384
Provider-specific beta headers
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.

Browse the catalog
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

ExceptionCause
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
Handle errors
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")