{"slug": "your-claude-agent-costs-9x-more-without-prompt-caching", "title": "Your Claude Agent Costs 9x More Without Prompt Caching", "summary": "A measured Claude Opus 5.5 agent session of 35 calls cost $2.39 at Anthropic list prices because 97.5% of prompt tokens were cache hits, versus $22.08 without prompt caching — a 9.2x difference for identical tokens, according to the analysis. The piece cites a November 4 AI Engineer YouTube talk by George Cameron, co-founder of benchmarking firm Artificial Analysis, who said Claude Opus 5.5 at max effort averaged about $21 per task on the AA Briefcase benchmark and that a 165-step task would exceed $200 with zero cache hits. It publishes a 101-line Python 3.8+ script, cachecheck.py, that reads Claude Code session logs in ~/.claude/projects and prices each call twice using Anthropic's rates — $4.00 per million uncached input tokens, $20.00 output, $5.00 for a 5-minute cache write, $8.00 for a 1-hour write and $0.20 per cache hit.", "body_md": "A real Claude Opus 5.5 agent log: 97.5% of prompt tokens were cache hits, so 35 calls came to $2.39 at list prices instead of $22.08. A 101-line script checks your own logs, and shows why a broken cache can cost nearly double.\n\nWithout prompt caching, the Claude agent session I measured would have cost 9.2x as much at list prices: $22.08 instead of $2.39, for exactly the same tokens. Below is a script that reads your own Claude Code logs and tells you your cache hit rate, what it saves you, and what it would cost if it broke.\n\nOn November 4 the AI Engineer YouTube channel posted [“Without Caching, Your AI Agent Costs 10x More”](https://www.youtube.com/watch?v=prGP0huECPo), a 20-minute talk by George Cameron, co-founder of the benchmarking firm Artificial Analysis.\n\nParaphrasing the captions: on their AA Briefcase benchmark, Claude Opus 5.5 at max effort averaged about $21 per task, and the task he walks through took 165 steps. The cost drivers he names are the number of steps (which he calls the biggest), input tokens per call, and the share of those that are cache hits. With zero cache hits, he said, that $21 task would cost over $200.\n\nAn agent re-sends its whole history on every step: system prompt, tools, every earlier tool result.\n\nPrompt caching keeps that prefix: you pay extra to write it once, then a fraction to read it back. Anthropic’s pricing page, read today:\n\n``` bash\n+---------------------+---------------+| Claude Opus 5.5     | $ per million |+=====================+===============+| Input (uncached)    | $4.00         || Output              | $20.00        || Cache write, 5 min  | $5.00         || Cache write, 1 hour | $8.00         || Cache hit           | $0.20         |+---------------------+---------------+\n```\n\nA cache hit costs one-twentieth of a normal input token. The talk cites a roughly 10x discount on cache-hit input; for Opus 5.5 it is 20x. Input is most of an agent’s tokens, so the hit rate decides most of the bill.\n\nClaude Code writes every session to ~/.claude/projects/<folder>/<session>.jsonl. Each assistant line carries the API's usage object, with three input buckets: input_tokens (uncached), cache_creation_input_tokens (written) and cache_read_input_tokens (hits), plus a cache_creation object that splits the writes into 5-minute and 1-hour.\n\nOne API call can be logged on several lines, so the script counts each message ID once. A 1-hour write costs 1.6x a 5-minute one, so the split matters.\n\nStandard library only, Python 3.8+. Edit PRICES for other models.\n\n```\n\"\"\"cachecheck.py - is prompt caching working in your Claude agent?Reads Claude Code / Agent SDK session logs (*.jsonl) and prices everycall twice: at list price, and as if no token had been a cache hit.usage: python3 cachecheck.py [file-or-dir ...]  (default ~/.claude/projects)\"\"\"import json, sysfrom pathlib import Path# $ per million tokens: input, output, 5m write, 1h write, cache hit.# Basis: platform.claude.com/docs/en/about-claude/pricing, read 2026-11-04.PRICES = {    \"claude-fable-5-1\":  (10.00, 50.00, 12.50, 20.00, 0.25),    \"claude-opus-5-5\":   (4.00, 20.00, 5.00, 8.00, 0.20),    \"claude-sonnet-5-5\": (2.00, 10.00, 2.50, 4.00, 0.10),    \"claude-haiku-5-5\":  (0.10, 0.50, 0.125, 0.20, 0.01),}def price_for(model):    for name, p in PRICES.items():        if model.startswith(name):            return p    return Nonedef calls(files):    seen = set()    for f in files:        for line in open(f, encoding=\"utf-8\", errors=\"replace\"):            try:                rec = json.loads(line)            except ValueError:                continue            msg = rec.get(\"message\") if isinstance(rec, dict) else None            if not isinstance(msg, dict) or not isinstance(msg.get(\"usage\"), dict):                continue            mid = msg.get(\"id\")            if mid is not None and mid in seen:  # logged once per block                continue            seen.add(mid)            yield f, str(msg.get(\"model\")), msg[\"usage\"]def main(args):    roots = [Path(a) for a in args] or [Path.home() / \".claude/projects\"]    files = []    for r in roots:        if not r.exists():            sys.exit(f\"usage: cachecheck.py [file-or-dir ...]\\nnot found: {r}\")        files += sorted(r.rglob(\"*.jsonl\")) if r.is_dir() else [r]    tot = dict(n=0, inp=0, w5=0, w1=0, hit=0, out=0, cost=0.0, nocache=0.0,               hitcost=0.0, rewrite=0.0)    short, skipped, last, count = [], set(), {}, {}    for f, model, u in calls(files):        p = price_for(model)        if p is None:            skipped.add(model)            continue        cc = u.get(\"cache_creation\")        cc = cc if isinstance(cc, dict) else {}        w1 = cc.get(\"ephemeral_1h_input_tokens\") or 0        w5 = (u.get(\"cache_creation_input_tokens\") or 0) - w1        inp, hit = u.get(\"input_tokens\") or 0, u.get(\"cache_read_input_tokens\") or 0        out = u.get(\"output_tokens\") or 0        cost = (inp*p[0] + out*p[1] + w5*p[2] + w1*p[3] + hit*p[4]) / 1e6        nocache = ((inp + w5 + w1 + hit)*p[0] + out*p[1]) / 1e6        key = (f, model)        count[key] = count.get(key, 0) + 1        if key in last and hit < last[key]:  # read less than last call cached            short.append((Path(f).name, count[key], last[key] - hit))        last[key] = hit + w5 + w1        for k, v in zip((\"inp\", \"w5\", \"w1\", \"hit\", \"out\", \"cost\", \"nocache\"),                        (inp, w5, w1, hit, out, cost, nocache)):            tot[k] += v        tot[\"n\"] += 1        tot[\"hitcost\"] += hit * p[4] / 1e6        # a lost hit is written again at the TTL this call used        tot[\"rewrite\"] += hit * (p[3] if w1 > w5 else p[2]) / 1e6    prompt = tot[\"inp\"] + tot[\"w5\"] + tot[\"w1\"] + tot[\"hit\"]    if not prompt or not tot[\"cost\"]:        names = \", \".join(sorted(skipped)) or \"none\"        sys.exit(f\"no priced calls with tokens (skipped: {names})\")    print(f\"files {len(files)}   API calls {tot['n']}   (log: usage per call)\")    print(f\"prompt tokens   {prompt:>12,}\")    print(f\"  cache hits    {tot['hit']:>12,}  {tot['hit']/prompt:6.1%}\")    print(f\"  cache writes  {tot['w5'] + tot['w1']:>12,}  \"          f\"{(tot['w5'] + tot['w1'])/prompt:6.1%}  (1h: {tot['w1']:,})\")    print(f\"  uncached      {tot['inp']:>12,}  {tot['inp']/prompt:6.1%}\")    print(f\"output tokens   {tot['out']:>12,}\")    print(f\"calls that read less than the previous call cached: {len(short)}\")    for name, n, gap in short[:5]:        where = f\"{name} \" if len(files) > 1 else \"\"        print(f\"  {where}call {n}: {gap:,} cached tokens not read back\")    print(f\"cost at list prices  ${tot['cost']:9.2f}\")    print(f\"cost with no cache   ${tot['nocache']:9.2f}   (same tokens, 0 hits)\")    print(f\"caching saved {1 - tot['cost']/tot['nocache']:.0%}; \"          f\"no cache = {tot['nocache']/tot['cost']:.1f}x the cost\")    h = tot[\"hit\"] / prompt    print(\"what-if: hits fall, lost hits are re-written (same tokens)\")    for target in [h] + [t for t in (0.9, 0.8, 0.5, 0.0) if t < h]:        lost = 1 - target / h if h else 0        c = tot[\"cost\"] + lost * (tot[\"rewrite\"] - tot[\"hitcost\"])        print(f\"  hit rate {target:6.1%}  ->  ${c:8.2f}  \"              f\"({c / tot['nocache']:.2f}x the no-cache cost)\")    if skipped:        print(f\"skipped models with no price: {', '.join(sorted(skipped))}\")if __name__ == \"__main__\":    main(sys.argv[1:])\npython3 cachecheck.py                  # every session on this machinepython3 cachecheck.py path/to/one.jsonl\n```\n\nA bad path gets a usage line and exit code 1.\n\nI ran it on the Claude Code log of the scheduled Claude session that built this article, frozen at 35 API calls, all Claude Opus 5.5:\n\n```\nfiles 1   API calls 35   (log: usage per call)prompt tokens      5,458,781  cache hits       5,323,005   97.5%  cache writes       135,702    2.5%  (1h: 135,702)  uncached                74    0.0%output tokens         12,091calls that read less than the previous call cached: 1  call 2: 17,020 cached tokens not read backcost at list prices  $     2.39cost with no cache   $    22.08   (same tokens, 0 hits)caching saved 89%; no cache = 9.2x the costwhat-if: hits fall, lost hits are re-written (same tokens)  hit rate  97.5%  ->  $    2.39  (0.11x the no-cache cost)  hit rate  90.0%  ->  $    5.59  (0.25x the no-cache cost)  hit rate  80.0%  ->  $    9.85  (0.45x the no-cache cost)  hit rate  50.0%  ->  $   22.62  (1.02x the no-cache cost)  hit rate   0.0%  ->  $   43.91  (1.99x the no-cache cost)\n```\n\n97.5% of 5.46 million prompt tokens were cache hits, and only 74 tokens were sent uncached. At list prices that is $2.39 versus $22.08 with no caching: 9.2x, or an 89% saving.\n\nOn call 2, 17,020 tokens that call 1 had cached were not read back. The log doesn’t say why; the script just flags it, which is your cue to look.\n\nThe what-if rows keep the same tokens and move hits into writes, at the TTL each call used.\n\nAt 80% hits the cost is still under half the no-cache price. With these 1-hour writes it breaks even just above 50%. At 0% it is 1.99x the no-cache cost, because every one of those tokens would be a 1-hour write at $8 per million, twice the plain $4 input price, and never read back.\n\nThis session used 1-hour writes on every call. On a copy with every write relabelled as 5-minute, the last what-if row reads:\n\n``` php\nhit rate   0.0%  ->  $   27.54  (1.25x the no-cache cost)\n```\n\nCheaper writes, smaller penalty: 1.25x at 0% hits. Either way, “caching is on” is not the same as “caching works”. If something early in the prompt changes every call, such as a timestamp in the system prompt, everything after it is written and paid for on every request and never read.\n\nA hard-coded result would look identical, so I mutated the input. I rewrote the same log with every cached token moved into input_tokens: the script then reports 0 hits, $22.08 both ways and \"1.0x the cost\". Same tokens, no hits, so the two costs meet. Then a bad path: python3 cachecheck.py nope prints the usage line and exits 1.\n\nIn my log, every call records \"promptCacheRequested\": \"1h\" from Claude Code. Your own agent on the Messages API does not cache unless you ask: the docs say you opt in with cache_control. The simplest form is automatic caching, one field at the top level of the request:\n\n```\nresponse = client.messages.create(    model=\"claude-opus-5-5\",    max_tokens=1024,    cache_control={\"type\": \"ephemeral\"},    system=SYSTEM,    messages=history,)\n```\n\nThe default lifetime is 5 minutes; {\"type\": \"ephemeral\", \"ttl\": \"1h\"} buys an hour at 2x the input price. Pick 1 hour only if your agent pauses longer than 5 minutes between steps.\n\nTo use the script on your own agent, log each response as one JSONL line shaped {\"message\": {\"id\": ..., \"model\": ..., \"usage\": ...}}.\n\nThe docs say the cache follows the order tools, then system, then messages, and a change at one level invalidates that level and everything after it. Editing a tool definition clears the whole cache. Toggling web search, citations or fast mode clears the system and message cache. Prompts below the minimum, 512 tokens for Opus 5.5, are not cached at all.\n\nIn practice: keep tools fixed for the session, put anything that changes per call (dates, retrieved documents) after the cached part, and don’t reorder history.\n\nIt prices tokens only, at list prices: no batch, fast-mode, web search or data-residency pricing. On a Pro or Max plan, usage normally counts against plan limits, so read the dollars as an API equivalent. Models not in PRICES are skipped and named. Logs without the cache_creation split are priced as 5-minute writes. The what-if assumes the token counts stay the same when hits are lost. One session is one data point: run it on yours.\n\nReproduce it: cachecheck.py prints every number in the output blocks of sections 06 to 08; the per-token prices are in PRICES. The 5-minute and no-cache runs use copies of the log with the writes relabelled, or with all cached tokens moved into input_tokens. The $21, $200 and 165 steps come from the talk; the prices come from Anthropic's pricing page.\n\nOn a real Claude Opus 5.5 agent log, caching cut the list-price cost 9.2x, matching the talk’s warning from a different workload. What decides your cost is your hit rate, and a broken cache with 1-hour writes costs nearly twice as much as no cache at all. It takes one command to see which one you have.\n\nWhat hit rate does your agent get, and did anything in this script surprise you?\n\nIf this saves you a bill, a clap helps others find it.\n\nFollow me for daily LLM deep-dives.\n\n[Your Claude Agent Costs 9x More Without Prompt Caching](https://pub.towardsai.net/your-claude-agent-costs-9x-more-without-prompt-caching-be6aab53375e) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "url": "https://wpnews.pro/news/your-claude-agent-costs-9x-more-without-prompt-caching", "canonical_source": "https://pub.towardsai.net/your-claude-agent-costs-9x-more-without-prompt-caching-be6aab53375e?source=rss----98111c9905da---4", "published_at": "2026-11-04 06:54:45+00:00", "updated_at": "2026-11-04 07:23:08.734281+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "large-language-models", "ai-infrastructure"], "entities": ["Claude Opus 5.5", "Anthropic", "Claude Code", "George Cameron", "Artificial Analysis", "AI Engineer", "AA Briefcase"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/your-claude-agent-costs-9x-more-without-prompt-caching", "markdown": "https://wpnews.pro/news/your-claude-agent-costs-9x-more-without-prompt-caching.md", "text": "https://wpnews.pro/news/your-claude-agent-costs-9x-more-without-prompt-caching.txt", "jsonld": "https://wpnews.pro/news/your-claude-agent-costs-9x-more-without-prompt-caching.jsonld"}}