# Anatomy of a $5 "Good Morning"

> Source: <https://karanbansal.in/blog/good-morning-tax/>
> Published: 2026-07-08 13:24:13+00:00

Last month a thread hit the front page of r/ClaudeAI: [someone topped up $250 in API credits, typed "hey", and watched about $20 disappear](https://reddit.com/r/ClaudeAI/comments/1um9j1u/fable_5_max_hit_limit_i_topped_up_250_then_one/). 1,612 upvotes, 421 comments. The comments got the diagnosis roughly right (caching, context, "stupid tax") and then the thread scrolled away, because nobody could link a writeup that showed the actual mechanics with actual numbers.

So I priced every request Claude Code has ever made from my machine: 923 transcript files, 12,992 API calls. It turns out I have been paying the same tax, repeatedly, without noticing. My most expensive requests ever are not big refactors or deep research runs. They are an acknowledgment, a menu choice, a couple of impatient nudges, and a parade of good mornings.

This post is the anatomy of one of them: "Good morning.. let's do clean up now?", a message that metered **66 fresh input tokens and billed $5.18**. Along the way it answers the thread's question properly, because the mechanics generalize: **your bill is not proportional to what you type. It is proportional to what the harness re-establishes.**

Every message you send re-sends the entire session context. Prompt caching makes that nearly free while you stay active, because reading cache is 20x cheaper than writing it and every read refreshes the timer. Claude Code's main loop uses a 1-hour cache: come back at minute 59 and a huge session costs cents, come back at minute 61 and you rebuy the whole context at 2x the base input price. Subagents run on the cheaper 5-minute tier. Compaction resets the meter. [A 60-line script at the end](#script) prices your own transcripts.

This is a sequel to [Prompt Caching Doesn't Cache Prompts](/blog/prompt-caching/). That post covered what the cache stores and why prefixes must match exactly. This one is about what those rules do to a real month of agent usage, receipt by receipt. I use Claude Code on a subscription, so no invoice shows these dollars: every figure here is my metered token counts priced at Anthropic's published API list rates, a shadow bill. Subscription usage limits meter the same underlying consumption, so the mechanics cost you either way; the dollars just make them visible.

## The receipt

On June 27 I opened a session that had been idle overnight and typed 41 characters: "Good morning.. let's do clean up now? 230". Here is the usage block the API returned, verbatim from the transcript on my disk:

```
"usage": {
  "input_tokens": 66,
  "cache_creation_input_tokens": 512749,
  "cache_read_input_tokens": 15877,
  "output_tokens": 1688,
  "cache_creation": {
    "ephemeral_1h_input_tokens": 512749,
    "ephemeral_5m_input_tokens": 0
  }
}
```

Priced at [Opus 4.8 list rates](https://platform.claude.com/docs/en/build-with-claude/prompt-caching):

| Line item | Tokens | Rate | Cost |
|---|---|---|---|
| Fresh input (what I typed, plus wrappers) | 66 | $5 / MTok | $0.0003 |
| Cache read (the stable head that survived) | 15,877 | $0.50 / MTok | $0.008 |
Cache write, 1-hour tier (the whole session, rebuilt) | 512,749 | $10 / MTok | $5.13 |
| Output (the reply) | 1,688 | $25 / MTok | $0.04 |
Total | $5.18 |

99% of the bill is one line item, and it has nothing to do with what I typed. The session's cache had expired overnight, so the API re-processed the entire 500K-token conversation history and wrote it back into cache at $10 per million tokens. My greeting was a rounding error riding on a re-establishment.

It gets better. My single most expensive request ever, across all 12,992, was the message "done.. single commit shows up now": six words of acknowledgment that triggered a 767,616-token cache write. Here is the leaderboard (my five priciest Opus 4.8 turns, at list prices):

| What I typed | Cache write | Bill |
|---|---|---|
| "done.. single commit shows up now" | 767,616 | $7.72 |
| "both 1 and 2 Submit maximum" | 629,347 | $6.35 |
| "Morning, it's july 5 I tink we should do next..." | 568,315 | $5.74 |
| "lets' trigger job to do clean up of 279 today?" | 525,814 | $5.31 |
| "Good morning.. let's do clean up now? 230" | 512,749 | $5.18 |

Typos preserved, because that is the point: these are throwaway messages. Nobody types "both 1 and 2" expecting it to be a six-dollar decision. (An Opus 4.7 receipt from the same archive deserves an honorable mention: telling the agent that em dashes scream AI-generated cost $6.06.)

## What a tiny message actually sends

The LLM API is stateless. It remembers nothing between calls. So on every turn, the harness re-sends everything the model needs to act like your session exists: the system prompt, tool definitions, your CLAUDE.md and memory files, and the full transcript so far including every file it read and every command output it saw. Your new message is the last few tokens on top of that stack.

Across my whole archive, the average API call carries about 145,000 input-side tokens. The messages that trigger those calls are often a dozen words. That ratio is normal, healthy agent operation, and it is the reason prompt caching exists: [as Part 1 covered](/blog/prompt-caching/), the model's computed state for a stable prefix can be stored once and reused, so the re-sent mountain usually costs a twentieth of the fresh price.

Usually. The word carrying that sentence is the subject of this post.

## Four bills for the same kind of message

Here are four short messages from my own sessions, each landing on a large established context, each metered completely differently. Same kind of typing, up to 26x apart in price:

| Scenario | Cache read | Cache write | Bill |
|---|---|---|---|
Warm: "current status?", 4 minutes after the last turn | 504,720 | 5 | $0.26 |
Expired: "good morning" after an overnight gap | 15,877 | 512,749 | $5.18 |
Invalidated: a message 6 seconds after the last turn | 15,877 | 396,090 | $4.10 |
Post-compact: first message after /compact | 16,559 | 9,575 | $0.20 |

The warm case is caching working as designed: half a million tokens re-established for a quarter. The other three rows are the three distinct ways the meter resets. Each gets a section.

## The 60-minute cliff

Anthropic sells two cache lifetimes: a 5-minute TTL at 1.25x the base input price, and a 1-hour TTL at 2x. Claude Code buys the 1-hour tier for your main session, and the receipts show why that is the right call: the TTL refreshes for free every time the cache is read, so as long as you reply within the hour, the timer keeps resetting and you keep paying read prices. An active session stays warm indefinitely.

Leave a gap longer than the TTL and the entry is gone. The next message, however trivial, re-processes and re-writes the entire context. In one long-running session of mine, the same kind of throwaway message got both treatments, and you can see the exact shape of the cliff:

Every message inside the hour cost between $0.04 and $0.35, no matter that each one was dragging half a million tokens of context behind it. The two messages that arrived past the hour cost $4.75 and $4.88. The cliff is not gradual. It is a step function at exactly the TTL, and the height of the step is your context size times $10 per million.

One detail in the cold receipts is worth noticing: even after a 12-hour gap, 15,877 tokens still read from cache. That segment is the stable head, the system prompt and tool definitions that are byte-identical across every session on my machine. Caching is prefix-based, so any recent activity in any session keeps that shared head warm. What expires is everything unique to your session: the transcript. You never rebuy Claude Code; you rebuy your own history.

## The other way to pay full price

The diamond in that chart is the failure mode that has nothing to do with time. Six seconds after the previous turn, with the cache as warm as it gets, one of my requests re-wrote 396,090 tokens: $4.10. The read counter stopped at the stable head and everything after it recomputed.

That is invalidation, and it is Part 1's exact-prefix rule collecting its fee. The cache matches byte-for-byte from the front; change anything early in the assembled context and every token after the change point is a miss. My transcript does not record which knob it was, but in Claude Code the usual suspects are all mid-session config changes: editing CLAUDE.md or memory files, adding or removing an MCP server or tool, or switching models (a different model has a different cache entirely). In my receipt the stable head still read from cache, which rules out a model switch; whatever changed sat just after the head. [Anthropic's own engineering post](https://claude.com/blog/lessons-from-building-claude-code-prompt-caching-is-everything) treats cache hit rate as an SEV-worthy production metric for precisely this reason: one careless prefix change and the fleet re-buys its context.

The practical read: the cliff you can schedule around, but invalidation you cause. Config changes are cheap at session start and expensive at hour three.

## Subagents run on a different meter

When Claude Code fans work out to subagents, each one gets a fresh, isolated context: that is the feature. It also means each one re-establishes its own prefix, because isolation and re-establishment are the same thing viewed from different sides.

The interesting part is in the tier data. Recall the two cache lifetimes. Here is where every cache-write token in my archive went:

| 1-hour tier writes | 5-minute tier writes | |
|---|---|---|
Main sessions | 63,665,722 | 21 |
Subagents | 10,297 | 40,426,737 |

Out of 63.7 million main-loop write tokens, exactly 21 went to the 5-minute tier. Out of 40.4 million subagent write tokens, 0.03% went to the 1-hour tier. This is the harness choosing deliberately, and the logic is readable straight from the pricing: your main session idles while you think, review, and get coffee, so it pays 2x for an hour of forgiveness. A subagent never idles; it runs flat out and dies, so it takes the 1.25x tier and never wastes a cent on durability it cannot use.

Cheap tier or not, fan-out multiplies re-establishment. My heaviest fan-out spawned 232 subagents in one session: 1,930 API calls, 9.8 million cache-write tokens, 75 million cache-read tokens, $117 at list prices while I watched a progress bar. None of that is waste if the parallelism does real work. All of it is waste if a single agent with a shared context could have done the job. "Subagents fix context rot" is folk wisdom precisely because nobody prices the other side of the trade; the ledger above is what the other side looks like.

And note the last line of the diagram: every subagent's findings come home as fresh tokens in the main transcript, where they join the 1-hour-tier context you will re-buy at the next cliff.

## Compaction resets the meter

The fourth row of the four-bills table is the escape hatch. When a session compacts, the harness summarizes the transcript and starts the context over: system prompt, config, summary. One of my sessions was riding a 666,134-token cache read per turn before compaction; the first request after it read 16,559 and wrote 9,575. Twenty cents to re-establish what had been a multi-dollar context.

Compaction is not free (the summarization request itself reads the whole context one last time) and it is not lossless (detail you did not write down is gone). But it changes the base your future taxes are computed on. The $5.18 good morning happened because I walked away overnight from a 500K-token context. Had I compacted before leaving, the same greeting would have cost cents.

## The shadow bill

Zooming out to everything Claude Code has stored on this machine, deduplicated to unique API calls and priced at list:

| Metric | Value |
|---|---|
| Transcript files | 923 |
| Unique API requests | 12,992 |
| Input-side tokens | 1.89 billion |
| Of which cache reads | 93.8% |
| Shadow bill as metered | ~$2,000 |
| Same tokens with caching off | ~$9,500 |

Two things are true at once. First, caching works: it is quietly eating 79% of what my agent usage would otherwise cost, and 93.8% of every input token I have ever sent was a cheap read. Agents are economically possible because of this one mechanism. Second, the residual is not evenly distributed. It concentrates in the moments caching is defeated: the overnight comeback, the mid-session config change, the 232-agent fan-out. The average turn is fine. The tail is where the money goes, and the tail is entirely made of re-establishment events.

That is also the honest answer to the Reddit thread. Nothing malfunctioned. The $20 "hey" was a working system doing exactly what the pricing page says it does, to a user who had no way to see which of their messages were $0.26 messages and which were $20 messages, because the interface shows you what you typed and hides what you re-established.

## What to do about it

Not a checklist this time, just the five behaviors the receipts actually indict:

**Make the first message after a break count.** The re-write happens whether your comeback is "hey" or a fully loaded next task. Same price. Spend it on substance.**Ask cheap questions in cheap contexts.**"current status?" into an expired 500K-token session is a $5 question. The same words in a fresh session cost cents. If you just want to know what happened, read the transcript or ask a new session.**Compact or close before walking away.** If you know you are done for hours, /compact turns tomorrow's re-establishment from dollars into cents. Inside the hour, do nothing; the free TTL refresh has you covered.**Keep the prefix boring mid-session.** CLAUDE.md edits, MCP server toggles, and model switches each invalidate everything downstream. Batch config changes at session start, when there is nothing downstream to invalidate.**Fan out on purpose, not by default.** Every subagent is a fresh prefix plus a homecoming payload. Parallelism that shortens wall-clock time on real work earns it; delegation reflex on small tasks just multiplies re-establishment.

And if you are on a subscription rather than API billing: the dollars in this post are invisible to you, but the tokens are not. Usage limits meter consumption, and a $5 cache re-write burns the same headroom whether or not an invoice prints it. The users hitting limits fastest are, very often, the ones paying the most re-establishment tax.

## Price your own transcripts

Everything above came from files already on my disk. Claude Code writes every API response's usage block into JSONL transcripts under `~/.claude/projects/`

, so you can audit yourself without a proxy, without instrumentation, without changing anything about how you work. Save this as `whered_my_tokens_go.py`

and run it:

``` bash
#!/usr/bin/env python3
# whered_my_tokens_go.py - price every request in your Claude Code transcripts.
# No dependencies. Reads ~/.claude/projects/**/*.jsonl and prints your ten most
# expensive requests, plus totals. Prices: API list, July 2026.
import json, glob, os

PRICES = {  # $/MTok: (input, 5m cache write, 1h cache write, cache read, output)
    "opus":   (5, 6.25, 10, 0.50, 25),
    "sonnet": (3, 3.75,  6, 0.30, 15),
    "haiku":  (1, 1.25,  2, 0.10,  5),
}

def price(usage, model):
    tier = next((t for t in PRICES if t in model), "opus")
    pi, pw5, pw1, pr, po = PRICES[tier]
    cc = usage.get("cache_creation") or {}
    w1 = cc.get("ephemeral_1h_input_tokens")
    w5 = cc.get("ephemeral_5m_input_tokens", 0)
    if w1 is None:  # old payloads lack the tier breakdown; assume the 1h tier
        w1 = usage.get("cache_creation_input_tokens", 0) - w5
    return (usage.get("input_tokens", 0) * pi + w1 * pw1 + w5 * pw5
            + usage.get("cache_read_input_tokens", 0) * pr
            + usage.get("output_tokens", 0) * po) / 1e6

rows, totals, seen = [], {}, set()
for path in glob.glob(os.path.expanduser("~/.claude/projects/**/*.jsonl"), recursive=True):
    last_text = ""
    with open(path) as f:
        for line in f:
            try:
                d = json.loads(line)
            except json.JSONDecodeError:
                continue
            if d.get("type") == "user":
                c = (d.get("message") or {}).get("content")
                if isinstance(c, str):
                    last_text = c
                elif isinstance(c, list):
                    t = " ".join(x.get("text", "") for x in c
                                 if isinstance(x, dict) and x.get("type") == "text")
                    last_text = t or last_text
            elif d.get("type") == "assistant":
                u = (d.get("message") or {}).get("usage")
                mid = (d.get("message") or {}).get("id")
                if not u or (mid and mid in seen):
                    continue  # streaming writes one line per content block; count each API call once
                seen.add(mid)
                m = (d.get("message") or {}).get("model", "")
                cost = price(u, m)
                rows.append((cost, u.get("cache_creation_input_tokens", 0),
                             u.get("cache_read_input_tokens", 0),
                             last_text[:48].replace("\n", " "), m))
                for k in ("input_tokens", "cache_creation_input_tokens",
                          "cache_read_input_tokens", "output_tokens"):
                    totals[k] = totals.get(k, 0) + u.get(k, 0)
                totals["cost"] = totals.get("cost", 0) + cost

rows.sort(reverse=True)
print(f"{'bill':>7}  {'cache write':>11}  {'cache read':>10}  what you typed")
for cost, w, r, text, m in rows[:10]:
    print(f"${cost:6.2f}  {w:11,}  {r:10,}  \"{text}\"")
inp = totals.get("input_tokens", 0) + totals.get("cache_creation_input_tokens", 0) \
      + totals.get("cache_read_input_tokens", 0)
print(f"\n{len(rows):,} requests, {inp/1e9:.2f}B input-side tokens, "
      f"{totals.get('cache_read_input_tokens', 0) / max(inp, 1) * 100:.1f}% cache reads, "
      f"~${totals.get('cost', 0):,.0f} at list prices")
```

Fair warning: your leaderboard will also be greetings.

The maintained version lives at [karanb192/claude-code-shadow-bill](https://github.com/karanb192/claude-code-shadow-bill), with two extra views: `--sessions`

(which sessions ate the budget) and `--cliff`

(the 60-minute cliff measured on your own history).

## Method and caveats

- All numbers come from the JSONL transcripts Claude Code keeps under
`~/.claude/projects/`

on my machine: 923 files spanning main sessions and their`subagents/`

directories. The usage blocks are stored verbatim as the API returned them. - One replication trap: streaming writes one transcript line per content block, all carrying the same usage object, so naive summing double-counts by roughly 2x. Everything here is deduplicated by message id (the script above does this too).
- Prices are Anthropic's published list rates as of July 2026 (Opus 4.8: $5 input, $6.25 and $10 cache writes, $0.50 cache reads, $25 output, per MTok). A small share of my requests ran on models without public per-token pricing; those are priced at Opus rates, which makes the aggregate figures approximate. Every individual receipt shown ran on Opus 4.8 except where labeled.
- I pay for a subscription, not per token, so these are list-price equivalents, not an invoice. The token counts themselves are exact.
- Cache TTLs, tier selection, and context assembly are harness behaviors and can change in any Claude Code release. The receipts describe June and July 2026.
- Your numbers will differ. That is the point of the script.

**The one-line takeaway:** agents re-establish context far more often than they receive instructions, so price your workflow by its re-establishment events, not by its messages. The bill was never about the "hey".
