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

# Prompt Caching

> Automatic prefix caching on every open source model. Cached input at $0.22/1M on GLM-5.2, with per-request TTL control.

Prefix caching is on for every open source model. No configuration, no cache-write surcharge. When a request shares a prefix with earlier traffic (system prompt, tool definitions, conversation history), those tokens skip prefill and bill at the cached rate.

| Model                              | ID                 | Input per 1M | Cached input per 1M | Output per 1M |
| ---------------------------------- | ------------------ | ------------ | ------------------- | ------------- |
| **GLM-5.2 744B**                   | `morph-glm52-744b` | \$1.10       | **\$0.22**          | \$4.10        |
| **Kimi K3** <sup>coming soon</sup> | —                  | —            | —                   | —             |

Cached input on GLM-5.2 is 80% off. Other open source models cache automatically too; their cached tokens currently bill at the regular input rate.

## Reading cache hits

Every response reports how much of the prompt was served from cache:

```json theme={null}
{
  "usage": {
    "prompt_tokens": 18211,
    "completion_tokens": 512,
    "total_tokens": 18723,
    "prompt_tokens_details": { "cached_tokens": 17408 }
  }
}
```

`cached_tokens` billed at the cached rate, the remainder of `prompt_tokens` at the input rate.

## Getting hits

<Frame>
  <img src="https://mintcdn.com/morph-555d6c14/bcAHnPuhOK-ICSqu/images/prompt-caching-prefix.webp?fit=max&auto=format&n=bcAHnPuhOK-ICSqu&q=85&s=eff2a7be8ce831010a748e011cf07095" alt="Prompt caching: a request matching the stable prefix is a cache hit and reuses those tokens; changing any token in the prefix is a cache miss" width="1672" height="941" data-path="images/prompt-caching-prefix.webp" />
</Frame>

Matching is exact-prefix and block-aligned. To maximize hit rate:

* Put stable content first: system prompt, then tool definitions, then history. Variable content (the user's latest message, retrieved context) goes last.
* Keep the prefix byte-identical between turns. A timestamp or request ID in the system prompt kills every hit after it.
* Short prompts rarely hit. Caching operates on \~1k-token blocks, so a 300-token prompt has nothing to reuse.

Multi-turn agent loops get this for free: each turn re-sends the previous turns verbatim, so everything but the newest turn is a cache hit.

## Cache TTL

By default cached prefixes persist under LRU eviction, with no fixed expiry. To control retention per request, pass `cache_ttl`:

| Value   | Retention  |
| ------- | ---------- |
| `"5m"`  | 5 minutes  |
| `"30m"` | 30 minutes |
| `"1h"`  | 1 hour     |
| `"6h"`  | 6 hours    |
| `"24h"` | 24 hours   |

Expiry is sliding, Anthropic-style: every cache hit refreshes the clock. Past the TTL the prefix stops hitting entirely (full recompute), and re-sending it caches it fresh. A prefix shared by multiple requests keeps the longest surviving TTL.

<Note>
  `cache_ttl` is rolling out now, GLM-5.2 first and Kimi K3 at launch. Requests that include it are accepted today; the field takes effect as each model's rollout completes.
</Note>

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.morphllm.com/v1/chat/completions" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "morph-glm52-744b",
        "messages": [{"role": "user", "content": "..."}],
        "cache_ttl": "1h"
      }'
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from openai import OpenAI

    client = OpenAI(
        api_key="YOUR_API_KEY",
        base_url="https://api.morphllm.com/v1",
    )

    # Non-standard fields go via extra_body
    response = client.chat.completions.create(
        model="morph-glm52-744b",
        messages=[{"role": "user", "content": "..."}],
        extra_body={"cache_ttl": "1h"},
    )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import OpenAI from "openai";

    const client = new OpenAI({
      apiKey: "YOUR_API_KEY",
      baseURL: "https://api.morphllm.com/v1",
    });

    const response = await client.chat.completions.create({
      model: "morph-glm52-744b",
      messages: [{ role: "user", content: "..." }],
      // @ts-expect-error non-standard field
      cache_ttl: "1h",
    });
    ```
  </Tab>
</Tabs>

Fine print:

* Invalid `cache_ttl` values are rejected with a 400. Only the five tiers above are accepted.
* Expiry granularity is \~30 seconds: treat a TTL as "at least this long, expired within \~30s after."
* Omitting `cache_ttl` keeps the default behavior (LRU, no fixed expiry).

## Pitfalls

<AccordionGroup>
  <Accordion title="cached_tokens is 0 on every request">
    Your prefix is changing between requests. Diff two consecutive prompts byte-for-byte; the first divergent token ends the cacheable prefix. Common culprits: timestamps, UUIDs, or shuffled tool order in the system prompt.
  </Accordion>

  <Accordion title="Hits stop after an edit mid-conversation">
    Editing an earlier message invalidates everything after it. Expected: caching is prefix-based, so append, don't rewrite.
  </Accordion>
</AccordionGroup>

## See Also

* [Standby Requests](/sdk/components/standby) — stack a 50% tier discount on top: cached standby input is \$0.11/1M
* [Open Source Models](/sdk/components/fast-models) — the models this page prices
* [Compact](/sdk/components/compact) — shrink context before caching it
