Migrating from OpenRouter to Zukko: what to change in code and config
Moving from OpenRouter to Zukko's OpenAI-compatible API comes down to the address, key and model names. Every change, OpenRouter-specific parameters and a rollout plan.
5 min · · development · сравнение
If your code runs through OpenRouter and you are looking for an alternative, moving to Zukko usually comes down to three changes: the API address, the key and the model names. Both services support the OpenAI-compatible format, so your SDK and request structure stay the same. Below: exactly what to change, what to do with OpenRouter-specific headers and parameters, and how to roll out safely.
Why move
The decision depends on your needs, so here are just the facts about Zukko:
- one
fetch_...key for Claude, GPT and GLM models, a single USD balance, pay per token with no subscription; - payment from Russia is available: SBP, Russian cards, crypto; top-ups from $1;
- two API formats: OpenAI-compatible and Anthropic-compatible;
- keys have daily and monthly limits in $ and an allowed-models list.
Compare cost at your own volume: take monthly input and output tokens from your OpenRouter stats and price them at Zukko rates, for example for Claude Sonnet 5: input_millions × $0.30 + output_millions × $0.30. For agents with a large repeated context, note that Zukko bills cached tokens at the regular input price.
Three migration points
1. Base URL. OpenRouter uses https://openrouter.ai/api/v1; Zukko uses https://api.zukko.pro/v1 for OpenAI-compatible clients.
2. Key. Create a key under API keys. It starts with fetch_ and is shown in full only once, so save it right away.
3. Model IDs. OpenRouter uses vendor-prefixed IDs (anthropic/claude-sonnet-5, openai/gpt-5.6-sol). Zukko has no prefix: claude-sonnet-5, gpt-5.6-sol.
Example: Python and the OpenAI SDK
Before (OpenRouter):
from openai import OpenAI
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="sk-or-v1-...",
default_headers={
"HTTP-Referer": "https://example.com",
"X-OpenRouter-Title": "My App",
},
)
response = client.chat.completions.create(
model="anthropic/claude-sonnet-5",
messages=[{"role": "user", "content": "Hello"}],
)After (Zukko):
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key="fetch_your_key",
)
response = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)According to the OpenRouter docs, the HTTP-Referer and X-OpenRouter-Title headers (older code may use X-Title) are optional and used for app attribution in OpenRouter rankings. Zukko does not need them; remove them to keep the code clean.
The same in JavaScript/TypeScript:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.zukko.pro/v1",
apiKey: process.env.ZUKKO_API_KEY,
});
const response = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Hello" }],
});
console.log(response.choices[0].message.content);Mapping table
| Find (OpenRouter) | Replace (Zukko) |
|---|---|
https://openrouter.ai/api/v1 | https://api.zukko.pro/v1 |
sk-or-v1-... | fetch_... |
anthropic/claude-sonnet-5 | claude-sonnet-5 |
| Opus 5.5 (OpenRouter writes the version with a dot) | claude-opus-5-5 (with a hyphen) |
openai/gpt-5.6-sol | gpt-5.6-sol |
openai/gpt-5.6-luna | gpt-5.6-luna |
z-ai/glm-5.3 | glm-5.3 |
z-ai/glm-5.3-flash | glm-5.3-flash |
aliases like ~anthropic/claude-sonnet-latest | a concrete id from /v1/models |
HTTP-Referer, X-OpenRouter-Title, X-Title headers | remove |
The OpenRouter IDs were checked against its public model list at https://openrouter.ai/api/v1/models. The Zukko model set changes: the current list is at GET https://api.zukko.pro/v1/models or on the "Models" page in the dashboard.
OpenRouter-specific parameters
OpenRouter accepts its own request-body fields that are not part of the standard OpenAI API. The most common are models (an array of fallback models) and provider (routing preferences across providers); both are described in OpenRouter's provider routing docs. Zukko's docs do not include these fields, so:
- remove
providerandmodelsfrom requests; - if you need a fallback to another model, implement it in your code: catch the error and retry with a different
model; - search for places where these fields are passed via
extra_body; they are easy to miss.
Standard OpenAI API parameters (messages, max_tokens, stream, tools and others) carry over unchanged. Reference: Chat Completions.
Streaming
Streaming code does not need changes, but check one detail: the last chunk may carry token usage data with an empty choices list. Add a check before accessing chunk.choices[0]:
stream = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Tell me about HTTP/2"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)LangChain, Open WebUI, Dify and other tools
LangChain: ChatOpenAI(model="claude-sonnet-5", base_url="https://api.zukko.pro/v1", api_key="fetch_...").
Open WebUI, Dify, LibreChat and similar: add an OpenAI-compatible provider in settings, set Base URL https://api.zukko.pro/v1 and the fetch_... key, then pick models. Field names differ between versions, so check the tool's own documentation.
Code on the Anthropic SDK: set base_url="https://api.zukko.pro" without /v1; the SDK adds the path itself. More in Anthropic or OpenAI format.
Production rollout plan
- Create a separate test key with a small daily limit and an allowed-models list.
- Move the base URL, key and model into environment variables if they are still hardcoded.
- On staging, swap the values and run your tests, including streaming and tool calls.
- Check spending in the dashboard and compare it with your formula estimate.
- Switch production. Rollback means restoring the old variable values and restarting the app.
Zukko error codes worth handling: 401 invalid key, 402 insufficient balance, 403 model not allowed for the key, 413 request too large, 429 too many requests, 503 provider unavailable.
Checklist
-
fetch_...key created and saved - Base URL is
https://api.zukko.pro/v1(for the Anthropic SDK,https://api.zukko.pro) - Vendor prefixes removed from model IDs, Opus 5.5 written as
claude-opus-5-5 -
HTTP-Referer,X-OpenRouter-Title,X-Titleheaders removed -
providerandmodelsfields removed - Streaming has the
if chunk.choicescheck - A test request returns an answer and spending shows up in the dashboard
FAQ
Do I need to rewrite prompts?
No: the models are the same, only the gateway changes. Still run your test set after the move to make sure model IDs and parameters were replaced correctly.
What about OpenRouter fallback models?
Move the logic into your code: on an error, retry with a different model. If a model is disabled, Zukko responds with 503 and the model_disabled code, which you can use to switch to a fallback.
How do I quickly check that everything works?
Send a short request to an inexpensive model:
r = client.chat.completions.create(
model="glm-5.3-flash",
messages=[{"role": "user", "content": "ping"}],
)
assert r.choices[0].message.content
print("OK")Where can I read more?
Quick start and authentication in the Zukko docs.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- OpenAI SDK or LangChain: when a framework helps and when it gets in the wayOne scenario written with the plain SDK and with LangChain: how much code, where debugging is easier and how to keep control of token spend.
- LangChain Ollama or a cloud model: one codebase, two modelsChatOllama runs a model on your machine, ChatOpenAI with a gateway base_url calls a cloud one. We write the code once and switch with environment variables.
- GPT-6 Astra or Claude Fable 5.1: Two Top Models by Price and IndexTwo premium models with the same intelligence index and different Zukko prices. We price a single request and an agent session with cache, and explain how to choose on your own data.
- Prompt Injection: What It Is and How to Protect a Bot and an AgentHow prompt injection works through user input and third-party documents, and which measures limit the damage: data isolation, tool permissions, key limits.