Claude context caching (Context Cache) is designed for reusing long prefixes such as system prompts, documents, codebases, or conversation history. Add cache_control to a stable prefix so that the first request creates a cache and subsequent requests can read the unexpired cache.
Before you begin, set your API key:
export API_KEY="YOUR_API_KEY"The examples in this guide use claude-sonnet-5. Refer to the platform's model documentation to confirm whether other models support context caching.
Use cases
When multiple requests repeatedly include the same large body of content, you can cache a stable prefix, such as:
- A long system prompt
- A fixed knowledge base or product documentation
- Conversation history that remains unchanged across a multi-turn conversation
- Reused codebases, tool definitions, and instructions
Context caching is ideal for requests where the content at the beginning remains unchanged while the final question keeps changing.
Claude Messages API
5-minute cache
Add cache_control to the content block you want to cache:
curl "https://api.openveer.com/v1/messages" \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "Place the long prefix you want to reuse here...",
"cache_control": {
"type": "ephemeral"
}
}
],
"messages": [
{
"role": "user",
"content": "Answer the question based on the content above."
}
]
}'system must be an array of content blocks. A string-form system cannot include cache_control.
If ttl is omitted, the cache lifetime defaults to 5 minutes.
1-hour cache
To use a 1-hour cache, add the anthropic-beta request header and set ttl to 1h:
curl "https://api.openveer.com/v1/messages" \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: extended-cache-ttl-2025-04-11" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "Place the long prefix you want to reuse here...",
"cache_control": {
"type": "ephemeral",
"ttl": "1h"
}
}
],
"messages": [
{
"role": "user",
"content": "Answer the question based on the content above."
}
]
}'Supported TTL values:
| TTL | Meaning |
|---|---|
5m |
Cache for 5 minutes; this value is used when ttl is omitted |
1h |
Cache for 1 hour; the corresponding anthropic-beta request header is also required |
Response usage fields
The Claude Messages API reports regular input, cache-write, and cache-read tokens separately in usage:
{
"usage": {
"input_tokens": 23,
"cache_creation_input_tokens": 2619,
"cache_read_input_tokens": 0,
"cache_creation": {
"ephemeral_5m_input_tokens": 2619,
"ephemeral_1h_input_tokens": 0
},
"output_tokens": 24
}
}Calculate total input tokens as follows:
input_tokens
+ cache_creation_input_tokens
+ cache_read_input_tokensThese three values do not overlap. The first request usually has cache_creation_input_tokens > 0. When you send the same stable prefix again, you should see cache_read_input_tokens > 0.
OpenAI-compatible API
Request example
When using caching through /v1/chat/completions, the cache_control structure is similar to the Claude Messages API:
curl "https://api.openveer.com/v1/chat/completions" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 1024,
"messages": [
{
"role": "system",
"content": [
{
"type": "text",
"text": "Place the long prefix you want to reuse here...",
"cache_control": {
"type": "ephemeral"
}
}
]
},
{
"role": "user",
"content": "Answer the question based on the content above."
}
]
}'1-hour cache
The OpenAI-compatible format also supports 1-hour caching. Add the anthropic-beta request header and set ttl: "1h" in cache_control:
-H "anthropic-beta: extended-cache-ttl-2025-04-11""cache_control": {
"type": "ephemeral",
"ttl": "1h"
}content must be an array
In the OpenAI-compatible format, cache_control must be placed in a specific content block. It cannot be attached to a string-form message.
{
"role": "system",
"content": "Place the long prefix here...",
"cache_control": {
"type": "ephemeral"
}
}The structure above does not enable caching, and the request does not return an error. Use the following structure instead:
{
"role": "system",
"content": [
{
"type": "text",
"text": "Place the long prefix here...",
"cache_control": {
"type": "ephemeral"
}
}
]
}If content is a string, the cache marker is ignored and the input is processed as regular input. Check the cache usage fields in the response to confirm whether the cache was hit.
Cache user or assistant content blocks
You can also add cache_control to a content block in a user or assistant message to cache a long document or a multi-turn conversation prefix:
{
"role": "user",
"content": [
{
"type": "text",
"text": "Place the long document you want to reuse here...",
"cache_control": {
"type": "ephemeral"
}
},
{
"type": "text",
"text": "Summarize the three main points in the document above."
}
]
}Split the stable content and the current question into separate content blocks, and add cache_control only to the stable content block.
Response usage fields
The OpenAI-compatible format uses different fields to report cache usage:
{
"usage": {
"prompt_tokens": 1942,
"completion_tokens": 22,
"prompt_tokens_details": {
"cached_tokens": 1921,
"cache_write_tokens": 0
},
"claude_cache_creation_5_m_tokens": 0,
"claude_cache_creation_1_h_tokens": 0
}
}Field mapping:
| Meaning | Claude Messages API | OpenAI-compatible API |
|---|---|---|
| Total input | Sum of the three input fields | prompt_tokens |
| Cache read | cache_read_input_tokens |
prompt_tokens_details.cached_tokens |
| General cache-write field | cache_creation_input_tokens |
prompt_tokens_details.cache_write_tokens (only populated when no TTL breakdown is available) |
| 5-minute cache write | cache_creation.ephemeral_5m_input_tokens |
claude_cache_creation_5_m_tokens |
| 1-hour cache write | cache_creation.ephemeral_1h_input_tokens |
claude_cache_creation_1_h_tokens |
| Output | output_tokens |
completion_tokens |
If prompt_tokens_details.cache_write_tokens is 0, also check claude_cache_creation_5_m_tokens and claude_cache_creation_1_h_tokens. When TTL-specific fields are available, the cache-write amount is returned in the corresponding field.
The numbers and units in claude_cache_creation_5_m_tokens and claude_cache_creation_1_h_tokens are separated by underscores. Use the field names exactly as returned in the response.
The OpenAI-compatible endpoint may return an SSE streaming response even when you do not explicitly pass stream: true. Clients should support parsing chat.completion.chunk; usage is included in the final data chunk that contains usage.
Conditions for a cache hit
The prefix meets the minimum length
For the model used in these examples, the cache prefix usually needs to be at least approximately 1024 tokens. If the prefix is too short, the cache marker may be ignored without an error.
The prefix remains byte-identical
The text, spaces, line breaks, and content-block order in the cache prefix must remain identical. Do not add timestamps, random IDs, request counters, or other dynamic content to the stable prefix.
The request does not trigger a model refusal
If the request triggers a model refusal, the response may still report cache-creation tokens, but that cache is not read on the next request. When troubleshooting a cache miss, also check whether stop_reason is refusal.
The cache is still valid
The cache lifetime is 5 minutes or 1 hour and is calculated from the most recent access. A cache hit refreshes the lifetime.
Billing usage
Cache-related usage falls into three categories:
| Usage | When it occurs |
|---|---|
| Cache write | When the cache is first created |
| Cache read | When a subsequent request hits the cache |
| Regular input | Input outside the cached prefix |
The three categories do not overlap. Cache writes usually cost more than regular input, while cache reads usually cost less. Context caching is therefore best suited to stable prefixes that will be reused within the TTL.
Minimal reproducible example
The script below generates a sufficiently long stable prefix and sends the same request twice in succession. The second response should have cache_read_input_tokens > 0.
python3 - <<'PY' > /tmp/claude-cache-request.json
import json
paragraph = (
"Prompt caching stores a prefix of the request so that later requests "
"can reuse the same byte-identical prefix without processing it again. "
)
system_text = (
"You are a documentation assistant. Reference material follows.\n\n"
+ paragraph * 40
)
print(json.dumps({
"model": "claude-sonnet-5",
"max_tokens": 32,
"system": [{
"type": "text",
"text": system_text,
"cache_control": {"type": "ephemeral"}
}],
"messages": [{
"role": "user",
"content": "In one sentence, what must remain unchanged?"
}]
}))
PY
for request_number in 1 2; do
echo "Request ${request_number}"
curl -s "https://api.openveer.com/v1/messages" \
-H "x-api-key: $API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
--data @/tmp/claude-cache-request.json \
| python3 -c "import json, sys; print(json.load(sys.stdin)['usage'])"
doneExpected result:
Request 1: cache_creation_input_tokens > 0, cache_read_input_tokens = 0
Request 2: cache_creation_input_tokens = 0, cache_read_input_tokens > 0Troubleshooting checklist
If the cache is not hit, check the following in order:
- Whether
stop_reasonisrefusal - Whether the cache prefix meets the model's minimum token requirement
- Whether the stable prefix is byte-identical across both requests
- Whether
contentis an array in the OpenAI-compatible format - Whether
cache_controlis placed in a specific content block - Whether a 1-hour cache includes both
ttl: "1h"and the correspondinganthropic-betarequest header - Whether the cache has exceeded its TTL
- Whether you are reading the cache usage fields for the endpoint you are using
This documentation is built and hosted on Mintlify, a developer documentation platform.