Back to blog

AI patterns

What an LLM Call Really Costs: Caching, Reasoning Effort and Routers, Measured

Anyone who has budgeted an AI project knows the price table: one price for input, one for output. The invoice at the end of the month rarely sticks to it. We measured 12 providers and routers on a single day and show where the difference comes from.

SvK by Sven von Känel 15 min read
  • KI
  • Prompt Caching
  • LLM-Kosten
  • Reasoning

TLDR

An LLM call isn't made of two token types but of five, each with its own price. If you use prompt caching well, a repeated prompt costs about 90 % less. If you use it badly, the first call costs 25 % more and you save nothing afterwards. Routers decide whether caching happens at all, and for open-weight models some charged far more than their listed price. Every number in this series is measured; scripts and raw data are public on GitHub.

Introduction

Price per million input tokens, price per million output tokens, done: that's what every price table looks like. The invoice at the end of the month still looks different. The culprit is three levers that no price table shows: prompt caching, reasoning effort ("thinking level") and the choice of provider or router.

This article opens a series. It summarises what we measured on 19 October 2026 at OpenAI, Anthropic, xAI, Google Gemini, Mistral, IONOS and the routers Cortecs, EU Router, Requesty and OpenRouter. The four deep dives follow over the next weeks.

We don't compare feature lists. We measured, with the same prompts and the same evaluation, and every statement has evidence behind it.

1. One call, five prices

What an LLM call is made of, price-wise

Price-wise, a single call splits into five token types:

Token type What it is Claude Sonnet 5 GPT-6.1 Sol Grok 4.7 Gemini 3.8 Flash
Input (uncached) Part of the prompt that is processed again $2.00 $2.00 $2.00 $0.75
Cache read Part of the prompt served from the cache $0.20 (0.1×) $0.10 (0.05×) $0.50 (0.25×) $0.075 (0.1×)
Cache write Part of the prompt written to the cache $2.50 / $4.00 (5 min / 1 h) $2.50 (1.25×) – –
Output The answer $10.00 $10.00 $6.00 $3.75
Reasoning "Thinking" before the answer like output like output like output like output

Prices per 1 million tokens, as of 2026-10-19, from the providers' pricing pages or pricing APIs.

Anthropic charges a cache write at 1.25× the input price for 5 minutes and 2× for 1 hour. The cache-read factor isn't uniform even within one provider: Claude Sonnet 5.5 reads at 0.05×, Claude Sonnet 5 at 0.1×. At OpenAI, the read factor for GPT-6.1 Sol is 0.05×, not the often-quoted 0.1×. xAI even publishes its prices through an API, cache price included.

Note: A token isn't the same size everywhere. The same system prompt counted 7,229 tokens on Claude and 4,568 on OpenAI. That's a 58 % difference. So a price comparison per million tokens compares apples with slightly smaller apples.

On the accounting itself: every response contains a usage block in which the provider lists the tokens consumed. At Anthropic, input_tokens doesn't include cached tokens; in OpenAI-compatible APIs, prompt_tokens does. The field names for the same quantity differ at every provider anyway. If you recalculate costs yourself, you have to normalise this per provider. Otherwise you count cached tokens twice or not at all.

2. The three levers

2.1 Prompt caching

When the start of a prompt (the prefix) repeats, such as the system prompt, tool definitions or documents, the provider can read it from a cache. With Claude Sonnet 5, our 7,200-token prompt cost 1.45 cents uncached, 1.81 cents with a cache write and 0.15 cents from the cache. So the first call is more expensive, and from the second call on you save about 90 %.

The mechanics differ by provider:

  • Anthropic caches only when you ask for it with cache_control, a marker in the request; without the marker, 0 of 4 follow-up calls hit the cache.
  • OpenAI, xAI and Gemini cache automatically. OpenAI from 1,024 tokens , Gemini 3.8 Flash from 4,096 , and Gemini caches only part of the prompt.
  • Below the minimum size, nothing happens: no cache, no error message.

This is what it looks like in the request: at Anthropic, cache_control isn't a header but a field in the content block. OpenAI needs nothing at all. Both report the hit in the response, just under different names (second call each, trimmed from our raw data):

