Response Caching
缓存相同 API 请求的响应以节省时间和成本
原文链接:https://openrouter.ai/docs/guides/features/response-caching
Response caching 目前处于 beta 阶段。API 和行为可能会发生变化。
Response caching 允许你缓存相同 API 请求的响应。当缓存的响应可用时,OpenRouter 会立即从缓存返回,不收取任何费用(所有可计费使用计数器都报告为 0),从而降低延迟和成本。
流式和非流式请求都有资格进行缓存。只有成功(200 OK)响应会被缓存。错误响应、速率限制响应和部分结果永远不会被缓存。包含 tool calls 的响应会正常缓存,因为它们是成功完成的一部分。对于流式请求,缓存的响应会通过相同的流式管道重放,因此客户端在缓存命中时会收到相同的 content chunks。 每个 chunk 中的 id 字段、created 时间戳和 X-Generation-Id 响应头反映新的缓存命中生成记录,而不是原始记录。
启用缓存
有两种方式启用 response caching:
1. 通过请求头按请求启用
添加 X-OpenRouter-Cache 头以启用单个请求的缓存:
curl -i https://openrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer {{API_KEY_REF}}" \
-H "Content-Type: application/json" \
-H "X-OpenRouter-Cache: true" \
-d '{
"model": "google/gemini-2.5-flash",
"messages": [
{
"role": "user",
"content": "What is the meaning of life?"
}
]
}'
第一个请求导致缓存 MISS。响应被存储并正常计费:
Response Headers (MISS)
HTTP/2 200
X-OpenRouter-Cache-Status: MISS
X-OpenRouter-Cache-TTL: 300
Response Body (MISS)
{
"id": "gen-abc123",
"model": "google/gemini-2.5-flash",
"choices": ["..."],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 120,
"total_tokens": 135
}
}
再次发送相同的请求会返回缓存 HIT,usage 为零且不收费。每次缓存命中都会获得自己唯一的生成 ID:
Response Headers (HIT)
HTTP/2 200
X-OpenRouter-Cache-Status: HIT
X-OpenRouter-Cache-Age: 12
X-OpenRouter-Cache-TTL: 288
X-Generation-Id: gen-def456
Response Body (HIT)
{
"id": "gen-def456",
"created": 1746000012,
"model": "google/gemini-2.5-flash",
"choices": ["..."],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0
}
}
2. 通过 Presets
你可以通过在 preset 中配置以下字段来为使用该 preset 的所有请求启用缓存:
| 字段 | 类型 | 描述 |
|---|---|---|
| cache_enabled | boolean | 为使用此 preset 的所有请求启用缓存 |
| cache_ttl_seconds | number | 缓存响应的默认 TTL(1-86400 秒,默认 300) |
当 preset 上设置了 cache_enabled 时,缓存会自动应用到引用该 preset 的每个请求。不需要 X-OpenRouter-Cache 头。
示例 preset 配置:
{
"name": "cached-tests",
"cache_enabled": true,
"cache_ttl_seconds": 600
}
工作原理
当两个请求共享相同的 API key、模型、endpoint 类型、流式模式 和请求体(包括所有参数)时,它们被视为相同。当启用缓存时,OpenRouter 从这些输入生成缓存键。如果之前进行过相同的请求且缓存的响应未过期,则立即返回缓存的响应。更改其中任何一个(包括模型、endpoint 或在流式和非流式之间切换)都会产生不同的缓存键并导致缓存未命中。
缓存作用域是你的 API key。不同的 API key,即使在同一个账户或组织下,也不共享缓存。轮换你的 API key 将导致新 key 的缓存为空。
非确定性:缓存响应会原样返回,无论 temperature 等随机参数如何。如果你需要新的响应,请使用 X-OpenRouter-Cache-Clear: true 或较短的 TTL。
缓存键详情
缓存键由你的 API key、模型、endpoint 类型、流式模式和请求体的 SHA-256 哈希值生成。流式和非流式请求是分开缓存的,因此 stream: true 请求不会返回缓存的非流式响应,反之亦然。请求体在哈希之前会被规范化,因此额外的空白不会影响缓存键。但是,JSON 体的属性顺序很重要:
- 逻辑上相同的 JSON 中的不同属性顺序(例如
{"model":"x","messages":[]}与{"messages":[],"model":"x"})会产生不同的缓存键 - 省略可选字段与显式发送默认值(例如
temperature: 1.0)会产生不同的键 - Attribution headers(例如
HTTP-Referer、X-Title)和 provider-specific headers 不是缓存键的一部分 - 多模态请求(图像、音频、视频、文件附件)有资格进行缓存。完整的请求体,包括 base64 编码的内容,都包含在哈希中
优先级
请求头和 preset 配置的交互规则如下:
- 如果 preset 明确设置
cache_enabled: false,则无论请求头如何,都禁用缓存——头不能覆盖 preset 的退出选择 X-OpenRouter-Cache: false头即使在 preset 启用缓存时也会禁用缓存X-OpenRouter-Cache: true头在 preset 未配置缓存时(即cache_enabled不存在)启用缓存——但不能覆盖明确设置cache_enabled: false的 preset(规则 1 优先)X-OpenRouter-Cache-TTL头覆盖 preset 的cache_ttl_seconds(默认:300 秒)- 如果既未设置头也未设置 preset,则缓存关闭
并发请求
如果两个相同的请求在第一个响应写入缓存之前同时到达,它们都会导致缓存 MISS 并独立计费。不存在请求合并。
支持的 Endpoints
| Endpoint | API 格式 |
|---|---|
| /api/v1/chat/completions | OpenAI Chat Completions |
| /api/v1/responses | OpenAI Responses |
| /api/v1/messages | Anthropic Messages |
| /api/v1/embeddings | OpenAI Embeddings |
缓存键包含 endpoint 类型识别符,因此具有相同 body 的不同 endpoint 请求不会冲突。
Provider 缓存:某些 provider 提供自己的 prompt caching(例如 Anthropic prompt caching、OpenAI cached context)。Provider 缓存与 OpenRouter response caching 是分开的,两者可以一起使用。OpenRouter 缓存在请求级别在调用到达 provider 之前运行,而 provider 缓存在 provider 的基础设施内运行。
请求头
| 头 | 值 | 描述 |
|---|---|---|
| X-OpenRouter-Cache | true | 为此请求启用缓存 |
| X-OpenRouter-Cache | false | 禁用缓存(覆盖 preset) |
| X-OpenRouter-Cache-TTL | <seconds> | 自定义 TTL(1-86400 秒,默认 300) |
| X-OpenRouter-Cache-Clear | true | 强制刷新此请求的缓存 |
无法解析为整数的 TTL 值(即不以数字开头)会被忽略,并回退到 preset 或默认 TTL。以数字开头的值会被接受,即使包含尾随非数字字符(例如 60abc 被视为 60);小数会被截断(例如 1.5 被视为 1)。超出有效范围的数值会被限制在 [1, 86400] 范围内。
响应头
| 头 | 值 | 描述 |
|---|---|---|
| X-OpenRouter-Cache-Status | HIT 或 MISS | 响应是否从缓存提供 |
| X-OpenRouter-Cache-Age | <seconds> | 响应已被缓存多长时间(仅 HIT) |
| X-OpenRouter-Cache-TTL | <seconds> | HIT 时剩余 TTL;MISS 时完整 TTL |
X-Generation-Id 头也存在于每个响应上(缓存或非缓存),不是缓存特有的。在缓存命中时,生成 ID 对于该命中是唯一的——不会从原始响应重用。
TTL(Time-to-Live)
TTL 控制缓存响应保持有效的时间。
- 默认:300 秒(5 分钟)
- 范围:1 秒到 86400 秒(24 小时)
你可以使用 X-OpenRouter-Cache-TTL 头自定义每个请求的 TTL,或在你的 preset 配置中设置默认 TTL。
清除缓存
要为特定请求强制刷新响应,请随 X-OpenRouter-Cache: true(或使用启用了 cache_enabled: true 的 preset)一起发送 X-OpenRouter-Cache-Clear: true 头。这会删除该缓存键的现有缓存条目,向 provider 发出新请求,并存储新响应。除非为请求启用了缓存,否则 X-OpenRouter-Cache-Clear 无效。这不会清除所有缓存条目——只会清除与当前请求匹配的条目。
新缓存条目使用当前请求的 X-OpenRouter-Cache-TTL 头、preset cache_ttl_seconds 或默认值(300 秒)的 TTL,遵循标准优先级规则。
计费
缓存命中是免费的。不消耗任何 token,所有可计费使用计数器都报告为 0。对于 chat completions 和 Responses endpoints,usage.prompt_tokens、usage.completion_tokens 和 usage.total_tokens 会被清零。对于 Embeddings endpoint,usage.prompt_tokens 和 usage.total_tokens 会被清零(embeddings 响应中不存在 completion_tokens)。对于 Anthropic Messages endpoint,usage.input_tokens 和 usage.output_tokens 会被清零。你只为填充缓存的原始请求(缓存 MISS)付费。
缓存命中不计入 provider 速率限制,因为请求永远不会到达 provider。
限制
- 账户级别 Zero Data Retention (ZDR) 已禁用:当强制执行账户级别 ZDR 时,Response caching 不可用,因为缓存需要临时存储响应数据。按请求的
provider.zdr不影响缓存资格。 - 并发相同请求:如果两个相同的请求在第一个响应被缓存之前到达,都会导致 MISS。请参阅并发请求。
- 缓存驱逐:在内存压力下,缓存响应可能在 TTL 到期之前被驱逐。你缓存的条目数量没有限制,但压力下的驱逐意味着条目不能保证存活完整的 TTL。
数据保留
缓存响应存储在边缘基础设施中,仅在 TTL 期间保留,并在到期时自动驱逐。缓存数据只能通过触发缓存的 API key 访问——其他 key、账户或组织都无法检索。缓存数据不用于训练或与第三方共享。
使用场景
Agent 工作流
当 agent 工作流中途失败时,你可以从失败点恢复,而无需重新运行和重新支付相同早期请求的费用。在工作流开始时启用缓存,重试时所有先前的步骤都会从缓存立即返回。
单元测试
为你的测试套件获得可重复的响应。初始运行填充缓存后,后续相同请求每次都以零成本返回相同的缓存响应。为了获得确定性的首次运行结果,请使用 temperature: 0 或固定种子。
重复的相同请求
如果你的应用程序多次发出相同的请求(相同的模型、相同的消息、相同的参数),缓存确保只有第一次调用命中 provider。后续相同调用立即从缓存以零成本返回。
监控缓存有效性
缓存命中和未命中状态在你的 Activity 日志中可见。每个缓存请求都显示为带有缓存指示器的单独条目,你可以过滤日志以仅显示缓存或非缓存请求。每次缓存命中都会获得自己唯一的生成 ID,因此你可以独立跟踪各个缓存响应。