Content hash: da90e70ea21cb1e88103796cc48a6c7a547079ae01f83b7d0cbf5ff473728b6e
## Provider Cache Pricing/Behavior Comparison
> Verify current numbers against provider docs before shipping - this changes.
| Feature | OpenAI | Anthropic | Google (varies) |
|---------|--------|-----------|-----------------|
| Mechanism | Automatic | Manual (`cache_control`) | Mixed / auto flags |
| Cache hit discount | ~50% off input | ~90% off input | Check docs |
| Guaranteed hits? | Best-effort | Yes (matching prefix) | Check docs |
| Min prefix length | ~1024 tokens | ~2048 tokens (model dep.) | Check docs |
| TTL | ~5-10 min (auto) | 5 min (1h option) | Check docs |
| Write cost | None | Per breakpoint | Check docs |
### Anthropic cache_control placement rules
- Put a breakpoint AFTER each long stable block (system prompt, tool definitions)
- Do NOT put breakpoints on the tiny dynamic tail
- Maximum breakpoints per request (typically 4)
### How to monitor
```python
# Anthropic usage object
usage.cache_creation_input_tokens # tokens WRITTEN to cache (pay write)
usage.cache_read_input_tokens # tokens READ from cache (discounted)
# OpenAI usage object
usage.prompt_tokens_details.cached_tokens # cached input tokens
```
### Hit-rate diagnostics
A hit rate below ~70% on a "static" prefix usually means:
1. Hidden timestamp / request id in the shared block
2. Nondeterministic ordering in a serialized list
3. Per-user metadata injected into the shared system prompt
4. Different apps/layouts fragmenting the cache
### The one rule that matters
```
[STATIC: system + tools + examples] <- identical across requests
[DYNAMIC: user query] <- varies, always last
```
A single varying byte in the prefix invalidates every token after it.