Execute work with multiple providers through OpenRouter. Use when comparing providers or building provider-agnostic systems...
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).
OPENROUTER_API_KEY โ it covers every provider (OpenAI, Anthropic, Google, Meta, Mistral); see the openrouter-install-auth skill for setupcurl and jq for the provider-landscape querypip install openai)curl -s https://openrouter.ai/api/v1/models | jq ... groups model IDs by their provider/ prefix and sorts by model count.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).provider.order with allow_fallbacks: False forces one provider (e.g. for regulated data); allow_fallbacks: True fails across providers such as Anthropic โ AWS Bedrock.normalized_completion() per Feature Normalization โ JSON mode uses response_format natively on openai/ models and a system-prompt instruction elsewhere.# 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)'
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={"HTTP-Referer": "https://my-app.com", "X-Title": "my-app"},
)
def compare_models(prompt: str, models: list[str], max_tokens: int = 500) -> list[dict]:
"""Run the same prompt across multiple models and compare results."""
results = []
for model in models:
start = time.monotonic()
try:
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
temperature=0,
)
latency = (time.monotonic() - start) * 1000
results.append({
"model": model,
"served_by": response.model,
"content": response.choices[0].message.content[:200] + "...",
"tokens": response.usage.prompt_tokens + response.usage.completion_tokens,
"latency_ms": round(latency, 1),
"status": "ok",
})
except Exception as e:
results.append({"model": model, "status": "error", "error": str(e)})
return results
# Compare top-tier models on the same task
results = compare_models(
"Explain the CAP theorem in distributed systems",
models=[
"anthropic/claude-3.5-sonnet", # Anthropic
"openai/gpt-4o", # OpenAI
"google/gemini-2.0-flash-001", # Google
"meta-llama/llama-3.1-70b-instruct", # Meta (open-source)
],
)
for r in results:
print(f"{r['model']}: {r.get('latency_ms', 'N/A')}ms, {r.get('tokens', 'N/A')} tokens")
| Provider | Best For | Example Models | Price Range |
|---|---|---|---|
| Anthropic | Analysis, safety, long context | claude-3.5-sonnet, claude-3-haiku |
$0.25-$15/1M |
| OpenAI | Code generation, tool calling | gpt-4o, gpt-4o-mini, o1 |
$0.15-$60/1M |
| Multimodal, huge context (1M) | gemini-2.0-flash-001, gemini-pro |
$0.075-$7/1M | |
| Meta | Budget tasks, self-hosting | llama-3.1-8b-instruct, llama-3.1-70b-instruct |
$0.06-$0.90/1M |
| Mistral | European data residency, code | mistral-large, mixtral-8x7b |
$0.24-$8/1M |
# Force specific provider for a model
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
extra_body={
"provider": {
"order": ["Anthropic"], # Direct to Anthropic
"allow_fallbacks": False, # Don't fall back to other providers
},
},
)
# Cross-provider fallback: if Anthropic is down, try via AWS Bedrock
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
extra_body={
"provider": {
"order": ["Anthropic", "AWS Bedrock"],
"allow_fallbacks": True,
},
},
)
# Use your own provider API key through OpenRouter
# Configure BYOK in the OpenRouter dashboard:
# Settings > Integrations > Add Provider Key
# Benefits:
# - First 1M requests/month free via OpenRouter
# - After that, 5% of normal provider cost (vs full OpenRouter markup)
# - Data flows directly to provider under your account
# - Useful for high-volume production workloads
# With BYOK configured, requests automatically use your provider key
response = client.chat.completions.create(
model="openai/gpt-4o", # Uses YOUR OpenAI key, routed through OpenRouter
messages=[{"role": "user", "content": "Hello"}],
max_tokens=200,
)
def normalized_completion(messages, model, **kwargs):
"""Handle provider-specific feature differences."""
# JSON mode: OpenAI native, others via system prompt
if kwargs.pop("json_mode", False):
if model.startswith("openai/"):
kwargs["response_format"] = {"type": "json_object"}
else:
# Add JSON instruction to system prompt for non-OpenAI models
messages = [{"role": "system", "content": "Respond in valid JSON only."}] + [
m for m in messages if m["role"] != "system"
] + [m for m in messages if m["role"] == "system"]
return client.chat.completions.create(model=model, messages=messages, **kwargs)
served_by (the endpoint that actually answered), truncated content, token totals, latency_ms, and status (ok or the error){provider, models} objects sorted by model count, showing which namespaces dominate the catalogresponse.model โ the raw material for cost/quality attribution across providersOne prompt โ "Explain what an API gateway is in 2 sentences." โ fanned across four providers through the same client produces a directly comparable scoreboard:
[OpenAI] 450ms, 65 tokens โ ok
[Anthropic] 380ms, 58 tokens โ ok
[Google] 620ms, 71 tokens โ ok
[Meta] 510ms, 63 tokens โ ok
Anthropic answered fastest with the fewest tokens on this run; the point is that switching providers cost zero code changes beyond the model ID. More worked examples: references/examples.md.
| Error | Cause | Fix |
|---|---|---|
| Feature not supported | Provider lacks capability (e.g., tools on Llama) | Check model capabilities via /models; use fallback |
| Different response quality | Providers trained differently | Test critical prompts per model; adjust system prompts |
| Provider outage | Single provider down | Use provider.order with fallbacks across providers |
| BYOK auth failure | Provider key expired or invalid | Update provider key in OpenRouter dashboard |
provider.order + allow_fallbacks: true for cross-provider resilienceallow_fallbacks: falseresponse.model) for attribution