// Anthropic – POST /v1/messages
{
  "model": "claude-sonnet-5-5",
  "system": [{
    "type": "text",
    "text": "You are the assistant of …",
    "cache_control": { "type": "ephemeral" }   // ← cache marker on the block
  }],
  "messages": [ … ]
}
// Response
"usage": {
  "input_tokens": 16,                // only the uncached rest
  "cache_creation_input_tokens": 0,
  "cache_read_input_tokens": 7224,   // ← from the cache
  "output_tokens": 4
}
// OpenAI – POST /v1/responses
{
  "model": "gpt-6.1-sol",
  "instructions": "You are the assistant of …",   // no marker needed
  "input": "Reply with the single word OK."
}
// Response
"usage": {
  "input_tokens": 4576,              // includes the cached tokens
  "input_tokens_details": {
    "cached_tokens": 4573            // ← from the cache
  },
  "output_tokens": 5
}

Part 1 of the series shows what breaks the cache, among other things a change of reasoning effort, which costs the cache on some models and not on others.

2.2 Reasoning effort

Almost every current model can "think" before it answers, and almost every provider names the control differently: reasoning.effort, output_config.effort, thinkingLevel, reasoning_effort. We sent each of the seven usual values from none to max to every model. The result is a patchwork:

  • GPT-6.1 Sol rejects none and minimal.
  • Claude rejects none and minimal but accepts max; the old budget_tokens gets an HTTP 400.
  • Mistral accepted only none and high.
  • xAI can't be switched off: none is rejected.

Thinking is expensive in any case: reasoning tokens are billed as output, even when the answer doesn't show them. Claude Haiku 5.5 billed 53 thinking tokens for an eight-character answer without showing a single one.

In the request, the control sits in a different place at each provider, and in the response the thinking tokens have a different name each time (same arithmetic puzzle with high, trimmed):

// OpenAI – POST /v1/responses
{
  "model": "gpt-6.1-sol",
  "input": "A bat and a ball cost 1.10 EUR …",
  "reasoning": { "effort": "high" }          // ← object reasoning, field effort
}
// Response
"usage": {
  "output_tokens": 24,
  "output_tokens_details": { "reasoning_tokens": 14 }   // included in output_tokens
}
// Anthropic – POST /v1/messages
{
  "model": "claude-haiku-5-5",
  "messages": [ … ],
  "output_config": { "effort": "high" }      // ← object output_config
}
// Response
"usage": {
  "output_tokens": 62,
  "output_tokens_details": { "thinking_tokens": 53 }    // included in output_tokens
}
// Gemini – POST /v1beta/models/gemini-3.8-flash:generateContent
{
  "contents": [ … ],
  "generationConfig": {
    "thinkingConfig": { "thinkingLevel": "high" }   // ← two levels deep
  }
}
// Response: usageMetadata instead of usage
"usageMetadata": {
  "candidatesTokenCount": 5,
  "thoughtsTokenCount": 247          // on top of the 5 answer tokens
}

Routers accept values that the provider itself rejects. Cortecs accepted none for Claude; Anthropic direct didn't. A router that accepts every value is polite. Whether it also follows it is a different question. Part 2 looks into it.

2.3 Routers and hosts

The path of a Claude call through a router

Routers such as Cortecs, EU Router or Requesty bundle many models behind one API. They offer choice, failover and — at the European providers — processing in the EU. The provider a router actually forwards the call to is called the upstream. Fixing it in the request instead of letting the router choose is called pinning. Hosts such as IONOS, by contrast, run the models themselves. For costs, this means:

  • Without cache_control, Claude doesn't cache through any of the routers; with the marker, it caches through all of them. EU Router passes the marker through, although its docs don't mention it.
  • The router picks the upstream. Without a preference, Cortecs sent Claude to Google Vertex — 0 of 4 cache hits. Pinned to AWS Bedrock: 4 of 4.
  • Listed isn't charged. For open-weight models (models whose weights are freely available), the price actually charged by Cortecs and EU Router was up to 170 % above the listed price, because the router chose a more expensive upstream. This surprised us most: for open-weight models through routers, the listed price is more of a floor.
  • Surcharges exist too. EU Router lists Claude Sonnet 5 10 % above Anthropic, OpenRouter and Requesty.

