> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Claude Context Caching Guide

> Cache reusable prompt prefixes through the Claude Messages API or the OpenAI-compatible Chat Completions API to reduce token costs from repeatedly processing long content.

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:

```bash theme={null}
export API_KEY="YOUR_API_KEY"
```

<Note>The examples in this guide use `claude-sonnet-5`. Refer to the platform's model documentation to confirm whether other models support context caching.</Note>

## 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:

```bash theme={null}
curl "https://api.apimart.ai/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."
      }
    ]
  }'
```

<Warning>`system` must be an array of content blocks. A string-form `system` cannot include `cache_control`.</Warning>

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`:

```bash theme={null}
curl "https://api.apimart.ai/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`:

```json theme={null}
{
  "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:

```text theme={null}
input_tokens
+ cache_creation_input_tokens
+ cache_read_input_tokens
```

These 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:

```bash theme={null}
curl "https://api.apimart.ai/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`:

```bash theme={null}
-H "anthropic-beta: extended-cache-ttl-2025-04-11"
```

```json theme={null}
"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.

```json theme={null}
{
  "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:

```json theme={null}
{
  "role": "system",
  "content": [
    {
      "type": "text",
      "text": "Place the long prefix here...",
      "cache_control": {
        "type": "ephemeral"
      }
    }
  ]
}
```

<Warning>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.</Warning>

### 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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "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`                                                                            |

<Note>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.</Note>

<Note>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.</Note>

<Warning>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`.</Warning>

## 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`.

```bash theme={null}
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.apimart.ai/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'])"
done
```

Expected result:

```text theme={null}
Request 1: cache_creation_input_tokens > 0, cache_read_input_tokens = 0
Request 2: cache_creation_input_tokens = 0, cache_read_input_tokens > 0
```

## Troubleshooting checklist

If the cache is not hit, check the following in order:

* Whether `stop_reason` is `refusal`
* Whether the cache prefix meets the model's minimum token requirement
* Whether the stable prefix is byte-identical across both requests
* Whether `content` is an array in the OpenAI-compatible format
* Whether `cache_control` is placed in a specific content block
* Whether a 1-hour cache includes both `ttl: "1h"` and the corresponding `anthropic-beta` request header
* Whether the cache has exceeded its TTL
* Whether you are reading the cache usage fields for the endpoint you are using
