OpenRouter Fundamentals Basics
8 examples to get you started with OpenRouter - 5 basic and 3 intermediate.
You will authenticate against the OpenAI-compatible gateway, call chat completions, swap model slugs, inspect routing metadata, and handle the errors agents hit first.
Prerequisites
- Python 3.11+ recommended
- An OpenRouter account and API key
- Credits for paid models, or willingness to use
:freevariants with tight rate limits
python -m venv .venv && source .venv/bin/activate
pip install openai httpx
export OPENROUTER_API_KEY="sk-or-..."Basic Examples
1. Point the OpenAI Client at OpenRouter
Use the standard OpenAI Python SDK with OpenRouter's base URL.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
resp = client.chat.completions.create(
model="openai/gpt-4o-mini", # verify slug at build
messages=[{"role": "user", "content": "Reply with the word pong."}],
)
print(resp.choices[0].message.content)- Same
chat.completionssurface most agent frameworks already use. - Base URL is the entire integration switch for many apps.
- Model must be an OpenRouter slug, not only a vendor marketing name.
Related: What OpenRouter Actually Is: One API for 300+ Models
2. Add Optional App Attribution Headers
Optional headers help rank your app on OpenRouter and do not change model behavior.
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello"}],
extra_headers={
"HTTP-Referer": "https://example.com", # your site
"X-OpenRouter-Title": "My Agent Demo",
},
)- Headers are optional for correctness.
- Set them once in client
default_headersfor cleaner call sites. - Use a stable app name so usage analytics stay coherent.
3. Swap Models by Changing Only the Slug
Keep messages and tools fixed; change the model string.
MODELS = [
"openai/gpt-4o-mini",
"anthropic/claude-3.5-sonnet", # verify at build
"google/gemini-2.5-flash",
]
prompt = [{"role": "user", "content": "In one sentence, what is an agent loop?"}]
for model in MODELS:
r = client.chat.completions.create(model=model, messages=prompt)
print(model, "=>", r.choices[0].message.content)- This is the core OpenRouter value for agents and evals.
- Put slugs in config or env, not hardcoded in ten modules.
- Re-run golden tasks after every slug change.
4. Try a Free Variant (Mind the Caps)
Free models use a :free suffix and platform rate limits.
resp = client.chat.completions.create(
model="meta-llama/llama-3.3-70b-instruct:free", # example; verify at build
messages=[{"role": "user", "content": "Say hi in five words."}],
)
print(resp.choices[0].message.content)- Free tier is for prototyping, not production agent load.
- Platform caps (verify at build): often 20 req/min; 50 req/day if lifetime credit purchases are under 10, else 1000 req/day.
- A 402 can still appear if account balance is negative, even for free models.
Related: Free Models on OpenRouter: What's Available and Their Limits
5. Read Usage and Provider Hints from the Response
Agents should log cost drivers every turn.
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Count to three."}],
)
print("usage:", resp.usage)
# Provider may appear on the object or in raw JSON depending on SDK version
print(resp.model_dump().get("provider"))- Token usage drives credit spend.
- Provider identity matters for debugging latency and policy.
- Store model slug + usage on every agent turn trace.
Intermediate Examples
6. Stream Tokens for Interactive Agents
stream = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "List three agent stop conditions."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
print()- Streaming uses SSE under the hood.
- Mid-stream errors may arrive as stream events after HTTP 200 - handle both paths.
- Buffer partial tool-call JSON carefully if you stream tool arguments.
7. List Models Programmatically
Do not hardcode the entire catalog in source control.
import os
import httpx
r = httpx.get(
"https://openrouter.ai/api/v1/models",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
timeout=30.0,
)
r.raise_for_status()
models = r.json()["data"]
print("count:", len(models))
print("sample:", models[0]["id"])- Catalog size and prices change; refresh at deploy or build time.
- Filter client-side by modality, price, or context length for agent routers.
- Cache with a TTL so cold starts do not hammer the models endpoint.
8. Handle 402 and 429 the Way Production Agents Should
from openai import APIStatusError
def complete(model: str, messages: list) -> str:
try:
resp = client.chat.completions.create(model=model, messages=messages)
return resp.choices[0].message.content or ""
except APIStatusError as e:
code = e.status_code
if code == 402:
raise RuntimeError("Insufficient OpenRouter credits or key limit") from e
if code == 429:
raise RuntimeError("Rate limited; back off or switch model") from e
raise
print(complete("openai/gpt-4o-mini", [{"role": "user", "content": "ok"}]))- 402: add credits, raise per-key limits, or fix negative balance.
- 429: exponential backoff; on free variants, buy credits threshold or switch to paid slug.
- Agent loops should map these to stop reasons, not infinite retries.
Related
- What OpenRouter Actually Is: One API for 300+ Models - gateway definition
- How OpenRouter's Unified API Normalizes Different Providers - why one client works
- OpenRouter Setup & Routing Basics - keys and first completion depth
- Calling OpenRouter from Python and TypeScript SDKs - multi-language recipes
- OpenRouter Fundamentals Best Practices - fit checklist for projects
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.