Question about GPT-5.6 API cache read/write token billing

Question about GPT-5.6 API cache read/write token billing

Hi,

I’m using the new GPT-5.6 API and I’m confused about how cache read/write tokens are reported and billed.

For one of my requests, the usage object looked like this:

{
  "prompt_tokens": 4583,
  "completion_tokens": 15,
  "total_tokens": 4598,
  "prompt_tokens_details": {
    "cached_tokens": 3945,
    "cache_write_tokens": 4580
  }
}

My current understanding is:

  • prompt_tokens: total input tokens for this request
  • completion_tokens: output tokens generated by the model
  • cached_tokens: number of input tokens read from cache
  • cache_write_tokens: number of tokens written to cache in this request

However, what confuses me is:

cached_tokens + cache_write_tokens = 3945 + 4580 = 8525

This is much larger than:

prompt_tokens = 4583

Intuitively, I would expect cached_tokens and cache_write_tokens to be subsets of the current request’s input tokens, so their sum should not exceed prompt_tokens. But in this API response, their sum is far greater than prompt_tokens.

So I’m not sure whether:

  1. This is expected behavior by design;
  2. cached_tokens and cache_write_tokens are not mutually exclusive and can overlap;
  3. The billing formula is not simply based on adding these two fields together;
  4. Or this is possibly a bug in the usage reporting.

I previously asked Codex / ChatGPT about this, and it suggested that the billing might be calculated like this:

prompt_tokens = total input tokens in this request
cached_tokens = tokens read from cache
cache_write_tokens = tokens written to cache in this request
completion_tokens = output tokens

Then the cost might be:

Cache read cost:
3945 × cached_input_price

Cache write cost:
4580 × cache_write_price

Regular uncached input cost:
3 × input_price

Output cost:
15 × output_price

If GPT-5.6 cache write pricing is 1.25× the normal input price, the formula would be:

3945 × cached_input_price
+ 4580 × input_price × 1.25
+ 3 × input_price
+ 15 × output_price

where:

3 = prompt_tokens - cache_write_tokens = 4583 - 4580

However, if this formula is correct, it seems that a large portion of the input tokens are being charged both as cached input tokens and as cache write tokens. This looks like the same tokens might be counted for both cache read and cache write billing, which feels like duplicate billing.

So I’d like to ask:

  1. Can cached_tokens and cache_write_tokens overlap?
  2. Is it normal for cached_tokens + cache_write_tokens to be greater than prompt_tokens?
  3. How exactly is cache read and cache write billing calculated for GPT-5.6?
  4. Is there any official documentation explaining the billing formula for these fields?
  5. If the formula above is not correct, what is the correct pricing formula?

What I’m mainly trying to understand is: for the usage example above, how should the actual cost be calculated?

If this is expected behavior, why does the billing appear to charge for both cache reads and cache writes on what looks like the same set of tokens?

If it is not expected behavior, could this be a bug in the usage reporting?

Thanks in advance for any clarification.

I already came to that conclusion as you see in the gpt-5.6 announcement topic with my caution. Also, what I’ve already written:

If you read the specs the right way, past obfuscation, the model essentially has a hidden “cache writes” permanent cost, 1.25x greater than advertised. Then, upon that real price (1.25x the “input tokens” that you cannot obtain unless input is too small to cache) could have been advertised a few percent more cache discount - when you can get one.

Not sending a prompt cache key is not an avoidance technique of increased billing. You send a >1k input, you get hit with the bill. You can try to manually fudge with “explicit” cache, but there, you also do new coding that guesses and needs to know the future, the future of what will happen in the next 30 minutes on a non-shareable not arbitrarily-incomplete context.

Read docs: the 5.6 cache retention is 30 min, without 24hr. The same kind of cache breaking without any partial matches seen on prior inputs as 5.5.


Besides “too small to cache” API calls, the only exception for input tokens with increased cost seems to be a variable bit of the end of billed input context that is not sent for prompt cache. What was there? Is there an arbitray hard-to-discover granularity to what can be stored…or an OpenAI post-prompt injection counter to your user’s goal?

If you want to diagnose the actual behavior, you’d have to create a new project, unenroll it from promotional free tokens, make a single call that “usage” shows that you got a bill almost entirely at $6.25/mil instead of $5.00 by all “writes”, and then pull down the truth of that with the costs API endpoint that only gives daily buckets and might not have the decimal places needed for differentiation.

Thanks for the reply, but I think we may be talking about slightly different issues.

My question is not simply about cache writes being charged, or whether usage equals the final bill.