For open-weight models, the host is the deciding factor anyway: the same gpt-oss-120b cached at Cortecs and Requesty, but not at IONOS.

3. Cost transparency: who delivers what?

To control costs per application, customer or department, you need costs per call. Not every provider delivers them:

  • Per call, in the response: xAI in cost_in_usd_ticks , Requesty in usage.cost , EU Router in euros and Cortecs as an integer in micro-euros, contrary to its own API reference.
  • Only aggregated via admin APIs, i.e. management interfaces with their own key: OpenAI reports costs only per day. Anthropic blocks this API for individual organisations.
  • Not via API at all: for the Gemini API there's only Cloud Billing (not measurable).

Where costs appear in the response, they're in the usage block, but in a different unit each time (trimmed from our raw data):

// xAI – POST /v1/responses (grok-4.7)
"usage": {
  "input_tokens": 5809,
  "input_tokens_details": { "cached_tokens": 5760 },
  "output_tokens": 65,
  "cost_in_usd_ticks": 33680000      // ← 10 bn ticks = $1, so $0.003368
}
// Cortecs – POST /v1/chat/completions (gpt-oss-120b)
"usage": {
  "prompt_tokens": 4636,
  "completion_tokens": 16,
  "cost": 631,                       // ← integer in micro-euros, so €0.000631
  "cost_details": { "prompt_cost": 613, "cache_read_cost": 9, "completion_cost": 9 }
}
// EU Router – POST /api/v1/chat/completions (claude-sonnet-5)
"usage": {
  "prompt_tokens": 7239,
  "completion_tokens": 4,
  "cost": 0.0159698,
  "cost_currency": "USD",
  "cost_eur": 0.01419156277          // ← the same amount in euros
}

Where the provider reports costs, our formula matched to the cent: 0.0 % deviation at xAI, Requesty, EU Router and OpenAI's daily totals.

Tip: xAI, Cortecs, EU Router, Requesty and OpenRouter publish prices through an API; OpenAI, Mistral and Gemini publish them only on web pages. A price catalogue like the one we run for CompanyWizard closes exactly this gap. Part 3 shows how to build one from models.dev, LiteLLM and the router APIs.

4. The comparison matrix

As of 19 October 2026. Every cell is measured; details and raw data are in the repository.

Provider Caching (hit on 2nd call) Cache control Cache write in usage Effort values accepted (of 7) Cost per call Prices via API
Anthropic 100 % explicit (cache_control) yes 5 no no
OpenAI 100 % automatic yes (GPT-5.6+) 4–6 no no
xAI 99 % automatic no 5 yes (USD) yes
Google Gemini 3.8 Flash 66 % automatic no 3 no no
Mistral partly (Magistral only) prompt_cache_key no 2 no no
IONOS (open weight) 0 % – no 3–4 no no
Cortecs depends on upstream automatic / pinned yes 7 yes (µEUR) yes (EUR)
EU Router with cache_control passed through yes 5–7 yes (EUR) yes
Requesty with cache_control or auto_cache passed through / automatic yes 6 yes (USD) yes
OpenRouter with cache_control passed through, sticky routing yes 6–7 yes (USD) yes

OpenRouter passes cache_control through, including the 1-hour TTL (the lifetime of the cache entry) , and keeps follow-up calls with a session key, a freely chosen ID per conversation, on the same upstream. This is called sticky routing.

5. Which provider for which purpose

If what matters to you is … … then Why
maximum savings on long, repeated prompts Anthropic or OpenAI direct, set the cache deliberately 90–95 % discount on cache reads, writes visible in usage
caching without code changes OpenAI, xAI they cache automatically, with no marker in the request
cost per call for controlling xAI, Requesty, EU Router, Cortecs cost field in every response
processing in the EU with Claude router with a pinned EU upstream and cache_control otherwise the router decides on upstream and cache
open-weight models at list price the host directly, or a router with a fixed upstream routers that pick the upstream freely charged up to 170 % more
fine control of reasoning effort OpenAI, Anthropic direct routers accept values they can't pass on

6. How we solved it in .NET

In CompanyWizard, all providers are priced with one formula. If a cache price is missing, the input price applies — better slightly overestimated than costs left out:

public static Decimal Calculate(ModelDefinition model, String? promptCacheTtl, TurnTokenUsage tokens)
{
    var billedInput = Math.Max(0, tokens.Input - tokens.CacheRead - tokens.CacheWrite);
    var readPrice   = model.CacheReadPricePer1M ?? model.InputPricePer1M;
    var writePrice  = (promptCacheTtl == OneHourTtl ? model.CacheWrite1hPricePer1M : model.CacheWritePricePer1M)
                      ?? model.InputPricePer1M;

    return (billedInput       * model.InputPricePer1M
          + tokens.CacheRead  * readPrice
          + tokens.CacheWrite * writePrice
          + tokens.Output     * model.OutputPricePer1M) / TokensPerMillion;
}

Source: ModelCostCalculator.cs (excerpt)

The formula is simple. The real work happens before it: normalising the usage fields so that tokens.Input means the same thing at every provider.

Lesson Learned: In CompanyWizard, Anthropic's automatic cache marker at first never hit across several conversation turns. A system message with the current date sat in front of the actual system prompt and made every prefix unique. Only a separate breakpoint on the system prompt (a marker up to which Anthropic caches the prefix), with the date message behind it, brought the hits.

7. How we measured

  • One reference date: all measurements on 19 October 2026, with separate measurement keys.
  • Raw HTTP instead of SDKs: every call is archived as JSON, without headers and without keys.
  • A claims register: 91 statements, each with a status (confirmed, refuted, deviating, not measurable, not measured) and evidence.
  • Everything can be re-measured: scripts, prompts, raw data and tables are at github.com/evanto-media-ag/llm-caching-reasoning-costs.

What we deliberately left open: Gemini 4 Argon couldn't be measured because of a quota limit. We didn't reconcile the admin APIs of Anthropic (blocked for individual organisations) and Cortecs (no management key). We tested DeepSeek only through routers, not through its own API. And a measurement on one day is a snapshot. Prices and models change every month.

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.

8. Measure it yourself with curl

To see the effect yourself, you don't need a programming language or an SDK, just curl, jq and an API key in an environment variable. Both examples use our test prompt from the repository. At about 7,200 tokens it is safely above the minimum size for caching. With a short prompt you would see nothing, because below that size caching silently doesn't happen.

The examples run in bash or zsh, so on macOS and Linux, and on Windows in WSL or Git Bash. Set the key once per terminal session beforehand, e.g. with export ANTHROPIC_API_KEY=… or export XAI_API_KEY=…. First we download the prompt. -s suppresses the progress bar, -O saves the file as large.txt in the current directory:

curl -sO https://raw.githubusercontent.com/evanto-media-ag/llm-caching-reasoning-costs/main/prompts/large.txt

8.1 Caching and reasoning effort at Anthropic

The same call three times, with a cache marker on the system prompt and changing reasoning effort:

ask() {
  jq -n --rawfile sys large.txt --arg effort "$1" '{
    model: "claude-haiku-5-5",
    max_tokens: 256,
    system: [{type: "text", text: $sys, cache_control: {type: "ephemeral"}}],
    messages: [{role: "user", content: "Reply with the single word OK."}],
    output_config: {effort: $effort}
  }' | curl -s https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "content-type: application/json" \
    -d @- | jq '.usage'
}

ask high; ask low; ask high

What happens here:

  • ask() { … } defines a shell function called ask. The call is written out only once and can be repeated with ask high, ask low and so on. The word after ask ends up in $1 and thus in the effort value.
  • jq -n builds the JSON request. --rawfile sys large.txt reads the prompt into the variable $sys and escapes quotes and line breaks along the way; --arg effort "$1" passes the effort value. Hand-written JSON would break on the long prompt.
  • cache_control: {type: "ephemeral"} is the cache marker. Anthropic keeps the entry for 5 minutes, so all three calls have to run within that time. For 1 hour, the marker is {type: "ephemeral", ttl: "1h"}.
  • | curl … -d @- sends the JSON produced by jq as the request body. @- means: read the body from standard input.
  • anthropic-version is a required header at Anthropic.
  • | jq '.usage' shows only the usage block of the response. To see the full response, write jq ..

