TLDR
Through Cortecs, EU Router, Requesty and OpenRouter, Claude caches only if you send
cache_control— but then it hits in 4 of 4 follow-up calls at all four. Without the marker: 0 of 4. The router decides which upstream answers, and with it whether caching happens and what it costs. Routers accept reasoning efforts that the provider rejects. All four report the cost per call, and for Claude it matched to the cent; for open-weight models, the charged price ranged from 80 % below to 170 % above the listed one. Part 4 of the series "What an LLM call really costs".
Introduction
A router is an API in front of many APIs. You send a request in the OpenAI format, and the router forwards it to a suitable provider — Anthropic, AWS Bedrock, Google Vertex, an open-weight host. For European companies, there's a third argument: routers such as Cortecs or EU Router promise that processing stays in the EU.
It's convenient in any case. Whether it's also cheap depends on details you only see when you measure.
Disclosure: evanto uses Cortecs as its partner for Managed AI in CompanyWizard. The measurements ran on separate measurement accounts; Cortecs didn't see the results before publication.
1. The test setup
We sent the same Claude Sonnet 5 call with a system prompt of about 7,200 tokens five times, 20 seconds apart — directly to Anthropic and through four routers. Each in two variants:
- Best effort: with
cache_control(TTL 1 hour) and, where possible, a fixed upstream. - Unchanged: without a marker, the way most applications call Claude through a router.
At OpenRouter, we added a third variant: cache_control with a session key, but without a fixed upstream.
2. Caching through the router