The confusing part in my example is that both cache read and cache write happened in the same request, and their sum is greater than the prompt tokens:

"prompt_tokens": 4583,
"prompt_tokens_details": {
  "cached_tokens": 3945,
  "cache_write_tokens": 4580
}

So:

cached_tokens + cache_write_tokens
= 3945 + 4580
= 8525

which is much larger than:

prompt_tokens = 4583

That is the specific issue I’m asking about.

If this were only a cache write case, I could understand that most of the input may be charged as cache write. But here the same request reports both a large cache read and a large cache write.

So my question is:

  1. Can cached_tokens and cache_write_tokens overlap?
  2. If they overlap, are the overlapping tokens billed both as cache read and cache write?
  3. For this specific usage object, how should the cost be calculated?
  4. Is cached_tokens + cache_write_tokens > prompt_tokens expected behavior or a reporting bug?

I’m not asking only whether usage is the final bill. I’m asking how these two fields should be interpreted when both are present and their sum exceeds the prompt token count.

Please take another look at the original example in the post. That is the part I’m trying to clarify.

It might be expected to receive a cache discount, and then a new content “write”. But if in whole (and not “building” more k-v store), that would result in a constant monotonic overbilling vs no cache discount at all.

The peculiar thing you show is a prior input match of 3945, and then a whole new cache write of nearly the entire input.

Here’s what I think you reveal:

  • double-billing of some sort, that becomes hard to explain.

This is a cold usage:

  "usage": {
    "input_tokens": 1502,
    "input_tokens_details": {
      "cache_write_tokens": 1499,
      "cached_tokens": 0
    },
    "output_tokens": 30607,
    "output_tokens_details": {
      "reasoning_tokens": 21515
    },
    "total_tokens": 32109
  },

1502 input tokens should always be what was actually sent. It should be replicable on the token-count endpoint. It already is “cache write tokens” = x1.25.

cached_tokens are the percentage of that input which fall into a “cached” bin. Completely unexpected would be a “details” item that is an additional fee instead of a description of a part of input tokens. (a cue can be taken from audio tokens: included, and the portion billed at 10x)

Unimaginable: Developers being double billed an unavoidable 1x + 1.25x, or $5+$6.25 for a model advertised at $5. Such is spelled out nowhere - and if it were actually true, I would consider that a serious breach of every implicit promise made and more categorical breaches.

I haven’t made a bunch of closely monitored calls myself . The aggravating factor in making sense of stuff is that on the Responses API, you have internal tools that you can turn on and use. Those are multi-turn automatic iterations, calling the model again on your behalf with the result of an internal tool call being added to a cumulative delivered usage tally of a final output event having “usage”.

Even given tools, I also cannot make sense of, or construct a scenario where:
- 3945 was cache discounted
- 4580 was billed at 1.25x

  • of a total 4583.

Were it the case that you get a cache discount, but then in parallel, the new longer kv input has to be separately and completely written to make a new cache record - that is significantly worse billing than no cache discount at all.

Based on your usage details - this demands OpenAI explanation.

Thanks, yes, that is exactly the part I was trying to point out.

If this were only a cold request with cache_write_tokens and cached_tokens = 0, the billing model would be easier to understand.

But in my case, the same request reports:

  • prompt_tokens: 4583
  • cached_tokens: 3945
  • cache_write_tokens: 4580

So almost the entire prompt appears to be written to cache, while a large part of the same prompt was also read from cache.

That is why I’m confused. If both fields are billed independently, this looks like a large overlap and possibly double billing. If they are not billed independently, then the usage fields need a clearer explanation.

I agree that this requires an official explanation from OpenAI.

The key question is still:

Can the same input tokens be counted and billed both as cached_tokens and cache_write_tokens in the same request?

If yes, then the billing formula needs to be clearly documented. If not, then this looks like either a reporting bug or a bug in cache accounting.

I have an anybody-facing web app that reuses code for making a tabular output format, separating out the types of tokens, and would additionally present and parse audio tokens if present.

That same call.

| ------------------ | -------------------- |
| input tokens: 1502 | output tokens: 30607 |
| ------------------ | -------------------- |
| uncached: 1502     | non-reasoning: 9092  |
| cached: 0          | reasoning: 21515     |

I can’t even make sense of where to go when details were to exceed the input being subtracted from by nearly double.

Hi, thanks for the report. This turned out to be a bug with certain types of requests not getting accounted correctly. We have fixed it now and will be looking through the requests made since launch to calculate refunds!