OpenRouter OpenAI Compatibility
SkillAI & models'Migrate from OpenAI to OpenRouter with minimal code changes. Use when
Use OpenRouter OpenAI Compatibility in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add OpenRouter OpenAI Compatibility and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the OpenRouter OpenAI Compatibility skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by jeremylongshore/tons-of-skills-marketplace in skills/.curated/openrouter-openai-compat/SKILL.md and read by ahel’s review.
Overview
OpenRouter implements the OpenAI Chat Completions API specification (/v1/chat/completions). Existing OpenAI SDK code works with OpenRouter by changing two values: base_url and api_key. 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
OPENROUTER_API_KEY— see theopenrouter-install-authskill for setup - Python 3.8+ with the
openaipackage, or Node.js 18+ with theopenainpm package — the same SDK you already use, no new dependency - Optionally keep
OPENAI_API_KEYexported too, so the Dual-Provider Pattern can switch back to direct OpenAI
Instructions
- Apply The Two-Line Migration: point
base_urlathttps://openrouter.ai/api/v1and swapapi_keytoOPENROUTER_API_KEY; optionally add theHTTP-Referer/X-Titleheaders for app attribution. - Prefix every model string per Model ID Mapping —
gpt-4obecomesopenai/gpt-4o,o1becomesopenai/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 theorganizationparam, plan around limited embeddings, and checklogprobssupport per model via/api/v1/models. - Layer in OpenRouter-Only Features through
extra_body: ordered fallback model lists with"route": "fallback", provider preferences withsort: "price", or theplugins: [{"id": "web"}]web-search plugin. - Keep the migration reversible with the Dual-Provider Pattern —
create_client()switches between direct OpenAI and OpenRouter off theLLM_PROVIDERenvironment 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"], # Changed
default_headers={
"HTTP-Referer": "https://your-app.com", # Added (optional)
"X-Title": "Your App", # Added (optional)
},
)
response = client.chat.completions.create(
model="openai/gpt-4o", # Prefix with provider namespace
messages=[{"role": "user", "content": "Hello"}],
)
TypeScript (After)
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" },
});
const res = await client.chat.completions.create({
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});
Model ID Mapping
| OpenAI Direct | OpenRouter ID |
|---|---|
gpt-4o | openai/gpt-4o |
gpt-4o-mini | openai/gpt-4o-mini |
gpt-4-turbo | openai/gpt-4-turbo |
o1 | openai/o1 |
o1-mini | openai/o1-mini |
You also gain access to non-OpenAI models through the same SDK:
# Same client, any provider
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet", # Anthropic
messages=[{"role": "user", "content": "Hello"}],
)
response = client.chat.completions.create(
model="google/gemini-2.0-flash", # Google
messages=[{"role": "user", "content": "Hello"}],
)
What Works Identically
| Feature | Status | Notes |
|---|---|---|
chat.completions.create | Fully supported | Main endpoint, all parameters |
stream: true | Fully supported | SSE format identical to OpenAI |
tools / tool_choice | Supported | OpenRouter transforms for non-OpenAI providers |
response_format: { type: "json_object" } | Supported | Basic JSON mode |
response_format: { type: "json_schema" } | Supported | Strict schema mode |
temperature, top_p, max_tokens | Supported | Standard parameters |
stop sequences | Supported | Array of stop strings |
n (multiple completions) | Supported | Multiple choices |
What Differs
| Feature | Difference | Workaround |
|---|---|---|
| Model IDs | Prefixed with provider/ | Update model strings |
organization param | Not used | Remove from client init |
| Embeddings | Limited support | Use direct provider or dedicated embedding service |
| Fine-tuned models | Not directly accessible | Use provider's fine-tuned model ID if hosted |
logprobs | Model-dependent | Check model capabilities via /api/v1/models |
| Responses API | Beta support | Use /api/v1/responses endpoint |
OpenRouter-Only Features
These are available through the same SDK but are unique to OpenRouter:
# Model fallbacks (try models in order)
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Hello"}],
extra_body={
"models": [
"anthropic/claude-3.5-sonnet",
"openai/gpt-4o",
"google/gemini-2.0-flash",
],
"route": "fallback",
},
)
# Provider preferences
response = client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Hello"}],
extra_body={
"provider": {
"order": ["anthropic"], # Prefer Anthropic direct
"allow_fallbacks": True,
"sort": "price", # Cheapest first
},
},
)
# Plugins (web search, response healing)
response = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "What happened today?"}],
extra_body={
"plugins": [{"id": "web"}], # Enable real-time web search
},
)
Dual-Provider Pattern
import os
from openai import OpenAI
def create_client(provider: str = "openrouter") -> OpenAI:
if provider == "openai":
return OpenAI(api_key=os.environ["OPENAI_API_KEY"])
return OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
default_headers={"HTTP-Referer": "https://your-app.com"},
)
# Switch providers without changing application code
client = create_client(os.environ.get("LLM_PROVIDER", "openrouter"))
Output
- Standard OpenAI-SDK
ChatCompletionobjects —choices[0].message.content,usagetoken counts, andmodelreporting the provider-prefixed ID that actually served the request - The identical code path returning completions from non-OpenAI models (Claude, Gemini) with only the model string changed
- A provider-switchable client from
create_client()— flippingLLM_PROVIDERmoves traffic between direct OpenAI and OpenRouter with zero application-code changes
Examples
After the two-line change, the untouched OpenAI SDK call round-trips through OpenRouter:
client = OpenAI(base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"])
response = client.chat.completions.create(
model="openai/gpt-3.5-turbo",
messages=[{"role": "user", "content": "What is the capital of France?"}],
max_tokens=100,
)
print(response.choices[0].message.content) # The capital of France is Paris.
print(response.model) # openai/gpt-3.5-turbo
Swap the model string to anthropic/claude-3.5-sonnet and the same code returns Claude's answer — that swap is the entire multi-provider story. More worked examples: references/examples.md.
Error Handling
| Issue | Cause | Fix |
|---|---|---|
| 400 unsupported parameter | Model doesn't support a parameter | Conditionally set params based on model capabilities |
| Different response quality | Non-OpenAI model handles prompt differently | Adjust prompts per model family; test before switching |
Missing organization | OpenRouter ignores org-level auth | Remove organization from client init |
Enterprise Considerations
- Use environment variables to switch between direct OpenAI and OpenRouter without code changes
- Test your full prompt suite across providers before migrating production traffic
- Monitor response quality and latency after migration; some prompts may need tuning
- OpenRouter normalizes the API across providers, but subtle behavioral differences exist between model families
- Use
extra_bodyfor OpenRouter-specific features (provider preferences, plugins, fallbacks)
References
- Examples | Errors
- OpenRouter Quickstart | API Parameters
Signals
- GitHub stars
- 3k
- Forks
- 415
- Last commit
- Oct 2026
ahel review
S4info
community integration, published by jeremylongshore, not openai
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
openrouter-openai-compat- Source
- github.com/jeremylongshore/tons-of-skills-marketplace
github.com/jeremylongshore/tons-of-skills-marketplace
Related picks
Skill · thedaviddias
The pick for JavaScriptmodern-javascript-patterns
Skill · wshobson
The pick for JavaScriptpython-performance-optimization
Skill · wshobson
The pick for Pythonpython-pro
Skill · jeffallan
The pick for Pythonsetup-ts-deep-modules
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScript