Docs

Prompt caching

Mark a stable prompt prefix once with cache_control. Every subsequent call that reuses the prefix bills it back at a fraction of input price — 10% on Anthropic, OpenAI and Gemini alike. Kunavo's routing layer keeps the cache warm by pinning your conversation to the same upstream key.

このドキュメントは英語版です。日本語版のクイックスタートはこちら:日本語版クイックスタート — 日本から Claude API を呼び出す

On a 30K-token system prompt with Claude Sonnet 4.6, a cache read costs $0.12 / 1M instead of $1.2 / 1M. For an agent that re-sends the same context 50 times a session, the cache saves ~90% of the input bill.

How to enable it

Add a cache_control: { type: "ephemeral" } breakpoint on any system block, message, or tool definition. The model caches the span up to that breakpoint on the first call; subsequent calls that share the same prefix bytes hit the cache.

cache_example.py
from openai import OpenAI

client = OpenAI(
    api_key="sk-kn-...",
    base_url="https://api.kunavo.com/v1",
)

# Mark the large, stable prefix once with cache_control.
# Every subsequent call that sends the same prefix reads it back
# from cache at ~10% of the input rate on Anthropic models.
SYSTEM = [
    {
        "type": "text",
        "text": open("long_system_prompt.txt").read(),
        "cache_control": {"type": "ephemeral"},
    },
]

resp = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": "Summarize the doc above in 3 bullets."},
    ],
)

u = resp.usage
# OpenAI-compat fields populated by Kunavo:
# - prompt_tokens_details.cached_tokens — read from cache
# - cache_creation_input_tokens          — written to cache this call
print(u.prompt_tokens_details.cached_tokens,
      getattr(u, "cache_creation_input_tokens", 0))

Identical wire-format on the native Messages API — cache_control passes through to Claude untranslated:

messages_api.sh
# Native Anthropic Messages API — cache_control passes through untranslated.
curl https://api.kunavo.com/v1/messages \
  -H "Authorization: Bearer $KUNAVO_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "system": [
      {
        "type": "text",
        "text": "<your large stable system prompt>",
        "cache_control": { "type": "ephemeral" }
      }
    ],
    "messages": [
      { "role": "user", "content": "Summarize the doc above in 3 bullets." }
    ],
    "max_tokens": 1024
  }'
Stable bytes matter. The cache key is content-addressed. A single different character anywhere in the prefix invalidates the cache for that span. Put dynamic content (timestamps, user names, per-call IDs) after the cached block, not inside it.

Pricing

Cache rates derive from each model's input price, at the vendor's own ratios. Cache reads bill at 0.10× input on Anthropic, OpenAI and Gemini models alike. Cache writes bill at 1.25× input on every Claude model and on GPT-6 Astra, GPT-5.6 Sol, GPT-5.6 Terra and GPT-5.6 Luna. Kunavo bills Claude's 1-hour-TTL writes at the same 1.25×, below Anthropic's . Every other model has no separate write price: its writes bill at the plain input rate. Either way, a prefix that is reused even once costs less cached than sent fresh. Every class carries the model's own discount, so its cache prices sit at the same discount off the vendor's list as its input and output.

Anthropic

ModelInputCache readCache write
Claude Fable 5$7$0.70.10×$8.751.25×
Claude Opus 5$2$0.20.10×$2.51.25×
Claude Opus 4.8$2.5$0.250.10×$3.131.25×
Claude Opus 4.7$2$0.20.10×$2.51.25×
Claude Opus 4.6$2$0.20.10×$2.51.25×
Claude Sonnet 5$2$0.20.10×$2.51.25×
Claude Sonnet 4.6$1.2$0.120.10×$1.51.25×
Claude Haiku 4.5$0.4$0.040.10×$0.51.25×

Google