| Path | Hits in calls 2–5, best effort | Hits, unchanged | Upstream |
|---|---|---|---|
| Anthropic direct | 4/4 | 0/4 | – |
| Cortecs (residency) | 4/4 (pinned to Bedrock) | 0/4 | amazon_ireland or google |
| EU Router | 4/4 | 0/4 | aws-bedrock |
| Requesty | 4/4 | 0/4 | not reported |
Requesty with auto_cache only |
4/4 | – | not reported |
OpenRouter, provider.order = Anthropic |
4/4 | 0/4 | Anthropic or Claude Platform on AWS |
| OpenRouter, session key only | 4/4 | – | Claude Platform on AWS |
The pattern is clear: without cache_control, Claude doesn't cache through any router; with the marker, it caches through all of them.
In detail:
- EU Router passes
cache_controlthrough, although its documentation doesn't mention it. - Requesty sets the breakpoints itself with
"requesty": {"auto_cache": true}— without any change to the prompt. Requesty reports cache writes ascaching_tokens, split into 5 minutes and 1 hour. - OpenRouter passes
cache_controlthrough, including the 1-hour TTL , and kept all follow-up calls with a session key on the same upstream — sticky routing works. - Cortecs caches only when the upstream caches. Without a preference, every one of our calls went to Google Vertex — without a single hit. Pinned to
amazon_ireland: 4 of 4. A provider switch between calls wasn't the problem; the router consistently stayed with the upstream that doesn't cache.
Lesson Learned: With a router, caching isn't a property of the model but of the path. If you want the saving, you have to fix the path.
3. Who actually answers?
Cortecs and EU Router name the chosen upstream in a response header (x-cortecs-provider, x-provider-slug), OpenRouter in the provider field of the response, and Requesty not at all. That's more than a detail: at Cortecs, the same prompt counted 5,426 prompt tokens on amazon_ireland and 7,238 on google. So the upstream affects not only the cache, but also the number you're billed for.
Tip: Log the upstream header for every call. Without it, you can't explain cost jumps after the fact.
4. Reasoning effort through the router
Routers tend to accept everything. Cortecs accepted all seven effort values for Claude, including none and minimal, which Anthropic direct rejects with HTTP 400. EU Router also accepted every value for Claude. Requesty reports no reasoning tokens for Claude, so you can't check whether the value arrives. OpenRouter also accepted all seven values for Claude, but rejected none for gpt-oss-120b.
Cortecs documents that it translates effort for Claude into thinking budgets of 1,024 to 4,096 tokens (not measurable). Requesty names budgets of up to 16,384 tokens (not measurable). Both are translations that newer Claude models no longer accept, according to Anthropic — a direct budget_tokens call ended with HTTP 400 in our test. What the router actually passes on remains a black box.
5. What it costs
All four routers report costs in every response:
- Requesty in USD (
usage.cost). - OpenRouter also in USD (
usage.cost). - EU Router in USD and euros, converted at the daily ECB rate.
- Cortecs as an integer in micro-euros — contrary to its own API reference, which lists no cost in the response. Cortecs also returns the cache fields, unlike what the API reference suggests.
For Claude, these costs matched our formula to the cent. Not so for open-weight models:
| Router | Model | Charged vs. listed (median) |
|---|---|---|
| EU Router | gpt-oss-120b | +103 % |
| Cortecs | gpt-oss-120b | +50 % to +67 % |
| Cortecs | DeepSeek V4.1 Flash | +44 % |
| Requesty | gpt-oss-120b via Fireworks | 0 % |
| OpenRouter | gpt-oss-120b | −75 % |
The listed price often doesn't match the upstream that actually answers — at Cortecs and EU Router it was more expensive, at OpenRouter clearly cheaper. Requesty avoids this because you fix the upstream in the model name (fireworks/gpt-oss-120b).
And there are surcharges too: EU Router lists Claude Sonnet 5 at $2.20 instead of $2.00 per million input tokens — 10 % above Anthropic, OpenRouter and Requesty.
6. Open-weight models: the host decides
For open-weight models, the router is only half the story; what matters is the host behind it. The same gpt-oss-120b cached fully at Cortecs and at Requesty via Fireworks, and not at all at EU Router and IONOS. The accepted effort values also differ by host — IONOS even rejected high for Qwen3.8-27B, but not xhigh.
7. What "EU" means at each router
"EU" isn't a uniform promise at routers:
- Cortecs separates accounts or keys: with the sovereign key, 49 models were available, without Claude; with the residency key, 87, including Claude via Bedrock and Vertex.
- EU Router filters providers by
data_residency,eu_ownedandmax_retention_days(not measurable). - Requesty's EU endpoint keeps only Requesty's own processing in the EU; whether inference also runs there depends on the model you choose (not measurable).
Whether data really never leaves the EU can't be measured from the outside. What you can measure is which upstream answers — see section 3.
8. How we solved it in .NET
In Evanto.ChatClients, all routers run over the OpenAI-compatible Chat Completions API, even when they're configured as "OpenAI". The Responses API is used only for the real OpenAI host:
internal static Boolean UsesResponsesApi(EvChatClientSettings settings)
=> String.Equals(settings.ProviderName, "OpenAI", StringComparison.OrdinalIgnoreCase)
&& Uri.TryCreate(settings.Endpoint, UriKind.Absolute, out var endpoint)
&& String.Equals(endpoint.Host, OPENAI_API_HOST, StringComparison.OrdinalIgnoreCase);
Source: UsesResponsesApi (excerpt)
CompanyWizard sets router-specific fields such as eu_native (Cortecs) or rule_name (EU Router) through freely configurable extra fields in the request body.
What's still missing on our side: so far, we allow cache_control only for the Anthropic provider type. So a Claude model behind a router gets no explicit caching — exactly the variant that brought 0 of 4 hits in this measurement. The gap is known; it isn't closed yet.
9. What we deliberately left open
- Load and failures: we didn't test failover behaviour under load.
- Data flows: where data is physically processed can't be checked from the outside.
Conclusion: patterns you can reuse
- Always send
cache_controlwhen Claude runs through a router — or turn onauto_cacheat Requesty. - Fix the upstream if cache or price matters.
- Log the upstream header so you can explain costs after the fact.
- Check effort values against the provider, not the router — the router accepts almost everything.
- For open-weight models, choose the host, not just the model.
All raw data and scripts: github.com/evanto-media-ag/llm-caching-reasoning-costs
The series
- Part 1: Prompt caching compared — what carries the cache and what breaks it
- Part 2: Reasoning effort compared — seven values, twelve interpretations
- Part 3: Cost transparency — formulas, field names, price sources
- Part 4: Routers compared — Cortecs, EU Router, Requesty, OpenRouter
References
- Cortecs: Prompt caching, Reasoning, Routing
- EU Router: Chat API, Routing
- Requesty: Auto-caching, EU routing
- OpenRouter: Prompt caching
Want to run Claude or open-weight models through an EU router? Let's spend 30 minutes on your specific workflow — no obligation, no cost, with concrete next steps at the end. Book a 30-minute call
Note: Article created with AI assistance.