Calling OpenRouter from Python and TypeScript SDKs
Most agent codebases already depend on the OpenAI client libraries.
Search across all documentation pages
Most agent codebases already depend on the OpenAI client libraries.
You can keep those libraries and call hundreds of models by changing base URL, key, and model slug - then pass OpenRouter-only options through extension fields when needed.
Use the official OpenAI Python and TypeScript SDKs pointed at https://openrouter.ai/api/v1.
Send standard chat.completions (or streaming) requests with OpenRouter model slugs.
Use extra_body (Python) or extra request fields (TypeScript) for OpenRouter extensions such as models fallbacks and provider preferences.
One client per process; one function that accepts messages + logical model config.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
def chat(model: str, messages: list[dict], **openrouter_extra):
return client.chat.completions.create(
model=model,
messages=messages,
extra_body=openrouter_extra or None,
)When to reach for this:
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="anthropic/claude-sonnet-4.5", # verify at build
messages=[
{"role": "system", "content": "Answer in one short paragraph."},
{"role": "user", "content": "What is model routing?"},
],
temperature=0.2,
extra_headers={
"HTTP-Referer": "https://example.com",
"X-OpenRouter-Title": "sdk-recipe",
},
extra_body={
"models": ["openai/gpt-4o-mini"], # fallbacks; verify at build
"provider": {"sort": "throughput"},
},
)
print(resp.model)
print(resp.choices[0].message.content)
print(resp.usage)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: process.env.OPENROUTER_API_KEY,
defaultHeaders: {
"HTTP-Referer": "https://example.com",
"X-OpenRouter-Title": "sdk-recipe",
},
});
const resp = await client.chat.completions.create({
model: "anthropic/claude-sonnet-4.5", // verify at build
messages: [
{ role: "system", content: "Answer in one short paragraph." },
{ role: "user", content: "What is model routing?" },
],
temperature: 0.2,
// OpenRouter extensions (types may not include them):
// @ts-expect-error OpenRouter-only fields
models: ["openai/gpt-4o-mini"],
// @ts-expect-error OpenRouter-only fields
provider: { sort: "throughput" },
});
console.log(resp.model);
console.log(resp.choices[0].message.content);
console.log(resp.usage);stream = client.chat.completions.create(
model="openai/gpt-4o-mini", # verify at build
messages=[{"role": "user", "content": "List three agent tools."}],
stream=True,
)
for chunk in stream:
text = chunk.choices[0].delta.content or ""
if text:
print(text, end="", flush=True)const stream = await client.chat.completions.create({
model: "openai/gpt-4o-mini", // verify at build
messages: [{ role: "user", content: "List three agent tools." }],
stream: true,
});
for await (const chunk of stream) {
const text = chunk.choices[0]?.delta?.content ?? "";
if (text) process.stdout.write(text);
}What this demonstrates:
messages, temperature, stream)model and usage are available after the call# Python
pip install openai
# TypeScript / Node
npm install openaiPin versions in your lockfile; verify current major at build.
| Goal | OpenAI SDK field | OpenRouter note |
|---|---|---|
| Host | base_url / baseURL | https://openrouter.ai/api/v1 |
| Auth | api_key / apiKey | OpenRouter key |
| Model | model | provider/model slug |
| Fallbacks | extra_body.models (Py) / models (TS) | Ordered backups |
| Provider prefs | extra_body.provider | order, sort, ignore, zdr, ... |
| Streaming | stream=True / stream: true | SSE under the hood |
| Attribution | extra_headers / defaultHeaders | Optional |
Function calling works through the same Chat Completions shapes when the selected model/provider supports tools.
OpenRouter routes tool requests toward capable endpoints when possible (verify tool-routing behavior at build).
Always re-test tool schemas after changing model policy.
# pip install openrouter (verify package name at build)
from openrouter import OpenRouter
import os
with OpenRouter(api_key=os.environ["OPENROUTER_API_KEY"]) as client:
response = client.chat.send(
model="openai/gpt-4o-mini", # verify at build
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)// npm install @openrouter/sdk (verify package at build)
import { OpenRouter } from "@openrouter/sdk";
const openrouter = new OpenRouter({
apiKey: process.env.OPENROUTER_API_KEY!,
});
const completion = await openrouter.chat.send({
model: "openai/gpt-4o-mini", // verify at build
messages: [{ role: "user", content: "Hello" }],
});
console.log(completion.choices[0].message.content);Prefer these when you want first-class types for OpenRouter-only features.
Prefer the OpenAI SDK when the rest of the monorepo already standardizes on it.
from openai import APIStatusError
try:
resp = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "hi"}],
)
except APIStatusError as e:
# 401 auth, 402 credits, 429 rate limit, 503 no provider, ...
print(e.status_code, e.response.text if e.response is not None else e.message)
raiseHonor Retry-After on 429/503 when present.
openai/gpt-4o-mini. Fix: use full slugs from openrouter.ai/models.extra_body in Python. They may be dropped or rejected. Fix: put models / provider in extra_body.@ts-expect-error with a comment.resp.model after fallbacks. Cost and quality retros break. Fix: log requested vs resolved model every time.| Alternative | Use When | Don't Use When |
|---|---|---|
| OpenAI SDK (this page) | Shared OpenAI ecosystem | You need every OpenRouter admin API typed |
| Official OpenRouter SDK | Greenfield OpenRouter apps | Team forbids new deps |
| Vercel AI SDK + OpenRouter provider | Web UI streaming stacks | Pure Python workers |
| LangChain / LlamaIndex chat models | Framework agents | You only need a 20-line client |
| Raw HTTP | Debugging | Large production surface without retries |
Pin the latest stable OpenAI SDK your framework supports and re-verify OpenRouter compatibility at build.
Yes.
from openai import AsyncOpenAI
client = AsyncOpenAI(base_url="https://openrouter.ai/api/v1", api_key=...)Include a provider object on the create call (may need a type assertion) matching OpenRouter's JSON schema.
Usually yes as a Chat Completions parameter.
Some models use related fields; check OpenRouter parameter docs for the model family at build.
OpenRouter may expose generation identifiers via response fields or headers such as X-Generation-Id (verify at build).
Log them for support tickets.
Yes.
Model is per-request; base URL and key stay fixed.
Use the Python version required by your pinned openai package (3.11+ recommended).
OpenRouter exposes embeddings endpoints separately.
Configure the same base URL/key, then call the embeddings API for supported models (verify catalog at build).
Yes.
Shared clients keep slug and header bugs out of "tests green, prod red" traps.
Use OpenRouter/model-supported response_format or tool schemas, then validate with Pydantic/Zod in your code regardless of provider.
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.
Reviewed by Chris St. John·Last updated Jul 16, 2026