ModelInputCache readCache write
Gemini 3.8 Flash$0.525$0.05250.10×$0.5251×
Gemini 3.7 Flash$0.525$0.05250.10×$0.5251×
Gemini 3.6 Flash$1.05$0.1050.10×$1.051×
Gemini 3.1 Pro$0.7$0.070.10×$0.71×
Gemini 2.5 Flash$0.09$0.0090.10×$0.091×

OpenAI

ModelInputCache readCache write
GPT-6 Astra$4$0.40.10×$51.25×
GPT-5.6 Sol$2$0.20.10×$2.51.25×
GPT-5.6 Terra$0.7$0.070.10×$0.8751.25×
GPT-5.6 Luna$0.07$0.0070.10×$0.08751.25×
GPT-5.5$2$0.20.10×$21×
Long prompts bill at the vendor's long-context tier. Past the threshold the whole request bills at a multiple of the model's input and output prices, cache reads and writes included: over 200K input tokens on Gemini 3.1 Pro (2× input, 1.5× output); over 272K input tokens on GPT-6 Astra, GPT-5.6 Sol, GPT-5.6 Terra, GPT-5.6 Luna and GPT-5.5 (2× input, 1.5× output). Claude has no long-context surcharge: its 1M context bills at the standard rate.
The catalog floor is cache-aware. Kunavo bills max(catalog_cost, upstream × markup) per call — the floor is computed against the cached-input rate, not the fresh rate, so the savings reach you instead of being flattened.
The rates above are catalog prices. The real per-call bill is computed from the upstream credits the model actually consumed (with a markup), then floored at the catalog rate. On cache hits the upstream charges fewer credits — so the upstream-times-markup branch usually drops below catalog and the catalog branch wins. In short: the catalog rate is your worst-case price; cached calls may land below it but never above.

Affinity routing keeps the cache warm

Upstream prompt caches are per-API-key. If every request lands on a random upstream key, the cache hit rate is ~1/N. Kunavo derives a stable hash from your system prompt + first user message and routes all calls with the same prefix to the same upstream key — using weighted rendezvous hashing, so only ~1/N keys are remapped when the pool changes.

You don't need to configure anything. Affinity routing applies automatically to all chat calls. (Image / video / TTS use random routing — there is no per-key cache for those modalities.)

Dashboard metrics

Once your calls start hitting the cache, /app/usage surfaces two rolled-up metrics for the selected window:

  • Hit rate — cached input tokens ÷ total input tokens. Exact, computed from per-call token counts.
  • Saved — USD saved this window, computed per model with its real cache-read rate (no flat-average approximations). This is an estimate at catalog rates — since real bills are floored at catalog and cached calls can land below the floor, the number is a tight upper bound on actual savings. For typical chat workloads the two are within a few percent.

Each call's detail page (/app/usage/<id>) shows the cached + cache-write token counts when they're non-zero, and the actual billed amount.

Response fields

Kunavo surfaces cache tokens in both response shapes. On the OpenAI-compatible /v1/chat/completions endpoint:

FieldMeaning
usage.prompt_tokensTotal input tokens (includes cached + cache-write).
usage.prompt_tokens_details.cached_tokensSubset of input served from cache.
usage.cache_creation_input_tokensTokens written to cache this call (billed at the model's cache-write rate, in the tables above).

On the native Messages API at /v1/messages, the Anthropic-original fields pass through:

FieldMeaning
input_tokensFresh (uncached) input.
cache_read_input_tokensServed from cache, billed 0.10× input.
cache_creation_input_tokensWritten to cache this call (billed 1.25× input — 1-hour-TTL writes too, below Anthropic's 2×).

Where to go next

  • Messages API reference — native Anthropic shape with worked cache examples.
  • Claude prompt caching — the usage-object numbers that diagnose a cache, and the three ways routing through a gateway breaks a hit rate.
  • Billing & the ledger — how the cache-aware max() floor interacts with upstream cost.
  • Full pricing table — fresh input / output prices for every chat model, with the cache sub-table at the bottom.