This is what it looked like for us (Claude Haiku 5.5, raw data from 2026-10-19):

Call effort cache_creation_input_tokens cache_read_input_tokens output_tokens of which thinking_tokens
1 high 7,223 0 19 15
2 low 0 7,223 4 0
3 high 0 7,223 35 31

Call 1 writes the cache, calls 2 and 3 read it. So on this model, changing the reasoning effort didn't break the cache. The answer was "OK" every time, yet at high up to 35 output tokens were billed instead of 4. Your numbers will differ by a few tokens, and more so for thinking: effort is a guideline, and the model decides for itself whether it actually thinks. In a control run on 2026-10-19, call 1 didn't think at all and call 3 used 21 thinking tokens.

To vary it: without cache_control, cache_read_input_tokens stays at 0. Other effort values and the control names of the other providers are in section 2.2.

8.2 Recalculating costs at xAI

xAI caches automatically and reports the cost in every response. Two identical calls are enough:

ask_xai() {
  jq -n --rawfile sys large.txt '{
    model: "grok-4.7",
    instructions: $sys,
    input: "Reply with the single word OK.",
    max_output_tokens: 16,
    store: false
  }' | curl -s https://api.x.ai/v1/responses \
    -H "Authorization: Bearer $XAI_API_KEY" \
    -H "content-type: application/json" \
    -d @- | jq '.usage | {input_tokens, cached: .input_tokens_details.cached_tokens,
                         output_tokens, reasoning: .output_tokens_details.reasoning_tokens,
                         cost_in_usd_ticks}'
}

ask_xai; ask_xai

New compared with 8.1:

  • xAI's Responses API expects the system prompt as instructions and the question as input.
  • store: false makes sure xAI doesn't store the response on its servers.
  • max_output_tokens: 16 limits the answer. Output tokens still came out higher for us, because reasoning comes on top.
  • The jq filter at the end picks only the five values you need for the recalculation from the usage block and gives them short names.

For us, the second call reported 5,809 input tokens, 5,760 of them from the cache, 65 output tokens (64 of them reasoning) and 33,680,000 ticks. With the prices from section 1 you can recalculate this:

(5,809 − 5,760) × $2.00 =    $98
         5,760  × $0.50 = $2,880
            65  × $6.00 =   $390
                          $3,368 per 1M tokens = $0.003368

Ten billion ticks are one dollar, so $0.003368 is exactly 33,680,000 ticks. The first call, with hardly any cache hits, cost 102,860,000 ticks, about three times as much.

At other providers the endpoint, the control name and the usage fields change, but the principle stays: send the prompt twice, compare the usage, recalculate with the price table. Part 3 shows which field is where.

Conclusion: five rules for realistic LLM costs

  1. Count all five token types. Input, cache read, cache write, output, reasoning.
  2. Caching isn't a switch, it's prompt architecture. Stable parts first, changing parts last, mind the minimum size.
  3. Check effort values against the specific model. An accepted value isn't a followed value.
  4. Pin the upstream when you use a router, if cache or price matters.
  5. Measure for yourself. Section 8 shows how with two curl calls. Where providers report costs, their figures matched ours to the cent.

The series

  • Part 1: Prompt caching compared — what carries the cache and what breaks it
  • Part 2: Reasoning effort compared — seven values, twelve interpretations (coming soon)
  • Part 3: Cost transparency — formulas, field names, price sources (coming soon)
  • Part 4: Routers compared — Cortecs, EU Router, Requesty, OpenRouter (coming soon)

References


Planning an AI application and want a realistic cost estimate up front? Let's spend 30 minutes on your workflow. The call is free, and you leave with suggestions you can put into practice right away. Book a 30-minute call

Note: Article created with AI assistance.

NEWSLETTER

Four to six times a year, no marketing noise.

One pattern, one case, one recommendation. Signup with double opt-in, unsubscribe at any time.