---
title: "Explain prices and cache costs"
description: "Inspect the price source and the cause and cost of prompt cache misses."
canonical_url: "https://docs.oz0.ai/spend/cost-and-usage/prices-and-cache"
---
# Explain prices and cache costs

> Inspect the price source and the cause and cost of prompt cache misses.

For: tenant administrators and cost owners.

Inspect the price source and the cause and cost of prompt cache misses.

## How prices are worked out

The gateway prices each call at the time of the call, from the usage the provider reports in the response or the stream: at the provider record's own price for the model, at the provider's reported cost where its record says so, or with [genai-prices](https://github.com/pydantic/genai-prices); and adds $0.01 for each of Anthropic's web searches.

Calls to [local models](https://docs.oz0.ai/fleet/local-models) are reported by the node that ran them, as provider `local`, at $0 unless the tenant priced them. Calls the gateway refused for a [data rule](https://docs.oz0.ai/spend/models-and-keys/data-rules#data-rules) cost nothing and are counted as refused, in `usage` and on the Cost page. Usage rows are kept in the management database and outlive the jobs' history in Temporal.

<note>

The console's *Cost* page shows the same calls over the [period](https://docs.oz0.ai/getting-started/web-ui/navigation#periods-filters-and-pages) you pick, by agent, model, node and job, and by user and team. The *By user and team* and *Prompt cache* cards count from the period's first day to now, and say so for a range that ended earlier. Trends over time are on the Overview page's KPIs and in each agent's, tool's and node's [deep dive](https://docs.oz0.ai/improve/errors-and-insights#kpis-and-deep-dives).

Planned: reports per tenant and plugin in the console.

</note>

## Reprice recorded spend

What would last month's work have cost under another price sheet, at another model's prices, or without a prompt cache? A [reprice](https://docs.oz0.ai/govern/governance-replay#reprice-recorded-spend) prices the recorded calls' own tokens again, part by part, exact to the cent, with no model call:

```bash [Terminal]
oz0 replay reprice --prices sheet.json --since 30d --wait
oz0 replay reprice --model anthropic/claude-haiku-4-5 --since 30d --wait
oz0 replay reprice --cache none --since 30d --wait
```

Another model's prices on the same tokens are labelled *same tokens, another price: quality not measured*: the figure says what the same tokens cost there, not what that model would have done. Per agent, model and team (teams through works-council mode), effective and at the list price.

## The prompt cache

Where a provider caches prompts, the gateway records for every call how much of its input was read from the cache, what that saved, and, for a call that read less than it could have, why and what it cost. Calls before this was recorded are not in the figures.

- Hit rate: the tokens read from the cache over the cacheable input (new input, cache reads and cache writes) of calls to providers that cache.
- Saved: the cache reads at the input price, minus what they cost, minus the write premium (cache writes at their price minus the input price; writes with the longer lifetime at theirs). A call that only wrote saves less than nothing; the next one that reads makes it up.
- Missed: for a call that read less than its job's previous call could have left it, the tokens it could have read, at what they were charged minus the read price.

Each miss has one cause, checked in this order:

<table>
<thead>
  <tr>
    <th>
      Cause
    </th>
    
    <th>
      What happened
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        first_call
      </code>
    </td>
    
    <td>
      the job's first call: there was nothing to read yet (no cost)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rewarm
      </code>
    </td>
    
    <td>
      the platform's own request that warmed the cache again after a wait (<a href="https://docs.oz0.ai/spend/models-and-keys/cache-and-tools#the-prompt-cache">
        the prompt cache
      </a>
      
      ); its cost is its whole charge
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        not_asked
      </code>
    </td>
    
    <td>
      the tenant's cache is off, or the request marked nothing on a provider that caches only what is marked
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        member_changed
      </code>
    </td>
    
    <td>
      another member of a <a href="https://docs.oz0.ai/spend/models-and-keys/deployment-pools#pools-of-deployments">
        pooled provider record
      </a>
      
       served the call than the job's previous one, after a 429 or a member with more headroom: its cache was on the first member
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model_changed
      </code>
    </td>
    
    <td>
      another model than the job's previous call: a model cascade's next round, or an alias that moved
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        tools_changed
      </code>
    </td>
    
    <td>
      the tool list changed: a tool loaded on demand, or a grant withdrawn
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        instructions_changed
      </code>
    </td>
    
    <td>
      the system prompt changed
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        compacted
      </code>
    </td>
    
    <td>
      the runtime compacted the job's history since the previous call
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        history_changed
      </code>
    </td>
    
    <td>
      an earlier message changed without a compaction
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        expired
      </code>
    </td>
    
    <td>
      more time passed since the previous call than the lifetime it asked for; the figures say when it came after an approval wait
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        not_cached
      </code>
    </td>
    
    <td>
      nothing changed and the lifetime held, and the provider still read nothing: a prefix under its minimum, another workspace, or an eviction
    </td>
  </tr>
</tbody>
</table>

The gateway compares each call with the job's previous one by hashes of its tools, its system prompt and its messages, salted per tenant; it never keeps their text.

```sh
oz0 usage cache --since 7d
oz0 usage cache --causes
oz0 usage cache --by-team --json
```

```text
Prompt cache: auto; figures since 2026-10-13T18:32:05Z
AGENT       TEAM  MODEL                        CALLS  HIT RATE  READ  WRITTEN  SAVED    MISSED
waiter      -     anthropic/claude-sonnet-5-5  5      18.4%     848   1746     $0.0006  $0.0038
```

The figures are per agent and model, or per team with `--by-team`; a team smaller than [works-council mode](https://docs.oz0.ai/govern/works-council-mode) shows is folded into "other teams", and nothing is shown per person. The Cost page shows the same as its Prompt cache card, and an agent's scorecard (on its page and in `oz0 insights agent <agent>`) its hit rate and saving beside the period before. To measure what the cache does for an agent on your own cases, run a [comparison](https://docs.oz0.ai/improve/measured-savings) with `--variant-cache off`.

### Prompts with their stable parts first

A provider's cache reads a prompt from its start up to the first byte that changed. So the runtime puts what stays the
same within a job first and what changes last, in every adapter:

1. The agent's instructions, then the skills index, then plugins' start-up text (such as accepted memory
decisions), each read once when the job starts and the same bytes in every request of the job. The skills index
changes only when a grant of another plugin's skill is given or withdrawn during the job.
2. The tool definitions, in the order the agent lists them, then the platform's built-ins in a fixed order;
a tool withdrawn for a request (a routine's list, finish-only) leaves the others in their places.
3. The conversation's history, in order.
4. What changes, last: the [status block](https://docs.oz0.ai/build/parts/contracts-and-limits/budgets#what-the-agent-sees-of-its-spend) of what is
left of the budget, attached to the newest message and never kept in the history, and the newest tool results. No
date or time is put into a prompt.

A harness session gets the instructions, the skills index and the start-up text once, in that order, when it starts;
what the harness puts before them (its own system prompt) is the harness's.

The finding. An agent whose jobs still show `instructions_changed` within one job, or `tools_changed` more than once,
has something changing at the start of its prompt: a date or a counter in its instructions file, a tool description
that changes, tools that come and go.

The platform never changes an agent's instructions within a job, so one change
of them counts; a single change of tools is left out, since that is the platform's own, such as a spend wall leaving
the model only its answer.

`oz0 doctor` and the console's health page list such agents per tenant from the last 7 days
("prompt prefix"), by agent and never by person. Instructions are not templated by the platform, so the cause is in
the agent's own files or in its tools' listings.

Large tool results reach the model as a stub above the job's threshold
([large results](https://docs.oz0.ai/build/parts/tools/results-and-files#large-results)), which keeps a long page from filling every later request.
Whether that lowers what a task costs depends on the agent: it may read the result back in parts. Measure it on your
own cases with a [comparison](https://docs.oz0.ai/improve/measured-savings) and `--variant-offload off`; no saving is claimed here.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
