Where Claude Code and Codex keep their usage logs, and what a token count is worth
Every coding agent that runs on a laptop already keeps a record of what it spent. It is not hidden and it is not encrypted; it is a directory of newline-delimited JSON in your home folder that the agent needs for resuming sessions. Everything a cost dashboard knows about local usage starts there, so it is worth knowing what is in those files, what is not, and how to turn a token count into a dollar figure that means something.
Claude Code: ~/.claude/projects/
Claude Code keeps one subdirectory per working directory it has been started in, named after the path with the slashes replaced, and inside it one .jsonl file per session, named by the session's UUID. Every line is one record: a user message, an assistant message, a tool result, a summary. The records that matter for cost are the assistant ones, and each of those carries the model that answered and the usage block the API returned for that call:
{
"type": "assistant",
"sessionId": "5b1c…",
"cwd": "/Users/alice/work/acme-api",
"timestamp": "2026-08-27T09:14:02.511Z",
"message": {
"model": "claude-sonnet-5",
"usage": {
"input_tokens": 412,
"cache_creation_input_tokens": 18230,
"cache_read_input_tokens": 96114,
"output_tokens": 655
},
"content": [ …the reply, and any tool calls… ]
}
}
Three things to notice. The usage is per API call, not per session, so summing the turns of a session counts the context window again on every turn. That is correct: it is what the vendor charges for. The cwd is a full path, which is how a tool can attribute tokens to a repository and also how it can leak a username and a client's name if it is careless. And the content is right there beside the numbers, which is why a collector's promise to not read it has to be a property of its code rather than its intentions.
Claude Code deletes transcripts older than a retention window (cleanupPeriodDays in its settings, 30 days by default). A usage tool that only runs when someone remembers will find that the second half of last quarter is gone. Anything that reads these files for cost has to run on a schedule, and a dashboard cannot backfill a month the agent already cleaned up.
Codex: ~/.codex/sessions/
OpenAI's Codex CLI writes a rollout file per session, filed by date: ~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl. The shape is the same idea, one JSON record per line, with the session's metadata first (including the working directory) and then the conversation. Token counts arrive as their own event type rather than on each message, and they are recorded as running totals for the session, so the naive sum of every count event overstates the session several times over. A correct reader takes the last total per session, or diffs consecutive events, and prices the difference.
Models are named in the session metadata and on the turn. Codex's cache accounting is coarser than Claude Code's, which is one reason a Codex estimate and a Claude Code estimate for the same afternoon should not be compared to the cent.
Other agents
Gemini CLI, OpenCode, Amp and the rest all do a version of the same thing, each in its own directory with its own field names. Parsing them is a moving target; the projects that do it well (Kibble's collector uses the open-source tokscale core for exactly this, behind an adapter so the parser can be swapped) treat every agent's format as something that will change with the next release, and measure their output against the raw transcripts rather than trusting the parser.
What a token is worth
A model's list price has four rates, and the one people forget is the one that dominates a coding agent's bill. Per million tokens, roughly:
| Field | What it is | Relative rate |
|---|---|---|
| input_tokens | Fresh prompt tokens the model read for the first time | 1× |
| output_tokens | What the model wrote, including any reasoning tokens | ≈5× input |
| cache_creation_input_tokens | Prompt tokens written into the prompt cache | 1.25× input |
| cache_read_input_tokens | Prompt tokens served from the cache | 0.1× input |
A coding agent re-sends the whole conversation on every turn, so after the first few turns nearly all of its input is a cache read. In the record above, cache reads are 83% of the tokens and about a quarter of the cost; the 18,230 tokens written to the cache cost more than twice as much as the 96,114 read from it. Two consequences:
- A "tokens used" number is not a cost number. Two engineers with the same token total can differ by 5× in spend depending on how much of it was cached reads versus output. Price the four fields separately, then add.
- Do not count reasoning tokens twice. Both Anthropic and OpenAI already include them in output. A parser that adds a
reasoning_tokensfield on top inflates every reasoning-heavy model.
Kibble's collector prices each field at the vendor's current list rate, keeps the result as an integer number of micro-dollars (a float would drift over a month of additions), and calls the result what it is: an estimate. What the vendor actually charged is a different number, from a different source, and the two are never added together.
What to leave on the machine
Given everything that is in those files, here is the line Kibble draws, field by field. It is the whole list; the server rejects anything not on it.
| Sent | Never sent |
|---|---|
| The day, the agent, the model | Prompts, replies, tool results |
| Input, output, cache read, cache write totals, message count | File contents, diffs |
| Opaque session ids, for de-duplication and size percentiles | Tool arguments |
The repository's name (acme-api, or owner/repo from the git remote) | The path, the username in it, the hostname, any hardware id |
The repo name is the one place a path is touched, so it gets its own function: take the checkout's directory name or the remote's owner/repo, fold worktrees into their main repo, and throw away everything above it. A monorepo package resolves to the monorepo. /Users/alice/clients/acme-bank/api leaves the machine as api, or as acme/api if that is what the remote says, and Alice's name and her client list stay where they were.
The numbers are in the same file as the words. A collector's privacy is the part of its code that reads one and not the other.
The collector that reads these files is open source and about a screen long per adapter.
Read the source