Switching from OpenRouter takes one line

Change your base URL and swap in an OrcaRouter key. Your models, your SDK and the rest of your code stay exactly as they are — openrouter/ router ids are the one rename.

Three things to check

  1. Use the right base URL for your SDK — The OpenAI SDK wants https://api.orcarouter.ai/v1. The other two want https://api.orcarouter.ai — each appends its own path.
  2. Swap in your OrcaRouter key — The value changes — an OpenRouter key will not authenticate here — but the place does not: whichever header your SDK already sends, we read it.
  3. Keep your model names — We use the same vendor/model names, so anthropic/claude-sonnet-4 works as-is. OpenRouter's own openrouter/ router ids are the exception: those get renamed.

Before and after: the line that changes

OrcaRouter speaks the OpenAI, Anthropic and Gemini APIs natively, so you keep the SDK you already use. In each snippet, lines starting with - are what you remove and lines starting with + are what you add.

OpenAI — Python

  client = OpenAI(
-     base_url="https://openrouter.ai/api/v1",
+     base_url="https://api.orcarouter.ai/v1",
      api_key=ORCAROUTER_API_KEY,
  )

That's the only code change. Swap in an OrcaRouter key as well — your model names, messages and streaming all stay the same, unless you call an openrouter/ router id.

Anthropic — Python

  client = Anthropic(
+     base_url="https://api.orcarouter.ai",
      api_key=ORCAROUTER_API_KEY,
  )

We speak Anthropic's API directly, which OpenRouter doesn't. If you have been calling Claude through the OpenAI SDK to fit OpenRouter, you can stop.

Gemini — TypeScript

  const ai = new GoogleGenAI({
    apiKey: ORCAROUTER_API_KEY,
+   httpOptions: { baseUrl: "https://api.orcarouter.ai" },
  });

We speak Gemini's API directly too, so the Google SDK works unchanged.

Complete examples for each SDK

Imports, client, and one real call. Runnable as it stands, once ORCAROUTER_API_KEY is set in your environment.

OpenAI — Python

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.orcarouter.ai/v1",
    api_key=os.environ["ORCAROUTER_API_KEY"],
)

response = client.chat.completions.create(
    model="anthropic/claude-sonnet-4",
    messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)

OpenAI — TypeScript

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.orcarouter.ai/v1",
  apiKey: process.env.ORCAROUTER_API_KEY,
});

const response = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-4",
  messages: [{ role: "user", content: "Hello" }],
});
console.log(response.choices[0].message.content);

OpenAI — cURL

curl https://api.orcarouter.ai/v1/chat/completions \
  -H "Authorization: Bearer $ORCAROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-4",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Anthropic — Python

import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.orcarouter.ai",
    api_key=os.environ["ORCAROUTER_API_KEY"],
)

message = client.messages.create(
    model="anthropic/claude-sonnet-4",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello"}],
)
print(message.content[0].text)

Anthropic — TypeScript

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.orcarouter.ai",
  apiKey: process.env.ORCAROUTER_API_KEY,
});

const message = await client.messages.create({
  model: "anthropic/claude-sonnet-4",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello" }],
});
console.log(message.content[0].text);

Anthropic — cURL

curl https://api.orcarouter.ai/v1/messages \
  -H "x-api-key: $ORCAROUTER_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-4",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello"}]
  }'

Gemini — Python

import os
from google import genai

client = genai.Client(
    api_key=os.environ["ORCAROUTER_API_KEY"],
    http_options={"base_url": "https://api.orcarouter.ai"},
)

response = client.models.generate_content(
    model="google/gemini-2.5-pro",
    contents="Hello",
)
print(response.text)

Gemini — TypeScript

import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI({
  apiKey: process.env.ORCAROUTER_API_KEY,
  httpOptions: { baseUrl: "https://api.orcarouter.ai" },
});

const response = await ai.models.generateContent({
  model: "google/gemini-2.5-pro",
  contents: "Hello",
});
console.log(response.text);

Gemini — cURL

curl "https://api.orcarouter.ai/v1beta/models/google/gemini-2.5-pro:generateContent" \
  -H "x-goog-api-key: $ORCAROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"parts": [{"text": "Hello"}]}]
  }'

Model ids: what to rename

We use the same vendor/model names, so anthropic/claude-sonnet-4 works as-is. OpenRouter's own openrouter/ router ids are the exception: those get renamed.

On OpenRouterOn OrcaRouter
openrouter/autoorcarouter/auto
openrouter/freeorcarouter/free
Any other openrouter/ routerNo counterpart — name a model instead
Any vendor/model id, such as anthropic/claude-sonnet-4The same id, unchanged

Billing differences

Four things we don't do

Better to know now than to find out in production.

Frequently asked questions

Is OrcaRouter a drop-in alternative to OpenRouter?

For most integrations, yes. Change the base URL and swap in an OrcaRouter key; your SDK, model names, messages and streaming stay the same. The exceptions are OpenRouter's own openrouter/ router ids, which get renamed, and the four things OrcaRouter deliberately does not do, listed above.

Which base URL does each SDK need?

The OpenAI SDK wants https://api.orcarouter.ai/v1. The other two want https://api.orcarouter.ai — each appends its own path.

Can I keep using my OpenRouter API key?

The value changes — an OpenRouter key will not authenticate here — but the place does not: whichever header your SDK already sends, we read it.

What replaces openrouter/auto and openrouter/free?

OrcaRouter's routers are the orcarouter/ ones. Two match theirs directly: use orcarouter/auto in place of openrouter/auto, and orcarouter/free in place of openrouter/free — same idea either way, we pick a model for you at request time, and the free one keeps you on the zero-cost tier. Their other routers have no counterpart here, so name a model instead.

Does fallback routing work the same way?

Move models and route inside extra_body. Everything else about it is the same: list your models in preference order and we try them in turn. At least 2 models, at most 5. Worth doing properly. At the top level we do not read them, so your fallbacks quietly stop working — and on deployments that forward the request body as-is, the model receives the extra fields and may reject the call outright.

How do I look up what a request cost?

Read X-Orca-Request-Id from the response, then call /v1/generation. A 501 means cost lookups are switched off on this deployment.

Do X-Title and HTTP-Referer still identify my app?

Send X-Title and your app keeps its name in your analytics with no change; we read OpenRouter's own header names too. HTTP-Referer on its own only names your app if its domain is one we already recognise, so send a title header if you want to be certain. This row is here so you do not remove them by mistake.

What happens to provider and transforms in my requests?

We do not use either. On a normal channel they are dropped before the request reaches the model; on a channel set to forward the raw body they are passed on, and a strict upstream can refuse the request. Taking them out is the safe move either way.

Which endpoints does OrcaRouter serve?

Everything you would expect, plus a few OpenRouter does not have. Served here and not by OpenRouter: /v1/messages, /v1beta/models … generateContent, /v1/rerank and /v1/realtime. Not available: /v1/fine-tunes, GET /v1/files and /v1/images/variations.

Do errors come back in the same shape?

Errors come back in the same shape you already handle. Act on the HTTP status: retry a 429 when we send a Retry-After, back off and retry a 5xx apart from 501 — which means we do not serve that call at all — and treat a 4xx on a relay call as something to fix in your request. A 404 from /v1/generation is the exception: it can simply mean the request has not settled yet, so retry that one briefly. Do not read error.type as the origin: orcarouter_api_error is always ours, but we deliberately send the provider’s own vocabulary for some of our own failures so your SDK takes its normal branch — a rate limit we applied and one the model applied both say rate_limit_error. Quote X-Orca-Request-Id when you need us to look up a single call.