Claude via /v1/messages or /v1/chat/completions: what happens to caching
Claude answers in both formats, but only the Anthropic format gives the cache discount. The cost difference and how to switch your code and tools.
5 min · · экономия · development
Many people connect the Claude API through the OpenAI-compatible format: the SDK is already installed, the code is written around chat.completions, and nobody wants to change it. It works, but for Claude that setup has a price — prompt caching is not counted in it. Let's look at where the difference matters, what it costs in money, and how to move your code and tools to the Anthropic format.
Two formats, one key
Zukko accepts requests in two formats:
- OpenAI-compatible —
POST https://api.zukko.pro/v1/chat/completions(and/v1/responses); - Anthropic-compatible —
POST https://api.zukko.pro/v1/messages.
The fetch_... key and the balance are shared. Any model can be called in either format — the gateway converts requests and responses. The difference is not which models are available but how caching is billed.
Claude API in the OpenAI-compatible format: what happens to caching
The rule is short:
- in the Anthropic format (
/v1/messages, Claude Code, the Anthropic SDK) the gateway sees how much input came from cache and bills those tokens at the cache read price; - in the OpenAI format (
/v1/chat/completions) all Claude input is billed at the input price.
For short one-off requests there is no difference: there is nothing to cache. It shows up where a long beginning repeats from request to request — agents, bots with large instructions, work over the same document.
What it costs
An agent step on claude-sonnet-5: 100,000 tokens of context, of which 95,000 is the repeating beginning and 5,000 is new; a 2,000-token answer. Zukko prices per 1M tokens: input $0.2, cache read $0.025, output $1.013.
Through /v1/messages, if the beginning is read from cache:
(95,000 × 0.025 + 5,000 × 0.2 + 2,000 × 1.013) / 1,000,000
= (2,375 + 1,000 + 2,026) / 1,000,000 = $0.005401
Through /v1/chat/completions, all input at the input price:
(100,000 × 0.2 + 2,000 × 1.013) / 1,000,000
= (20,000 + 2,026) / 1,000,000 = $0.022026
Over 200 such steps: $1.0802 versus $4.4052. The calculation is simplified: in a real session some steps also write new chunks of context to the cache, and a write on Claude Sonnet 5 costs $0.25 per million. The usage field in responses and the Usage section of the dashboard show the exact picture. The full formula with writes and reads is in the article on how cache reads and writes are billed.
How to switch your code to the Anthropic SDK
Before — the OpenAI SDK:
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_your_key")
resp = client.chat.completions.create(
model="claude-sonnet-5",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question},
],
)
print(resp.choices[0].message.content)After — the Anthropic SDK (pip install anthropic):
import anthropic
client = anthropic.Anthropic(base_url="https://api.zukko.pro", api_key="fetch_your_key")
resp = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
system=[{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"},
}],
messages=[{"role": "user", "content": question}],
)
print("".join(block.text for block in resp.content if block.type == "text"))
print("from cache:", resp.usage.cache_read_input_tokens,
"written:", resp.usage.cache_creation_input_tokens)What changes:
- Base URL without
/v1— the Anthropic SDK adds/v1/messagesitself. - System prompt — a separate
systemparameter, not a message with thesystemrole. max_tokensis required.- Response — a list of
contentblocks; text is assembled from blocks of typetext. - Cache marker —
cache_controlon the stable part. Without it, caching will not kick in for your own code. Per Anthropic's documentation, the minimum cacheable prefix is 1,024 tokens for Claude Sonnet 5 and 512 for Claude Opus 5.5.
If your code uses tools (function calling), you will have to port that too: in the Anthropic format, tool calls and results are tool_use and tool_result blocks inside messages rather than separate fields. This is the most laborious part of the migration; start with the calls where the context is long and repetitive.
You can test without an SDK using curl:
curl https://api.zukko.pro/v1/messages \
-H "Authorization: Bearer fetch_your_key" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 512,
"messages": [{"role": "user", "content": "Hello"}]
}'How to switch your tools
| Tool | Format for Claude | Base URL |
|---|---|---|
| Claude Code | Anthropic | https://api.zukko.pro |
| Roo Code | Anthropic provider, enable Use custom base URL | https://api.zukko.pro |
| OpenCode | anthropic provider | https://api.zukko.pro/v1 |
| Cline, Kilo Code | OpenAI Compatible in our guide | https://api.zukko.pro/v1 |
| Cursor | OpenAI format only | https://api.zukko.pro/v1 |
Claude Code already works in the Anthropic format and places cache markers itself. It needs two variables: ANTHROPIC_BASE_URL=https://api.zukko.pro and ANTHROPIC_AUTH_TOKEN=fetch_... (AUTH_TOKEN specifically).
OpenCode connects through the anthropic provider, but here the address includes /v1 — without it requests go to /messages and get a 404. A config example is in the OpenCode guide.
Roo Code — the Anthropic provider, a fetch_... key, Use custom base URL enabled, and the address without /v1. If the model you need is not in that provider's list, you are left with OpenAI Compatible — and then Claude input gets no cache discount. Step by step — in the VS Code extensions guide.
Cline is connected through OpenAI Compatible in our guide. Cline's documentation also describes an Anthropic provider with a Use custom base URL option; there you pick the model from its list. If you try that route, enter the address without /v1, as in Roo Code, and check the suffix if you get a 404.
Cursor and Kilo Code work through the OpenAI format in our setup. Claude is available in them, but long context is billed at the full input price.
For GPT, caching is visible in both formats
For GPT models the choice of format does not affect caching. In the OpenAI format the number of tokens read from cache arrives in usage.prompt_tokens_details.cached_tokens and is billed at the cache read price. A cache write is not charged separately for GPT: the first request is billed in full at the input price. Through /v1/messages GPT caching is counted too. So GPT models keep their cache discount in tools that only speak the OpenAI format.
If a tool only supports the OpenAI format and your contexts are long, compare Claude and, for example, gpt-6.1-sol on your own tasks: run the same task and look at the totals in the Usage section. Judge quality by your own results, not by the model name. More on formats — in the docs section Choosing a provider.
How to confirm the discount works
- Send the same long request in the Anthropic format twice, less than five minutes apart.
- In the first response
cache_creation_input_tokensis above zero; in the second,cache_read_input_tokensis. - Compare the
X-Zukko-Cost-Microsheader (request cost in micro-dollars, present on regular, non-streaming responses) across the two requests.
If you do not have a key yet, create one on the API keys page — one key works in both formats. The current model list and prices are at GET https://api.zukko.pro/v1/models or on the Models page in the dashboard.
FAQ
Does Claude work through /v1/chat/completions at all?
Yes. Requests and responses are correct; it is just that in this format all Claude input is billed at the input price, with no cache discount.
Why does the Anthropic SDK use an address without /v1 while OpenCode uses /v1?
The Anthropic SDK and Claude Code add /v1 to the address themselves. The anthropic provider in OpenCode does not, so the suffix is specified manually.
Do I need the anthropic-version header?
SDKs set it themselves. In manual curl requests add anthropic-version: 2023-06-01.
Can I call GPT through /v1/messages?
Yes, the gateway converts the format for any model. GPT cache reads are counted there as well as in /v1/chat/completions.
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
- Chatbot memory: how to store conversation history without paying twiceThe model does not remember earlier messages: you send the history with every request. We look at how dialogue cost grows and what keeps it in check.
- LangChain text splitters: how to cut text into chunks correctlyHow to choose chunk size and overlap in RecursiveCharacterTextSplitter, why you should count in tokens and how to check the count against an exact token number.
- LangChain RAG: a working example with a model via a gatewayWe build a simple RAG with LangChain: documents, chunks, vector search and a model answer via a gateway. We count what a question costs and trim wasted tokens.
- opencode agents: setting up roles, models and toolsA custom agent in opencode is a role, a model and a set of permissions. A working config where a cheap model handles routine work and a stronger one edits code.