| 400 |
invalidtoolschema |
Tool definition has unsupported types |
Use basic JSON Sche
'Review OpenRouter integration for regulatory compliance (SOC2, GDPR,.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Compliance Review
Overview
OpenRouter is a proxy that routes requests to upstream providers (OpenAI, Anthropic, Google, etc.). Compliance depends on both OpenRouter's data handling and the selected provider's policies. Key considerations: data transit through OpenRouter infrastructure, provider-specific data retention, model selection for regulated data, and audit trail requirements.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK for provider-pinned requests and the automated checker in the references
curl and jq to run the Compliance Audit Script
- An existing OpenRouter integration to review — the audit script scans its source tree for hardcoded
sk-or-v1- keys
- Knowledge of which regimes apply (SOC2, GDPR, HIPAA) and how your data is classified
Instructions
- Work through the four areas of the Compliance Checklist —
datahandling, accesscontrol, audittrail, and providerselection — recording pass/fail per item.
- Classify each workload with the Data Classification Matrix (Public → Internal → Confidential → Restricted/PHI) to determine allowed providers and required controls.
- Pin regulated traffic per Provider Routing for Compliance: set
provider.order plus allow_fallbacks: False, then verify response.model confirms the approved provider actually served the request.
- For data-sovereignty requirements, configure BYOK per BYOK for Data Sovereignty so inference runs on your own provider account and OpenRouter only routes.
- Run the Compliance Audit Script: key label/limit check via
GET /api/v1/auth/key, a free-tier warning (free tier is unsuitable for regulated data), and the hardcoded-key scan.
- Document the data flow for auditors — client → OpenRouter (routing) → provider (inference) — per Enterprise Considerations.
Compliance Checklist
COMPLIANCE_CHECKLIST = {
"data_handling": [
"Verify OpenRouter does NOT train on your data (confirmed in their privacy policy)",
"Confirm provider-level data policies (OpenAI, Anthropic, Google each differ)",
"Document data flow: your app -> OpenRouter -> provider -> OpenRouter -> your app",
"Identify if prompts contain PII, PHI, or regulated data",
"Implement PII redaction before sending to API",
],
"access_control": [
"Use per-service API keys (not shared keys)",
"Set credit limits per key to isolate blast radius",
"
'Optimize context window usage for OpenRouter models to reduce cost and.
ReadWriteEditGrepBash(python3:*)Bash(node:*)Bash(curl:*)Bash(jq:*)
OpenRouter Context Optimization
Overview
OpenRouter models have varying context windows (4K to 1M+ tokens). Since pricing is per-token, stuffing unnecessary context wastes money and can degrade output quality. This skill covers context window lookup, token estimation, conversation trimming, chunking strategies, and Anthropic prompt caching for large contexts.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK and
requests for model-metadata lookup; tiktoken for exact token counting per the references
curl and jq to query context windows and pricing from /api/v1/models
- Node.js 18+ if you use the TypeScript context-budget calculator in the references
Instructions
- Run the Query Context Limits one-liner — it returns
context_length and prompt price per 1M tokens for each candidate model, so you know the real budget before writing code.
- Estimate input size (~4 characters per token, or exactly with
tiktoken per the references) and pick a model with selectmodelfor_context() from Context-Aware Model Selection — it applies an 80% safety margin and falls back through gpt-4o-mini (128K) → Claude 3.5 Sonnet (200K) → Gemini 2.0 Flash (1M).
- Keep long conversations inside budget with
trim_conversation() per Conversation Trimming: system prompt plus the last N messages, with a trim-marker note injected where history was dropped.
- For documents that exceed any window, use
chunkandprocess() per Chunking for Large Documents — 8,000-char chunks with 500-char overlap, analyzed independently at temperature=0 and then synthesized.
- Mark large static blocks with
cache_control: {"type": "ephemeral"} per Prompt Caching for Repeated Context to cut repeated input cost by 90% on Anthropic models.
- Monitor
prompttokens on every response (Enterprise Considerations) to catch context bloat before it becomes a 400 contextlength_exceeded.
Query Context Limits
# Check context window for specific models
curl -s https://openrouter.ai/api/v1/models | jq '[.data[] | select(
.id == "anthropic/claude-3.5-sonnet" or
.id == "openai/gpt-4o" or
.id == "google/gemini-2.0-flash-001" or
.id == "meta-llama/llama-3.1-70b-instruct"
) | {id, context_length, prompt_per_M: ((.pricing.prompt|tonumber)*1000000)}]'
Context-Aware Model Selection
import os, requests
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/a
'Implement cost controls for OpenRouter API usage.
ReadWriteEditGrepBash(python3:*)Bash(node:*)Bash(curl:*)Bash(jq:*)Bash(bc:*)
OpenRouter Cost Controls
Overview
OpenRouter provides per-key credit limits, a credit balance API, and per-generation cost queries. Combined with client-side budget middleware, you can enforce hard spending caps at the key level and soft caps in your application. This skill covers key-level limits, per-request cost tracking, budget enforcement middleware, and alert systems.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK and
requests; Node.js 18+ for the TypeScript per-request cost logger in the references
curl, jq, and bc for the balance check and the Budget Alert Script
- An OpenRouter management key exported as
OPENROUTERMGMTKEY if you provision per-key credit limits via POST /api/v1/keys
Instructions
- Query
GET /api/v1/auth/key per Check Credit Balance to see credits used, the key's limit, remaining balance, free-tier status, and rate limit.
- Provision scoped keys per Per-Key Credit Limits —
POST /api/v1/keys with a dollar limit (e.g. $50 for backend-prod) using the management key, then list keys to review usage against limits per service.
- Deploy the
BudgetEnforcer from Budget Enforcement Middleware: checkbudget() rejects any request whose pre-flight estimate exceeds the per-request or daily cap, and recordcost() books the exact spend from GET /api/v1/generation?id=.
- Cut unit cost with Cost-Saving Model Variants:
:floor picks the cheapest provider, :free costs nothing where available, and the task-based ROUTING table sends classification to gpt-4o-mini and simple Q&A to Llama 3.1 8B.
- Schedule the Budget Alert Script (cron) so a balance drop below the threshold fires an alert to Slack or PagerDuty.
- Set
max_tokens on every request and enable auto-topup per Enterprise Considerations to cap completion cost without risking an outage.
Check Credit Balance
# Current balance and limits
curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '{
credits_used: .data.usage,
credit_limit: .data.limit,
remaining: ((.data.limit // 0) - .data.usage),
is_free_tier: .data.is_free_tier,
rate_limit: .data.rate_limit
}'
Per-Key Credit Limits
import os, requests
MGMT_KEY = os.environ["OPENROUTER_MGMT_KEY"] # Management key
# Create a key with a $50 credit limit
resp = requests.post(
"https://openrouter.ai/api/v1/k
'Implement data privacy controls for OpenRouter API usage.
ReadWriteEditGrepBash(python3:*)
OpenRouter Data Privacy
Overview
When sending data through OpenRouter to upstream LLM providers, you're responsible for ensuring prompts don't leak PII inappropriately. OpenRouter itself does not train on API data, but each upstream provider has its own data retention and training policies. This skill covers PII detection and redaction, placeholder substitution, provider selection for privacy, and consent tracking.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK (
pip install openai) — every pattern in this skill is Python
- A sensitivity classification for your workloads (
public / standard / sensitive) so privacyawarecompletion() can route each one
- A list of providers your org approves for sensitive data, to plug into
provider.order with allow_fallbacks: False
Instructions
- Start with PII Detection and Redaction: adapt
PIIRULES (email, phone, SSN, credit card, sk-or-v1- API keys, IPs) to your data, then run scanand_redact() on representative inputs and review the findings for false positives.
- When downstream code needs the original values back, use the Placeholder Substitution Pattern instead of plain redaction —
PrivacyProxy.anonymize() before the API call, deanonymize() on the model's reply.
- Classify each workload and route it via Provider Selection for Privacy:
privacyawarecompletion() maps sensitivity to a model plus a provider block (order: ["Anthropic"], allow_fallbacks: False for standard/sensitive).
- Wire the Privacy Middleware into every call path, choosing
blockonpii=True (raise on detection) or auto_redact=True (scrub and continue) per workload.
- Apply the Enterprise Considerations: hash logged prompts (SHA-256) for GDPR right-to-erasure, and use BYOK for the most sensitive workloads.
PII Detection and Redaction
import re
from dataclasses import dataclass
from typing import Optional
@dataclass
class PiiScanResult:
clean_text: str
findings: list[dict]
has_pii: bool
PII_RULES = [
("email", r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'),
("phone", r'\b(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b'),
("ssn", r'\b\d{3}-\d{2}-\d{4}\b'),
("credit_card", r'\b(?:\d{4}[- ]?){3}\d{4}\b'),
("api_key", r'\bsk-or-v1-[a-zA-Z0-9]+\b'),
("ip_address", r'\b(?:\d{1,3}\.){3}\d{1,3}\b
'Create debug bundles for troubleshooting OpenRouter API issues.
ReadWriteEditGrepBash(curl:*)Bash(jq:*)Bash(python3:*)Bash(node:*)
OpenRouter Debug Bundle
Current State
!node --version 2>/dev/null || echo 'N/A'
!python3 --version 2>/dev/null || echo 'N/A'
Overview
When an OpenRouter request fails or returns unexpected results, you need a structured debug bundle: the exact request, response, headers, generation metadata, and environment info. The generation ID (gen-* prefix in response.id) is the key correlator -- it lets you look up exact cost, provider used, and latency via GET /api/v1/generation?id=.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
curl and jq for the quick-debug flow and the Common Debug Checks
- Python 3.8+ with the
openai and requests packages for the Debug Bundle Generator
- A failing or suspect request you can reproduce — its
gen-* generation ID is what everything else correlates on
Instructions
- Rule out environment problems first with the Common Debug Checks: verify the key via
/api/v1/auth/key, confirm the model exists in /api/v1/models, and check status.openrouter.ai.
- Reproduce the failure with the Quick Debug: curl command —
curl -v ... | tee /tmp/openrouter-debug.txt captures request headers, response headers, and body in one transcript.
- Extract the generation ID (
jq -r '.id') and query GET /api/v1/generation?id=$GENID to get exact cost, token counts, generationtime, and provider_name.
- For failures inside an application, call
debugrequest() from the Python Debug Bundle Generator to capture the same request/response/error/latency/environment data as a DebugBundle and save it with bundle.save("debugbundle.json").
- Match the symptoms against the Error Handling table (missing generation ID, 502/503,
modelnotfound, slow TTFT).
- Before sharing a bundle, redact API keys per Enterprise Considerations (
sk-or-v1-... -> sk-or-v1-[REDACTED]) and include the generation ID in any OpenRouter support request.
Quick Debug: curl
# Send a request and capture full response with headers
curl -v https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "HTTP-Referer: https://my-app.com" \
-H "X-Title: debug-test" \
-d '{
"model": "openai/gpt-4o-mini",
"messages": [{"role": "user", "content": "Say hel
'Configure automatic model fallbacks for high availability on OpenRouter.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Fallback Config
Overview
OpenRouter supports native model fallbacks: pass multiple model IDs and OpenRouter tries each in order until one succeeds. You can also use provider.order to control which provider serves a specific model. This skill covers native fallbacks, provider routing, client-side fallback chains, and timeout configuration.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK (
pip install openai) for the fallback patterns; curl and jq for the Testing Fallbacks step
- A ranked list of acceptable models for your workload, matched by capability (tool calling, vision, context length) so a fallback never silently drops a feature you depend on
Instructions
- Start with Native Model Fallback (Server-Side): pass a
models array plus route: "fallback" in extra_body and let OpenRouter try each model in order.
- Log
response.model after every call — it tells you which model actually served the request, which is how you detect that a fallback fired.
- If you need the same model from specific vendors (e.g., Claude via Anthropic direct vs AWS Bedrock), use Provider Fallback with
provider.order and allow_fallbacks.
- For per-model timeouts and custom error handling, implement the Client-Side Fallback Chain:
resilientcompletion() walks FALLBACKCHAIN (primary → secondary → budget-fallback → last-resort) and raises once every entry fails.
- Pick chains per feature with Fallback with Capability Matching —
CAPABILITY_CHAINS keeps tool-calling, vision, long-context, and budget workloads on models that actually support them.
- Verify the behavior with Testing Fallbacks: send the curl request with an invalid primary model and confirm the response comes back from
openai/gpt-4o-mini.
Native Model Fallback (Server-Side)
import os
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
# Pass multiple models -- OpenRouter tries each in order
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet", # Primary (used for param validation)
messages=[{"role": "user", "content": "Explain recursion"}],
max_tokens=500,
extra_body={
"models": [
"anthropic/claude-3.5-sonnet",
"
'Implement function/tool calling with OpenRouter models.
ReadWriteEditGrepBash(python3:*)Bash(node:*)
OpenRouter Function Calling
Overview
OpenRouter supports OpenAI-compatible tool/function calling across multiple providers. Define tools as JSON Schema, send them with your request, and the model returns structured tool_calls instead of free text. This works with GPT-4o, Claude 3.5, Gemini, and other tool-capable models via the same API. The key difference from direct provider APIs: OpenRouter normalizes the tool calling interface, so the same code works across providers.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ or Node.js 18+ with the OpenAI SDK (
pip install openai / npm install openai)
- A tool-capable model — check the Model Compatibility table below or query
/api/v1/models (e.g., openai/gpt-4o, anthropic/claude-3.5-sonnet)
- Real function implementations to dispatch tool calls to (the
executetool() dispatcher below stubs getweather and search_database)
Instructions
- Pick a model from the Model Compatibility table that supports the features you need (tool calling, JSON mode, parallel tools).
- Define your tools as JSON Schema per Basic Tool Calling and send them with
tool_choice="auto" (or "required" to force a call, or a specific function name).
- Read
response.choices[0].message.tool_calls — each entry carries function.name and JSON-encoded function.arguments to parse with json.loads().
- For agents, wire the Multi-Turn Tool Loop: append the assistant message, execute each tool via
executetool(), append role: "tool" results keyed by toolcallid, and loop until the model returns plain text (bounded by maxrounds).
- Use the TypeScript Tool Calling section for the identical flow in Node — same schema, same
tool_calls shape.
- When you only need structured data (no function execution), skip tools and use Structured Output (JSON Mode) with
responseformat={"type": "jsonobject"}.
- Handle failures per the Error Handling table: force
tool_choice: "required" for extraction pipelines and validate arguments server-side before executing.
Basic Tool Calling
import os, json
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
# Define tools with JSON Schema
tools = [
{
"ty
'Send your first OpenRouter API request and understand the response.
ReadWriteEditGrepBash(curl:*)Bash(python3:*)Bash(node:*)Bash(jq:*)
OpenRouter Hello World
Overview
Send a minimal chat completion request through OpenRouter, understand the response format, try different models, and verify the full round-trip works. All requests go to the single endpoint POST https://openrouter.ai/api/v1/chat/completions.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
curl and jq for the command-line request, or Python 3.8+ / Node.js 18+ with the OpenAI SDK (pip install openai / npm install openai)
- A free-tier model works for every step here (no credits required for
:free models)
Instructions
- Export your key:
export OPENROUTERAPIKEY="sk-or-v1-...".
- Send the minimal cURL request below and confirm you get a
choices[0].message.content back.
- Read the Response Format section to identify the four key fields (
id, model, usage, finish_reason).
- Repeat the same request from your app language using the Python or TypeScript example.
- Swap model IDs per Try Different Models to confirm multi-model access works with the same code.
- Query
GET /api/v1/generation?id=gen-... per Check Generation Cost to verify cost tracking on the request you just sent.
Minimal Request (cURL)
curl -s https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemma-2-9b-it:free",
"messages": [{"role": "user", "content": "Say hello in three languages"}],
"max_tokens": 100
}' | jq .
Response Format
{
"id": "gen-abc123xyz",
"model": "google/gemma-2-9b-it:free",
"object": "chat.completion",
"created": 1711234567,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! Bonjour! Hola!"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 8,
"total_tokens": 20
}
}
Key fields:
id (gen-...) -- use this to query generation stats via GET /api/v1/generation?id=gen-abc123xyz
model -- confirms which model actually served the request
usage -- token counts for cost calculation<
'Set up OpenRouter API authentication and configure API keys.
ReadWriteEditGrepBash(python3:*)Bash(node:*)Bash(pip:*)Bash(npm:*)
OpenRouter Install & Auth
Overview
Set up OpenRouter API credentials, configure the OpenAI-compatible client, verify authentication, and check credit balance. OpenRouter keys start with sk-or-v1- and authenticate against https://openrouter.ai/api/v1.
Prerequisites
- OpenRouter account (free at openrouter.ai)
- Python 3.8+ or Node.js 18+
- OpenAI SDK (
pip install openai or npm install openai)
Instructions
1. Generate an API Key
- Go to openrouter.ai/keys
- Click Create Key, name it (e.g.,
my-app-dev)
- Copy the
sk-or-v1-... value immediately (shown only once)
- Optionally set a credit limit on the key for spend control
2. Configure Environment
# .env file (add .env to .gitignore!)
OPENROUTER_API_KEY=sk-or-v1-your-key-here
# Or export directly
export OPENROUTER_API_KEY="sk-or-v1-your-key-here"
3. Initialize the Client
Python:
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={
"HTTP-Referer": "https://your-app.com", # For analytics attribution
"X-Title": "Your App Name", # Shows in dashboard
},
)
TypeScript:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
"HTTP-Referer": "https://your-app.com",
"X-Title": "Your App Name",
},
});
4. Verify Authentication
# Quick auth + credit check
import requests
resp = requests.get(
"https://openrouter.ai/api/v1/auth/key",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
)
data = resp.json()["data"]
print(f"Key: {data['label']}")
print(f"Credits used: ${data['usage']:.4f}")
print(f"Credit limit: ${data.get('limit', 'unlimited')}")
print(f"Free tier: {data['is_free_tier']}")
print(f"Rate limit: {data['rate_limit']['requests']} req / {data['rate_limit']['interval']}")
5. Send a Test Request
response = client.chat.completions.create(
model="google/gemma-2-9b-it:free", # Free model for testing
messages=[{"role": "use
'Avoid common OpenRouter integration mistakes and gotchas.
ReadWriteEditGrepBash(python3:*)
OpenRouter Known Pitfalls
Overview
A curated list of real-world mistakes developers make when integrating OpenRouter, each with the specific API behavior that causes the problem and the exact fix. These are not theoretical -- they come from production incidents and support requests.
Prerequisites
- An existing (or in-progress) OpenRouter integration to audit against the 10 pitfalls below
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK (
pip install openai) to run the validation snippets (e.g., the startup check against /api/v1/models)
- Grep access to the codebase to hunt hardcoded
sk-or-v1- keys and scattered model IDs
Instructions
- Audit request format first: every model ID uses the
provider/model form (Pitfall 1), and model IDs live in one MODELS config validated against /api/v1/models at startup instead of being scattered through the code (Pitfall 3).
- Check cost controls:
max_tokens is set on every request (Pitfall 2) and no :free models are used in production, where the 50-1000 req/day limits will 429 you (Pitfall 5).
- Review routing: sensitive-data requests pin
provider.order with allow_fallbacks: False (Pitfall 4), and response.model is logged on every call to catch unexpected fallbacks (Pitfall 6).
- Inspect client hygiene: one shared client instance with connection pooling (Pitfall 7) configured with
timeout and max_retries (Pitfall 9).
- Sweep for secrets: grep for hardcoded
sk-or-v1- strings and move any hits to env vars or a secrets manager, rotating the exposed keys (Pitfall 8).
- Verify caching only stores deterministic
temperature=0 responses (Pitfall 10).
- Finish by walking the Quick Checklist (
PITFALL_CHECKLIST) top to bottom — it condenses all 10 pitfalls into a code-review pass.
Pitfall 1: Missing Provider Prefix on Model ID
# WRONG: Model ID without provider prefix
response = client.chat.completions.create(
model="gpt-4o", # ← Will fail with 400 "model not found"
messages=[{"role": "user", "content": "Hello"}],
)
# RIGHT: Always include provider/model format
response = client.chat.completions.create(
model="openai/gpt-4o", # ← Correct
messages=[{"role": "user", "content": "Hello"}],
)
Pitfall 2: No max_tokens = Runaway Costs
# WRONG: No max_tokens -- model may generate 4000+ tokens
response = client.chat.complet
'Distribute OpenRouter requests across multiple keys and models for high.
ReadWriteEditGrepBash(python3:*)
OpenRouter Load Balancing
Overview
A single OpenRouter API key has rate limits (requests/minute and tokens/minute). To scale beyond those limits, distribute requests across multiple keys. OpenRouter also provides server-side load balancing via provider routing and the :nitro variant for low-latency inference. This skill covers multi-key rotation, health-based routing, circuit breakers, and concurrent request patterns.
Prerequisites
- Two or more OpenRouter API keys exported as
OPENROUTERKEY1, OPENROUTERKEY2, OPENROUTERKEY3 so the KeyPool has keys to rotate — see the openrouter-install-auth skill for creating and exporting keys
OPENROUTERAPIKEY exported for the single-key concurrent-processing pattern
- Python 3.8+ with the OpenAI SDK and
requests (pip install openai requests) — the concurrent example uses AsyncOpenAI from the same package
- Adequate credits on every key in the pool; per-key quota is visible via
GET /api/v1/auth/key
Instructions
- Export your pool keys and build the
KeyPool from Multi-Key Round Robin — it round-robins across keys, trips a circuit breaker after 3 consecutive errors, and auto-recovers a key after a 60s cooldown.
- Send traffic through
balancedcompletion(): on RateLimitError it calls pool.markerror(key) and retries with the next healthy key.
- For batch workloads, use
parallelcompletions() from Concurrent Request Processing — an asyncio.Semaphore (maxconcurrent=3-5) caps in-flight requests against a single key.
- Layer on server-side distribution per Provider-Level Load Balancing: pass
extrabody={"provider": {"order": [...], "allowfallbacks": True}} so OpenRouter spreads the same model across Anthropic, AWS Bedrock, and GCP Vertex.
- Monitor quota per key with
checkratelimits() (GET /api/v1/auth/key) from Rate Limit Awareness, and when 429s hit all keys simultaneously, apply the fixes in Error Handling (more keys, request queuing).
Multi-Key Round Robin
import os, itertools, time, logging
from openai import OpenAI, RateLimitError
from dataclasses import dataclass, field
log = logging.getLogger("openrouter.lb")
@dataclass
class KeyPool:
"""Round-robin API key pool with health tracking."""
keys: list[str]
_cycle: itertools.cycle = field(init=False, repr=False)
_health: dict[str, dict] = field(init=False, default_factory=dict)
def __post_init__(self):
self._cycle = itertools.cycle(self.keys)
self._health = {k: {"errors": 0, &qu
'Monitor OpenRouter model availability and implement health checks.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Model Availability
Overview
OpenRouter's /api/v1/models endpoint is the source of truth for model availability. Models can be temporarily unavailable, have degraded performance, or be permanently removed. This skill covers querying model status, building health probes, tracking availability over time, and automating failover.
Prerequisites
- An OpenRouter API key exported as
OPENROUTERAPIKEY for live probes (the catalog query itself needs no auth) — see the openrouter-install-auth skill for setup
curl and jq for the catalog status queries and the cron monitoring script
- Python 3.8+ with the OpenAI SDK and
requests (pip install openai requests) for the health-check service
- A small credit balance — each
max_tokens: 1 probe costs roughly $0.0001
Instructions
- Confirm your models exist in the catalog with
curl -s https://openrouter.ai/api/v1/models | jq ... per Query Model Status — pull context_length and per-million pricing without spending any tokens.
- For a zero-cost existence check inside code, use
checkmodelexists() from Catalog-Based Availability Check; on a miss it calls find_similar() to suggest same-provider replacements.
- Probe live health with
probemodel() from Health Check Service — a maxtokens: 1 request that returns a HealthStatus with available, latencyms, and checkedat.
- Sweep your critical set with
checkcriticalmodels(), which logs OK/FAIL plus latency per model.
- Automate via the Availability Monitoring Script as a
/5 * cron job appending timestamped status lines to /var/log/openrouter-health.log.
- Tune alerting per Error Handling — require 2-3 consecutive failures before marking a model down to avoid false positives.
Query Model Status
# Check if specific models exist and their status
curl -s https://openrouter.ai/api/v1/models | jq '[.data[] | select(
.id == "anthropic/claude-3.5-sonnet" or
.id == "openai/gpt-4o" or
.id == "openai/gpt-4o-mini"
) | {
id,
context_length,
prompt_per_M: ((.pricing.prompt | tonumber) * 1000000),
completion_per_M: ((.pricing.completion | tonumber) * 1000000)
}]'
# List all available models (just IDs)
curl -s https://openrouter.ai/api/v1/models | jq '[.data[].id] | sort'
# Count models by provider
curl -s https://openrouter.ai/api/v1/models | jq '[.data[].id | split("/")[0]] | group_by(.) | map({provider: .[0], count: length}) | sort_by(-.count)'
Health Check
'Query, filter, and select from OpenRouter''s 400+ model catalog.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Model Catalog
Overview
Query the GET /api/v1/models endpoint to browse 400+ models, filter by capabilities, compare pricing, and check provider endpoints. No API key required for the models endpoint.
Prerequisites
curl and jq for the command-line catalog queries — GET /api/v1/models itself requires no auth
- An OpenRouter API key exported as
OPENROUTERAPIKEY only for the Special Routers completion example — see the openrouter-install-auth skill for setup
- Python 3.8+ with
requests for filtering, plus the OpenAI SDK for the openrouter/auto example (pip install requests openai)
Instructions
- List the catalog per List All Models:
curl -s https://openrouter.ai/api/v1/models | jq '.data | length'; add ?supported_parameters=tools to filter to tool-calling models.
- Read Model Object Shape to interpret each entry —
pricing.prompt/pricing.completion are per token (multiply by 1M for readable rates), plus contextlength, topprovider.maxcompletiontokens, and architecture.modality.
- Filter programmatically per Python: Query and Filter — free models, tool-calling models, cheapest paid models sorted by prompt price, and 128K+ context models.
- Compare per-provider pricing and quantization for a single model via
GET /api/v1/models/{id}/endpoints per List Providers for a Model.
- Pick behavior with a suffix per Model Variants (
:free, :nitro, :floor, :extended, :thinking), or delegate selection entirely to openrouter/auto per Special Routers.
- Sanity-check choices against the Popular Model Quick Reference, but always verify live pricing via
/api/v1/models — prices change frequently.
List All Models
# Full catalog (no auth required)
curl -s https://openrouter.ai/api/v1/models | jq '.data | length'
# → 400+
# Filter to text output models only
curl -s "https://openrouter.ai/api/v1/models?supported_parameters=tools" | jq '.data | length'
Model Object Shape
{
"id": "anthropic/claude-3.5-sonnet",
"name": "Claude 3.5 Sonnet",
"description": "Anthropic's most intelligent model...",
"context_length": 200000,
"pricing": {
"prompt": "0.000003",
"completion": "0.000015",
"image": "0.0048",
"request": "0"
},
"top_provider": {
"context_length": 200000,
"max_complet
'Implement intelligent model routing to optimize cost, quality, and latency.
ReadWriteEditGrepBash(python3:*)
OpenRouter Model Routing
Overview
OpenRouter gives you access to 100+ models through one API. The key to cost efficiency is routing each request to the right model based on task complexity, required capabilities, cost budget, and latency requirements. This skill covers task-based routing, complexity classification, cost-aware selection, and OpenRouter's native routing features.
Prerequisites
- An OpenRouter API key exported as
OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK and
requests (pip install openai requests)
- A rough inventory of your task mix (classification, summarization, code generation, deep reasoning, ...) to seed the
TASK_ROUTING table
- Credits sized for the tiers you route to — the premium tier (
openai/o1) runs $15/$60 per 1M tokens, 250x the budget tier
Instructions
- Define your tiers per Task-Based Router: the
MODELS dict (free → budget → mid → standard → premium) and the TASKROUTING map, then send requests through routerequest(), which returns content, the serving model, tier, and token count.
- When callers can't label tasks, switch to the Complexity-Based Auto-Router —
classifycomplexity() scores word count, code, reasoning, and math markers to pick a tier inside autoroute().
- Add resilience per OpenRouter Native Routing:
extra_body={"models": [...], "route": "fallback"} tries models in order, provider.order controls which provider serves, and the :floor variant picks the cheapest provider automatically.
- Keep pricing current per Cost-Aware Router —
getmodelpricing() pulls live per-1M rates from GET /api/v1/models, and cheapestmodelfor_task() selects under context/tooling constraints.
- Log every routing decision (task type, tier, model, cost) and tune per Error Handling and Enterprise Considerations — escalate the tier on quality regressions and cap per-request cost with
max_tokens.
Task-Based Router
import os, re
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
# Model tiers by cost and capability
MODELS = {
"free": "google/gemma-2-9b-it:free", # $0/0 — testing only
"budget": "meta-llama/llama-3.1-8b-instruct", # $0.06/$0.06 per 1M
"mid": "openai/gpt-4o-mini",
'Use multiple AI providers (OpenAI, Anthropic, Google, Meta) through.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Multi-Provider
Overview
OpenRouter's unified API lets you access models from OpenAI, Anthropic, Google, Meta, Mistral, and others with a single API key and endpoint. Model IDs use provider/model-name format. The same OpenAI SDK code works for any provider by simply changing the model ID. This skill covers provider comparison, cross-provider routing, feature normalization, and BYOK (Bring Your Own Key).
Prerequisites
- A single OpenRouter API key exported as
OPENROUTERAPIKEY — it covers every provider (OpenAI, Anthropic, Google, Meta, Mistral); see the openrouter-install-auth skill for setup
curl and jq for the provider-landscape query
- Python 3.8+ with the OpenAI SDK (
pip install openai)
- For BYOK only: your own provider API key (e.g. an OpenAI key) added in the OpenRouter dashboard under Settings > Integrations > Add Provider Key
Instructions
- Survey what's on offer per Provider Landscape:
curl -s https://openrouter.ai/api/v1/models | jq ... groups model IDs by their provider/ prefix and sorts by model count.
- Benchmark candidates with
compare_models() from Cross-Provider Comparison — the same prompt at temperature=0 across Anthropic, OpenAI, Google, and Meta, capturing latency, tokens, and the actual serving endpoint (response.model).
- Shortlist by task using the Provider Strength Matrix — Anthropic for analysis/long context, OpenAI for code and tool calling, Google for multimodal and 1M context, Meta for budget work, Mistral for European data residency.
- Pin or fail over per Provider-Specific Routing:
provider.order with allowfallbacks: False forces one provider (e.g. for regulated data); allowfallbacks: True fails across providers such as Anthropic → AWS Bedrock.
- For high-volume production, configure BYOK — requests route to your own provider key with the first 1M requests/month free, then 5% of normal provider cost.
- Smooth capability gaps with
normalizedcompletion() per Feature Normalization — JSON mode uses responseformat natively on openai/ models and a system-prompt instruction elsewhere.
Provider Landscape
# List all providers and their model counts
curl -s https://openrouter.ai/api/v1/models | jq '
[.data[].id | split("/")[0]] |
group_by(.) | map({provider: .[0], models: length}) |
sort_by(-.models)'
Cross-Provider Comparison
import os, time, json
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={&quo
'Migrate from OpenAI to OpenRouter with minimal code changes.
ReadWriteEditGrepBash(python3:*)Bash(node:*)
OpenRouter OpenAI Compatibility
Overview
OpenRouter implements the OpenAI Chat Completions API specification (/v1/chat/completions). Existing OpenAI SDK code works with OpenRouter by changing two values: baseurl and apikey. This gives you access to 400+ models from all providers through the same SDK interface.
Prerequisites
- An existing OpenAI SDK integration to migrate — Python or TypeScript code calling
chat.completions.create
- An OpenRouter API key exported as
OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the
openai package, or Node.js 18+ with the openai npm package — the same SDK you already use, no new dependency
- Optionally keep
OPENAIAPIKEY exported too, so the Dual-Provider Pattern can switch back to direct OpenAI
Instructions
- Apply The Two-Line Migration: point
baseurl at https://openrouter.ai/api/v1 and swap apikey to OPENROUTERAPIKEY; optionally add the HTTP-Referer / X-Title headers for app attribution.
- Prefix every model string per Model ID Mapping —
gpt-4o becomes openai/gpt-4o, o1 becomes openai/o1 — and try a non-OpenAI model (anthropic/claude-3.5-sonnet) through the same client.
- Confirm your feature usage against What Works Identically (streaming,
tools, JSON mode, stop, n) and adjust per What Differs — remove the organization param, plan around limited embeddings, and check logprobs support per model via /api/v1/models.
- Layer in OpenRouter-Only Features through
extra_body: ordered fallback model lists with "route": "fallback", provider preferences with sort: "price", or the plugins: [{"id": "web"}] web-search plugin.
- Keep the migration reversible with the Dual-Provider Pattern —
createclient() switches between direct OpenAI and OpenRouter off the LLMPROVIDER environment variable.
The Two-Line Migration
Python (Before)
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"]) # OpenAI direct
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)
Python (After)
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1", # Changed
api_key=os.environ["OPENROUTER_API_KEY"],
'Optimize OpenRouter request latency and throughput.
ReadWriteEditGrepBash(python3:*)
OpenRouter Performance Tuning
Overview
OpenRouter adds minimal overhead (~50-100ms) to direct provider calls. Most latency comes from the upstream model. Key levers: model selection (smaller = faster), streaming (lower TTFT), parallel requests, prompt size reduction, and provider routing to faster infrastructure. This skill covers benchmarking, streaming optimization, concurrent processing, and connection tuning.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK (
openai package) — the examples use both the sync OpenAI client and AsyncOpenAI for parallel processing
- Credits on the key if you benchmark paid models like
anthropic/claude-3.5-sonnet; a :free model is enough to validate the benchmark harness itself
HTTP-Referer / X-Title header values for your app (set in every client constructor here)
Instructions
- Establish a baseline: run
benchmark_model() from Benchmark Latency against your candidate models (e.g. openai/gpt-4o-mini vs anthropic/claude-3.5-sonnet) and record p50/p95.
- Check the results against the Model Speed Tiers table to confirm each candidate sits in the right tier for your latency budget (200-500ms TTFT fastest tier; 5-30s for reasoning models).
- Switch user-facing paths to
streamcompletion() per Streaming for Lower TTFT and verify ttftms drops (typically 2-10x).
- Move batch workloads to
parallelcompletions() per Parallel Request Processing, capping concurrency with asyncio.Semaphore (maxconcurrent=5-10).
- Apply Connection Optimization — one shared client with
timeout=30.0 and max_retries=2 instead of a new client per request.
- Work through the Performance Optimization Checklist (set
max_tokens, shrink prompts, consider :nitro variants and provider routing), then re-run the benchmark to quantify each change.
Benchmark Latency
import os, time, statistics
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
def benchmark_model(model: str, prompt: str = "Say hello", n: int = 5) -> dict:
"""Benchmark a model's latency over N requests."""
latencies = []
for _ in range(n):
start = time.monotonic()
response = client.chat.completions.creat
'Understand OpenRouter pricing, calculate costs, and optimize spend.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Pricing Basics
Overview
OpenRouter charges per token with separate rates for prompt (input) and completion (output) tokens. Prices are listed per token in the models API (multiply by 1M for per-million rates). Credits are prepaid with a 5.5% processing fee ($0.80 minimum). Free models are available for testing and low-volume use.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
curl and jq for the model-pricing and credit-balance queries
- Python 3.8+ with the OpenAI SDK plus the
requests package for the cost-calculation and generation-endpoint snippets
- Prepaid credits for paid models — the public models/pricing endpoint needs no auth, but real completions require credits or a
:free model
Instructions
- Read How Pricing Works: prepaid credits (5.5% fee, $0.80 minimum) are drawn down per request as
(prompttokens promptrate) + (completiontokens completionrate).
- Query per-token rates via
GET /api/v1/models per Query Model Pricing, and place candidate models in the Cost Tiers table (free → premium).
- Estimate spend before committing: run
estimate_cost() from Calculate Request Cost with your expected prompt/completion token counts.
- After sending real traffic, fetch the exact charge with
GET /api/v1/generation?id= per Track Actual Cost Per Request.
- Watch the balance via
GET /api/v1/auth/key per Check Credit Balance, and enable auto-topup for production keys.
- Cut costs with the
:floor and :free variants per Save Money with Variants, and check Special Pricing for reasoning tokens, image inputs, per-request fees, and BYOK.
How Pricing Works
- Buy credits at openrouter.ai/credits (5.5% fee, $0.80 minimum)
- Each request deducts
(prompttokens promptrate) + (completiontokens completionrate)
- Check balance via
GET /api/v1/auth/key or the dashboard
- Auto-topup is available to prevent service interruption
Query Model Pricing
# Get pricing for all models
curl -s https://openrouter.ai/api/v1/models | jq '.data[] | select(.id == "anthropic/claude-3.5-sonnet") | {
id: .id,
prompt_per_M: ((.pricing.prompt | tonumber) * 1000000),
completion_per_M: ((.pricing.completion | tonumber) * 1000000),
context: .context_length
}'
# → { "id": "anthropic/claude-3.5-sonnet", "prompt_per_M": 3, "completion_per_M"
'Validate production readiness of your OpenRouter integration.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Production Checklist
Overview
A comprehensive production readiness checklist for OpenRouter integrations covering security, reliability, observability, cost management, and operational procedures. Each item includes the specific API endpoint or configuration needed to verify compliance.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- A working OpenRouter integration ready to launch — this skill validates an existing integration, it doesn't build one
curl and jq for the pre-launch validation script and the auth/credit-limit checks
- Python 3.8+ if you turn the checklist dicts (
SECURITY, RELIABILITY, OBSERVABILITY) into automated CI checks
- A secret scanner in CI (gitleaks or trufflehog) — the security checklist verifies its presence
Instructions
- Work through the Security Checklist: keys in a secrets manager, 90-day rotation, per-key credit limits (
GET /api/v1/auth/key → .data.limit), secret scanning in CI, HTTPS-only endpoints.
- Verify the Reliability Checklist: a fallback chain (
models array + route: "fallback"), SDK maxretries=3 with built-in backoff, timeout=30.0, a circuit breaker on the primary model, and maxtokens on every request.
- Confirm the Observability Checklist: structured logs carrying
generation_id/model/latency/tokens/cost, error-rate alerts (>5% over 5 min), P50/P95 latency per model, daily cost tracking via GET /api/v1/generation?id=, and credit-balance alerts.
- Run the Pre-Launch Validation Script — it exercises auth, credit limit, primary-model availability, a live test request, and a hardcoded-key scan.
- Fix every FAIL and re-run until the script prints
READY FOR PRODUCTION, then wire it into CI as a pre-deploy gate per Enterprise Considerations.
Security Checklist
SECURITY = {
"api_key_storage": {
"check": "API keys stored in secrets manager (not .env files on disk)",
"verify": "grep -r 'sk-or-v1-' --include='*.py' --include='*.ts' . | grep -v node_modules",
"pass": "Zero matches",
},
"key_rotation": {
"check": "Keys rotated on 90-day schedule",
"verify": "Check key creation dates in OpenRouter dashboard",
"api": "GET /api/v1/keys (management key)",
},
"credit_limits": {
"check": "Per-key credit limits set to isolate blast radius",
'Understand and handle OpenRouter rate limits.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Rate Limits
Overview
OpenRouter rate limits are per-key, not per-account. Free tier keys get lower limits; paid keys get higher limits that scale with credit balance. The OpenAI SDK has built-in retry with exponential backoff for 429 responses. Check your current limits via GET /api/v1/auth/key. Rate limit headers are returned on every response.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
curl and jq for querying your key's limits from GET /api/v1/auth/key
- Python 3.8+ with the OpenAI SDK (sync
OpenAI and AsyncOpenAI) plus the requests package for reading rate-limit headers directly
- Awareness of your tier: free keys get 20 req/10s, keys with any credits 200 req/10s (see Rate Limit Tiers)
Instructions
- Query your key's limits via
GET /api/v1/auth/key per Check Your Rate Limits — note ratelimit.requests and ratelimit.interval.
- Place yourself in the Rate Limit Tiers table, remembering free models carry separate daily caps (50 req/day free, 1000 req/day with $10+ credits).
- Inspect live headroom with
checkrateheaders() per Read Rate Limit Headers — watch x-ratelimit-remaining and retry-after.
- Configure SDK retries per Retry Strategy with OpenAI SDK:
max_retries=5, timeout=60.0; the SDK catches 429s and backs off with jitter automatically.
- Add the client-side
TokenBucket limiter from Custom Rate Limiter, set below the server limit (e.g. 150 per 10s under a 200/10s cap) so you rarely hit 429 at all.
- For bulk jobs, use
batchwithrate_limit() per Batch Processing with Rate Awareness — staggered starts plus semaphore-capped concurrency instead of bursts.
Check Your Rate Limits
# Query current rate limit configuration for your key
curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY" | jq '{
label: .data.label,
rate_limit: .data.rate_limit,
is_free_tier: .data.is_free_tier,
credits_used: .data.usage,
credit_limit: .data.limit
}'
# Example output:
# {
# "label": "my-app-prod",
# "rate_limit": {"requests": 200, "interval": "10s"},
# "is_free_tier": false,
# "credits_used": 12.34,
# "credit_limit": 100
# }
Rate Limit Tiers
| Tier |
Requests |
Interval |
Who |
| Free (no credits) |
20 |
10s |
Ne
'Design production architectures using OpenRouter as the LLM gateway.
ReadWriteEditGrepBash(python3:*)
OpenRouter Reference Architecture
Overview
OpenRouter serves as a unified LLM gateway, abstracting provider complexity. A production architecture wraps it with caching, rate limiting, cost controls, observability, and async processing. This skill provides three reference architectures: simple (single service), standard (microservice), and enterprise (event-driven).
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK; FastAPI + Pydantic for Architecture 2's AI service, and a Redis instance (with the
redis package) for Architecture 2's cache and Architecture 3's queue/results store
- SQLite or Postgres if you implement Architecture 2's budget enforcer
- Your scale numbers — team size, requests/day, and latency needs drive the decision in Choosing an Architecture
Instructions
- Score your system against the Choosing an Architecture table: team size, requests/day, latency needs, budget-tracking granularity, failure handling, observability.
- Start with Architecture 1 (Simple): one shared client (
max_retries=3, timeout=30.0) behind the logging complete() wrapper.
- When you need task routing, caching, and per-user budgets, move to Architecture 2 (Standard): a FastAPI
/v1/complete endpoint with the ROUTING_TABLE, cache-first lookup, budget check, and a fallback chain (models + route: "fallback").
- At 100K+ requests/day or mixed sync/async workloads, adopt Architecture 3 (Enterprise): queue (Redis/SQS) → auto-scaling workers running
worker_loop() → results store, with OTEL metrics feeding dashboards and alerts.
- Whichever tier you land on, route every call through the same OpenRouter client wrapper per Enterprise Considerations — consistent logging, cost tracking, and no budget bypass.
Architecture 1: Simple (Single Service)
┌─────────────┐ ┌──────────────────────────┐ ┌──────────────┐
│ Your App │────▶│ OpenRouter Client │────▶│ OpenRouter │
│ │ │ - Retry (SDK built-in) │ │ /api/v1 │
│ │◀────│ - Cost tracking │◀────│ │
│ │ │ - Structured logging │ └──────────────┘
└─────────────┘ └──────────────────────────┘
import os, logging
from openai import OpenAI
log = logging.getLogger("llm")
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
max_retries=3,
timeout=30.0,
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
'Define custom routing rules for OpenRouter requests based on user tier,.
ReadWriteEditGrepBash(python3:*)
OpenRouter Routing Rules
Overview
Beyond simple task-based model selection, production systems need configurable routing rules that consider user tier, cost budget, time of day, model availability, and feature requirements. This skill covers building a rules engine for OpenRouter model selection with config-driven rules, dynamic conditions, and override capabilities.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK — the rules engine itself is stdlib (
dataclasses, json, random) layered on top
- Per-request metadata available in your app: user tier, task type, remaining budget, tool/vision needs, latency SLA (the
RoutingContext fields)
- Budget tracking wired up (see
openrouter-cost-controls) if you use budget-conditioned rules like low-budget
Instructions
- Model each request's metadata as a
RoutingContext (user tier, task type, budget remaining, tools/vision flags, latency SLA) per Rules Engine.
- Define
RoutingRule entries in priority order — free-tier first, then budget, capability (tools/vision), task type, latency, and always a priority=99 default catch-all.
- Resolve the winning rule with
evaluate_rules(ctx): first match by ascending priority wins; failing conditions return False instead of raising.
- Execute through
routedcompletion() per Routed Completion — it applies the rule's model, fallback chain (models + route: "fallback"), and maxtokens.
- To make rules hot-reloadable, express them as JSON per Config-Driven Rules and match with
matchconfigrule() instead of lambdas.
- Validate any rule change on a slice of traffic with
abtestrouting() per A/B Testing Rules before full rollout.
Rules Engine
import os, json, time
from dataclasses import dataclass
from typing import Optional, Callable
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
@dataclass
class RoutingContext:
user_tier: str = "free" # "free" | "basic" | "pro" | "enterprise"
task_type: str = "general" # "chat" | "code" | "analysis" | "classification"
budget_remaining: float = 0.0 # Remaining daily budget in dollars
prompt_tokens_est: int = 0 # Estimated prompt t
'Build reusable OpenRouter client wrappers with retries, typing, and.
ReadWriteEditGrepBash(python3:*)Bash(node:*)
OpenRouter SDK Patterns
Overview
Build production-grade OpenRouter client wrappers using the OpenAI SDK. The OpenAI Python/TypeScript SDKs work natively with OpenRouter by changing base_url to https://openrouter.ai/api/v1. This skill covers typed wrappers, retry strategies, middleware, and reusable patterns.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK plus
requests (used for the /auth/key credits check and /generation cost lookups), or Node.js 18+ with the OpenAI SDK
- Optional:
tenacity if you want the custom retry decorator beyond the SDK's built-in backoff
- An app name and URL to send as
HTTP-Referer / X-Title default headers for dashboard attribution
Instructions
- Start from the Python: Production Client Wrapper (or the TypeScript variant): point the OpenAI SDK at
baseurl="https://openrouter.ai/api/v1", read OPENROUTERAPI_KEY from the environment, and set the HTTP-Referer / X-Title default headers in the constructor.
- Return typed results from every call — the
CompletionResult dataclass/interface captures content, the served model, prompttokens/completiontokens, generationid, and latencyms.
- Tune the SDK's built-in retries per the Retry Strategy section (
max_retries, timeout in the constructor); add the tenacity decorator only when you need retry behavior beyond the SDK's 429/5xx/connection handling.
- Layer cross-cutting concerns via the Middleware Pattern —
withcosttracking queries GET /api/v1/generation?id= after each request and accumulates a session cost total.
- Surface remaining credits and rate limits with
check_credits(), which calls GET /api/v1/auth/key with the same key.
- Map SDK exceptions using the Error Handling table, then apply the Enterprise Considerations (single central wrapper, dependency injection for tests, SLA-based
max_retries).
Python: Production Client Wrapper
import os, time, hashlib, json, logging
from dataclasses import dataclass
from typing import Optional
from openai import OpenAI, APIError, RateLimitError, APITimeoutError
log = logging.getLogger("openrouter")
@dataclass
class CompletionResult:
content: str
model: str
prompt_tokens: int
completion_tokens: int
generation_id: str
latency_ms: float
class OpenRouterClient:
def __init__(
self,
'Implement streaming responses with OpenRouter for real-time UIs.
ReadWriteEditGrepBash(python3:*)Bash(node:*)
OpenRouter Streaming Setup
Overview
OpenRouter supports Server-Sent Events (SSE) streaming via stream: true, compatible with the OpenAI SDK. Streaming returns tokens as they're generated, reducing time-to-first-token (TTFT) from seconds to milliseconds. Usage stats are available via streamoptions: {includeusage: true} in the final chunk. This skill covers Python and TypeScript streaming, SSE forwarding to browsers, and error recovery.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ or Node.js 18+ with the OpenAI SDK (the async example uses
AsyncOpenAI from the same Python package)
- FastAPI if you plan to forward the SSE stream to browsers per the SSE Forwarding section
- A streaming-appropriate client timeout (e.g. 120s) — longer than for non-streaming requests
Instructions
- Start with Python: Basic Streaming — pass
stream=True plus streamoptions={"includeusage": True} so the final chunk carries token counts, and print each chunk.choices[0].delta.content as it arrives.
- Wrap that loop in the Python: Streaming with Metrics generator to capture TTFT and total time per request; the metrics dict is available after the generator is exhausted.
- For Node services, use the TypeScript: Streaming
for await loop over the same stream: true request.
- To reach a browser UI, expose the FastAPI endpoint in SSE Forwarding to Browser — it re-emits each token as a
data: {"token": ...} SSE line and terminates with data: [DONE].
- Consume that endpoint with the Browser Client (JavaScript) reader loop, appending tokens to the DOM as they decode.
- In async web frameworks, switch to the Async Streaming pattern built on
AsyncOpenAI.
- Handle mid-stream failures (cut-offs, missing
usage, keep-alive pings, finish_reason: "length") per the Error Handling table.
Python: Basic Streaming
import os
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
# Stream with usage stats
stream = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Explain how HTTP streaming works"}],
max_tokens=500,
stream=True,
stream_options={"include_usage": True}, # Get token counts in final chunk
)
full_content = []
for chunk in stre
'Configure OpenRouter for multi-user teams with per-user keys, budget.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Team Setup
Overview
OpenRouter supports team usage through per-user API keys with individual credit limits, management keys for programmatic key provisioning, and usage attribution via headers. This skill covers key provisioning, per-user budgets, usage tracking, and governance policies for multi-user deployments.
Prerequisites
- A management key (
sk-or-v1-...) with provisioning rights exported as OPENROUTERMGMTKEY — created separately at openrouter.ai/keys; it can create/list/delete API keys but cannot call completions
- A regular OpenRouter API key exported as
OPENROUTERAPIKEY for the shared-key attribution pattern — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK and
requests; sqlite3 (stdlib) backs the per-user budget database
curl and jq for the Team Key Dashboard Script
Instructions
- Create a management key at openrouter.ai/keys and export it as
OPENROUTERMGMTKEY.
- Provision one key per team member via Key Provisioning via Management API —
createteamkey(name, creditlimit) posts to /api/v1/keys; record the one-time key value and keep the keyhash for later listing/revocation.
- Alternatively, keep a single shared key and attribute usage per user with the Shared Key with User Attribution pattern (
X-Title: my-app:{user_id} header shows each user in the dashboard).
- Enforce spend locally with Per-User Budget Enforcement — initialize the
userusage / userbudgets sqlite tables, call checkuserbudget before each request and recorduserusage after.
- Gate expensive models per tier with the Model Governance allowlists (
enforcemodelpolicy downgrades disallowed requests).
- Monitor continuously: run the Team Key Dashboard Script (curl + jq against
/api/v1/keys) and generate the weekly Team Usage Report from the sqlite DB.
- Revoke keys for departed members with
deleteteamkey(key_hash) (DELETE /api/v1/keys/{hash}).
Key Provisioning via Management API
import os, requests
MGMT_KEY = os.environ["OPENROUTER_MGMT_KEY"] # Management key (cannot call completions)
def create_team_key(name: str, credit_limit: float = 25.0) -> dict:
"""Create a new API key for a team member."""
resp = requests.post(
"https://openrouter.ai/api/v1/keys",
headers={"Authorization": f"Bearer {MGMT_KEY}"},
json={"name": name, "limit": credit_limit},
)
resp.ra
'Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model.
ReadWriteEditGrepBash(python3:*)Bash(node:*)Bash(npm:*)Bash(pip:*)
OpenRouter Upgrade & Migration
Current State
!npm list openai 2>/dev/null | head -5
!pip show openai 2>/dev/null | head -5
Overview
Migrating to OpenRouter from a direct provider API (OpenAI, Anthropic) is minimal: change baseurl and apikey, add two headers. The OpenAI SDK works natively with OpenRouter. This skill covers migrating from direct APIs, switching between models, upgrading SDK versions, and running comparison tests.
Prerequisites
- An existing direct OpenAI or Anthropic integration to migrate — the Current State block above checks your installed
openai SDK via npm list openai / pip show openai
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ or Node.js 18+ with the OpenAI SDK (Anthropic SDK users switch to the OpenAI SDK as part of the migration)
- The old provider key (
OPENAIAPIKEY / ANTHROPICAPIKEY) kept active during migration for comparison tests and quick rollback
Instructions
- Confirm your installed SDK versions from the Current State output at the top of this skill.
- Apply the 3-line change per Migration from Direct OpenAI, Migration from Direct Anthropic, or TypeScript Migration: swap
baseurl to https://openrouter.ai/api/v1, switch to OPENROUTERAPI_KEY, and add the HTTP-Referer / X-Title headers. Anthropic migrations also change response parsing to .choices[0].message.content.
- Prefix every model ID with its provider per the Model ID Migration Map (e.g.
gpt-4o → openai/gpt-4o).
- Work through the Migration Checklist — config, code, testing, and operations items — before flipping traffic.
- Run the Comparison Test Script on your critical prompts (
temperature=0) to compare content, tokens, and latency against the old backend.
- Roll out gradually with the Feature Flag Migration pattern (
USEOPENROUTER env var plus getmodel_id mapping), moving 10% → 50% → 100%.
- Watch for post-migration failures (401,
modelnotfound, response-format drift, +50–100ms latency) per the Error Handling table.
Migration from Direct OpenAI
# BEFORE: Direct OpenAI
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
)
# AFTER: Via OpenRouter (3 lines changed)
from openai import Open
'Track and analyze OpenRouter API usage patterns, costs, and performance.
ReadWriteEditGrepBash(python3:*)Bash(curl:*)Bash(jq:*)
OpenRouter Usage Analytics
Overview
OpenRouter provides usage data through three endpoints: GET /api/v1/auth/key (credit balance and rate limits), GET /api/v1/generation?id= (per-request cost and metadata), and response usage fields (token counts). This skill covers collecting metrics from these sources, building analytics pipelines, cost reporting, and performance dashboards.
Prerequisites
- An OpenRouter API key (
sk-or-v1-...) exported as OPENROUTERAPIKEY — see the openrouter-install-auth skill for setup
- Python 3.8+ with the OpenAI SDK plus
requests (used to fetch exact per-request cost from the generation endpoint); sqlite3 (stdlib) backs the analytics database
curl and jq for the Credit Balance Monitoring one-liner
HTTP-Referer / X-Title headers set on the client if you also want OpenRouter dashboard attribution
Instructions
- Route completions through
trackedcompletion (Collect Per-Request Metrics) — it times each call, then fetches the exact totalcost from GET /api/v1/generation?id= and emits a JSON metric with tokens, latency, and modelrequested vs modelused.
- Initialize
openrouteranalytics.db with initanalyticsdb (Analytics Database) and persist every metric via storemetric — the generation_id unique constraint plus INSERT OR IGNORE deduplicates retries.
- Query the store with the Analytics Queries: daily cost summary, cost by model, top users by spend, hourly request pattern, and 30-day cost trend.
- Watch remaining credits with the Credit Balance Monitoring snippet — curl + jq against
/api/v1/auth/key reports creditsused, creditlimit, and remaining.
- Generate the Weekly Report Generator output for stakeholders (totals plus top 5 models by cost).
- Apply the retention and alerting policies from Enterprise Considerations (aggregate raw rows after 30 days, alert when daily cost exceeds 2x the historical average).
Collect Per-Request Metrics
import os, time, json, logging
from datetime import datetime, timezone
from openai import OpenAI
import requests as http_requests
log = logging.getLogger("openrouter.analytics")
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
def tracked_completion(messages, model="openai/gpt-4o-mini", user_id="system", **kw
How It Works
After installation, these skills activate automatically when you:
- Set up OpenRouter API integration
- Configure model fallbacks and provider routing
- Optimize LLM costs and manage credits
- Debug API requests and handle errors
- Implement streaming, tool calling, or caching
Ready to use openrouter-pack?
|
|