Anthropic API from Russia: moving your code and Claude Code to Zukko
Anthropic SDK code and Claude Code move to the gateway with an address and key change. Where the /v1 trap is, which Claude models are available, and how caching works.
6 min · · development · IDE
What stops people from using the Anthropic API from Russia isn't code, it's payment: you can't attach a local card to the Anthropic console. Zukko accepts requests in native Anthropic format, so the Anthropic SDK and Claude Code move over with an address and key change, and prompt caching keeps saving you money. Below: the changes for Python, TypeScript and Claude Code, a table of Claude models, and a cost calculation with caching.
What stays the same
Zukko is an API gateway to Claude, GPT, Gemini, DeepSeek, Qwen and GLM models with a single fetch_... key and a balance in dollars. For the Anthropic format it offers:
POST https://api.zukko.pro/v1/messages— the main endpoint;POST https://api.zukko.pro/v1/messages/count_tokens— token counting before a request.
The official Anthropic SDK stays the same. Authentication works both via the x-api-key header (which is what the SDK sends) and via Authorization: Bearer. The SDK sets the anthropic-version: 2023-06-01 header itself.
Anthropic API from Russia: code changes
Python
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.zukko.pro",
api_key=os.environ["ZUKKO_API_KEY"], # a fetch_... key
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain idempotency with an example"}],
)
for block in message.content:
if block.type == "text":
print(block.text)The main trap is the address. base_url goes without /v1: the SDK appends /v1/messages itself. If you add /v1, requests go to /v1/v1/messages and return 404.
Parse the response by block type rather than reading content[0].text: if the model has reasoning enabled, the first block may be of type thinking.
TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.zukko.pro",
apiKey: process.env.ZUKKO_API_KEY,
});
const message = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Explain idempotency" }],
});
for (const block of message.content) {
if (block.type === "text") console.log(block.text);
}Via environment variables
The SDKs read the address from ANTHROPIC_BASE_URL and the token from ANTHROPIC_AUTH_TOKEN (sent as Authorization: Bearer). Then you can create the client with no arguments:
export ANTHROPIC_BASE_URL=https://api.zukko.pro
export ANTHROPIC_AUTH_TOKEN=fetch_your_keyclient = anthropic.Anthropic()If an old Anthropic key (sk-ant-...) is still in your environment, remove it so the SDK doesn't send the old key to the gateway alongside or instead of the new token.
Claude models: names and prices
Model IDs on Zukko match the familiar Claude names. Prices as of October 6, 2026, $ per 1M tokens:
| Model | Input / output | Cache read / write | Vendor price input / output | Intelligence index |
|---|---|---|---|---|
claude-opus-5-5 | 0.4 / 2 | 0.038 / 0.425 | 4 / 20 | 58 |
claude-fable-5-1 | 3.48 / 17.4 | 0.3 / 3.663 | 10 / 50 | 53 |
claude-opus-5 | 0.425 / 2.113 | 0.05 / 0.538 | 5 / 25 | — |
claude-sonnet-5 | 0.2 / 1.013 | 0.025 / 0.25 | 2 / 10 | — |
claude-sonnet-4-6 | 0.263 / 1.275 | 0.038 / 0.325 | 3 / 15 | — |
All five have a 1M-token context and accept images. The intelligence index is the Artificial Analysis Intelligence Index shown in the Zukko catalog; a dash means no value is listed. If your code names a model that isn't in the catalog, the request returns 404 model_not_found — switch to one that is. For the current list and prices, see GET https://api.zukko.pro/v1/models or the Models page in your dashboard.
You can also call other vendors' models through /v1/messages, such as gpt-6.1-sol or deepseek-v4.1-flash: the gateway converts the format. Your Anthropic SDK code stays the same — only the model name changes.
Prompt caching works in Anthropic format
For Claude, the cache discount comes from the Anthropic format specifically: the gateway sees how much input came from cache and bills those tokens at the cache read price. In OpenAI format (/v1/chat/completions) all Claude input is billed at the input price. So if you already use cache_control, stay on the Anthropic SDK. The difference between the two formats in numbers is in the article on Claude caching in Messages and Chat Completions.
LONG_INSTRUCTIONS = open("instructions.md", encoding="utf-8").read()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system=[{
"type": "text",
"text": LONG_INSTRUCTIONS,
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": "Review this contract for risks: ..."}],
)
u = response.usage
print("fresh input:", u.input_tokens)
print("cache read:", u.cache_read_input_tokens)
print("cache write:", u.cache_creation_input_tokens)
print("output:", u.output_tokens)A prefix that's too short won't be cached — Anthropic sets the minimum length, and it depends on the model. If cache_read_input_tokens stays at zero on repeated requests, check whether the start of your prompt changes (a date, a random ID).
Worked example: what caching does for an agent session
Formula: cost = fresh input × input price + cache read × read price + cache write × write price + output × output price.
Example for claude-sonnet-5: over a session the agent sent 1,000,000 input tokens, of which 900,000 were read from cache, 50,000 were written to cache and 50,000 were fresh input; output was 100,000 tokens.
- Fresh input: 50,000 × $0.2 / 1M = $0.01
- Cache read: 900,000 × $0.025 / 1M = $0.0225
- Cache write: 50,000 × $0.25 / 1M = $0.0125
- Output: 100,000 × $1.013 / 1M = $0.1013
- Total in Anthropic format: $0.1463
The same session in OpenAI format, where all Claude input is billed at the input price: 1,000,000 × $0.2 / 1M + $0.1013 = $0.2 + $0.1013 = $0.3013. The cache share varies between real sessions — check your own numbers in usage.
Claude Code through Zukko
Claude Code appends /v1 itself, so the address goes without the suffix. The key is passed via ANTHROPIC_AUTH_TOKEN:
export ANTHROPIC_BASE_URL=https://api.zukko.pro
export ANTHROPIC_AUTH_TOKEN=fetch_your_key
export ANTHROPIC_MODEL=claude-sonnet-5
claudeTo avoid exporting variables every session, put them in the env block of ~/.claude/settings.json, as described in the Claude Code docs:
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.zukko.pro",
"ANTHROPIC_AUTH_TOKEN": "fetch_your_key",
"ANTHROPIC_MODEL": "claude-sonnet-5"
}
}Claude Code works in Anthropic format, so in long sessions the prompt cache is billed at the cache read price. If Claude Code still asks you to log in to an Anthropic account, see the article on Claude Code asking for an Anthropic login. Full instructions are in the Claude Code docs.
Counting tokens before a request
count_tokens works the same way as on Anthropic and helps estimate the cost of a long document up front:
count = client.messages.count_tokens(
model="claude-sonnet-5",
messages=[{"role": "user", "content": LONG_INSTRUCTIONS}],
)
print(count.input_tokens, "tokens, input ≈ $", count.input_tokens * 0.2 / 1_000_000)More in the count_tokens reference.
Migration checklist
- Create a key on the API keys page, set a daily limit and allowed models.
- In the SDK, set
base_url="https://api.zukko.pro"— without/v1. - Use your
fetch_...key and remove the oldsk-ant-...key from your environment. - Check model names against
GET /v1/models. - Parse
contentby block type. - For Claude Code, set
ANTHROPIC_BASE_URL,ANTHROPIC_AUTH_TOKENandANTHROPIC_MODEL. - Confirm in
usagethatcache_read_input_tokensgrows on repeated requests.
The balance can be topped up from $1: SBP and Russian bank cards in rubles at the market rate, cryptocurrency, or a foreign Mastercard EU card with a fee of about 13%.
FAQ
Why is the Anthropic SDK address without /v1 while the OpenAI SDK one has /v1?
The Anthropic SDK and Claude Code append /v1/messages to the base address themselves. The OpenAI SDK only appends chat/completions, so it needs https://api.zukko.pro/v1.
Should I set ANTHROPIC_MODEL in Claude Code?
It's best to set it explicitly, for example claude-sonnet-5 for everyday work or claude-opus-5-5 for hard tasks. Which model handles your project better is something to check on a few real tasks.
Does prompt caching work the same as on Anthropic?
Yes, in Anthropic format: the cache_control markup is the same, and usage shows cache reads and writes. Reads and writes are billed at their own prices from the table.
What should I do about a 402 error?
It means insufficient balance. Top up in the dashboard — from $1, including via SBP.
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
- A neural network for Lua and Roblox: writing Luau scripts with AIWithout guidance a model may confuse Lua with Luau and suggest outdated Roblox APIs. How to phrase the task, check the code and connect an agent to your project.
- How to connect Qwen to VS Code: Companion, Cline and ContinueTwo ways to get Qwen in your editor: the official Companion for Qwen Code, or the Cline, Kilo Code and Continue extensions with an OpenAI-compatible key.
- Claude Code in JetBrains: IntelliJ IDEA and PyCharm on Your Own KeyThe plugin does not bundle Claude Code. It launches the console agent in the IDE terminal. Install, gateway key, diffs and common failures explained.
- Cursor for 1C: writing BSL code with an AI agentHow to open a 1C configuration dump in Cursor, write rules for BSL, give the agent metadata and avoid accepting invented code. With a session cost estimate.