OpenRouter 的 API 变更怎么跟:versioning 与 changelog 怎么读
接了 OpenRouter 的团队迟早会碰到同一类事:某天客户端突然反序列化失败,或者监控里冒出一个没见过的枚举值;再或者反过来——你去翻 changelog,看到一串打着 Breaking 标签的条目,心里一紧,却不知道到底要不要动代码。
这两个方向的困惑其实是同一个问题:你不知道这个平台的版本边界画在哪里。官方文档《API Versioning》(openrouter.ai/docs/api_reference/versioning)和《API Changelog》(openrouter.ai/docs/changelog)两页把这条边界写得很清楚,只是很多人从来没连起来读。下面按排查的顺序走一遍。
一、现象
典型的三种表述,本质是一件事:
- 上周还好好的调用,这周开始在解析响应时报错——某个字段变成了
null,或者某个枚举值程序不认识; - SDK 升级之后,原来的调用方式提示已废弃,但不确定什么时候会真的失效;
- changelog 里 Breaking 标签一堆,逐条读完还是不知道跟自己有没有关系。
二、怎么确认是这个问题
第一个判定动作:先确认你没有版本可以回退。 这一步反直觉,但省时间。文档《API Versioning》写明,OpenRouter API 只有一个稳定版本 v1,由 URL 路径选择:
https://openrouter.ai/api/v1
同一页紧跟着一句:没有版本 header,也没有基于日期的版本固定(date-based version pinning),API Reference 上记录的所有端点都属于 v1。所以如果你的第一反应是「找个参数把 API 版本钉在上个月」,可以停了——文档里不存在这个开关。文档同时写明,API 是持续演进的,不按编号发版,每次改动都会反映到 OpenAPI 规范里,每个改动了规范的 release 都会在 API Changelog 产生一条记录。
第二个判定动作:按 Breaking 标签过滤 changelog,只看你上次上线之后的条目。 《API Versioning》的「Staying up to date」小节给了三条途径:盯 changelog(它由每次 release 的 OpenAPI 规范 diff 自动生成)、订阅 RSS、用 Breaking 标签过滤出只影响兼容性的条目。RSS 地址文档里写的是:
https://openrouter.ai/docs/changelog/rss.xml
如果你想把这条塞进日常巡检,Linux/macOS 上一句 curl -s https://openrouter.ai/docs/changelog/rss.xml,Windows 侧在 PowerShell 里用 Invoke-WebRequest -Uri https://openrouter.ai/docs/changelog/rss.xml,在 Git Bash 或装了 curl 的 cmd 里则和 Linux 写法一致。这一小段是通用运维做法,不是官方文档中的内容——官方文档只给了 RSS 地址,没有规定你怎么拉它。
第三个判定动作:把你的症状归到官方定义的那两类里去。 这是这套机制真正的分界线。《API Versioning》把改动明确劈成两半:
| 官方定义 | 通告方式 | |
|---|---|---|
| 非破坏性 | 新端点、新的可选请求参数、响应里的新字段、新的响应状态码、新 schema 以及既有 schema 上新增的可选属性或联合类型变体 | 不预先通知,直接上线 |
| 破坏性 | 删除或重命名端点、参数、响应字段;改字段类型;把一个原本总是存在的响应字段放宽为允许 null;把可选参数改成必填 | 发布前经人工复核,在 changelog 打 Breaking 标签并附迁移说明 |
非破坏那栏是五类,破坏那栏是四类,都可以回原页数。看懂这张表,你就能自己判断症状归谁管:新冒出来的枚举值、多出来的响应字段,按官方定义属于非破坏,不会有预告;而字段变 null,官方把它算作破坏性。
三、文档语义给出的处置
处置一:按文档要求把客户端写成防御式的。 《API Versioning》在非破坏那一节后面直接给了要求:忽略你不认识的响应字段,不要在未知枚举值上失败。这句话的分量在于它是平台的契约口径——既然新增字段和新增枚举值被归进「不预告」的那一类,客户端不做这两件事就等于把自己暴露在每一次 release 面前。
处置二:null 要当成一个独立状态处理,不要拿默认值兜底。 文档单开了「Nullable response fields」一节:字段存在但为 null,含义是这个值确实缺席;要把 null 读作 not set 并显式处理,不要替换成某个默认值,也不要假定 null 隐含任何 fallback。
这一节给的例子很具体,值得原样记住:workspace_id 在两种情况下是 null——一个没有绑定到任何工作区的 BYOK 凭据(它作用于整个账户),以及一个在工作区作用域这个能力出现之前就创建的 guardrail。文档写明这两种 null 都是区别于「绑到默认工作区」的另一种状态,不能把 null 读成默认值,也不能假定一个 null 的 guardrail 对所有工作区生效。
这一节末尾还自己给出了归类的理由(文档自述,不是我们的推断):因为一个假定字段总是存在的客户端会因此挂掉,所以「把响应字段放宽为允许 null」被列进破坏性变更,并要求带迁移说明在 changelog 公告。这是全篇唯一一处文档主动解释「为什么这么定」的地方,别的分类它只给结论、不给理由,我们也不替它补。
处置三:废弃不等于立刻消失,但也别赖着不改。 文档《API Versioning》的「Deprecations」一节写明:废弃会在 changelog 公告,同时在 API Reference 上把受影响的端点或字段标记为 deprecated;而移除一个已废弃的功能本身是破坏性变更,要走上面那套人工复核流程。
changelog 里有现成的样子。2026-07-25 那条把 POST /responses 的 beta.responses 标签移除,条目自己写明 Responses API 已转为 GA,beta.responses 只是 OpenAPI 的分组标签和 SDK 命名空间、从来不是 URL,因此裸 HTTP 调用方无需改动;SDK 用户则是「在 sunset 之前可选」,旧命名空间作为已废弃的别名继续可用,移除会带 sunset 日期提前公告。2026-07-28 那条里 FusionCallAnalysisInProgressEvent 新增了必填属性 analyst_model,条目注明它替代 judge_model,而 judge_model 保留为已废弃的别名、始终携带相同的值。
这两条合起来是同一种做法:先并存、后公告、再移除。
四、处置后怎么验证
验证不能是「跑一遍没报错」——没报错只说明这次调用的响应里恰好没出现那个字段。可核的动作有三个:
- 拿条目里的名字去自己代码里搜。 changelog 条目是按端点与 schema 名组织的(Breaking changes、Modified endpoints、New schemas、Modified schemas、New response codes 这些小节),schema 名下面还列了「哪些端点在用它」。所以核对路径是固定的:从条目里抄出 schema 名或端点路径,回到自己仓库里搜一遍,搜不到就与你无关。
- 回 API Reference 看标记。 changelog 由 OpenAPI 规范 diff 自动生成,机器措辞有时候会含糊。2026-07-28 那条
AutoRouterPlugin与AutoBetaRouterPlugin写的是「新属性cost_tier」外加一行deprecated property_added——单看这行你无法确定被废弃的是哪个属性。文档写明废弃会在 API Reference 上标出来,那就以 API Reference 的标记为准,别对着 diff 猜。 - 确认防御式解析真的生效。 对照上面那张表逐项自查:未知字段是否被忽略、未知枚举值是否走了兜底分支、可能为
null的字段是否有显式分支(而不是?? 默认值)。
顺带一个值得记住的坑:机读 diff 的措辞会比实际情况吓人。2026-07-24 那条里 MessagesToolAdditionBlock 与 MessagesToolRemovalBlock 的 diff 写着 type property_removed,但同一条目下的人工注解解释了:tool 从单个 tool_reference 对象放宽成了 tool_reference、mcp_tool_reference(name + server_name)和 mcp_toolset_reference(server_name)的联合类型,name 和 type 是移进了那个变体里而不是被删除,既有的 { "type": "tool_reference", "name": "…" } 载荷仍然有效——而这条 Update 并没有打 Breaking 标签。把这两处放在一起看,结论只有一条:以 Breaking 标签和人工注解为准,别拿机读 diff 的动词吓自己。
五、什么情况说明不是这个原因
以下几种症状,翻 changelog 是白翻:
- 某个模型不可用了。 《API Versioning》最后写明,模型可用性与 API 版本管理是分开的:模型由各家 provider 独立增删,要看当前有哪些模型该去 Models 页(
openrouter.ai/docs/guides/overview/models)。模型下线不会走破坏性变更那套流程。 - 你的客户端被新增的枚举值噎住了。 changelog 里像
ProviderName、Quantization、ReasoningFormat这类「enum property_added」,按官方定义属于非破坏性变更;而文档又明确要求客户端不要在未知枚举值上失败。这两条摆在一起,根因在调用方的解析逻辑,不在平台的变更流程。 - 你是裸 HTTP 调用方,却在为文档分组类的 Breaking 条目改代码。 2026-07-28 那条
$.tags: responses object removed带着「No action needed」的标注,条目自己写明这是把 OpenAPI 标签responses改成 Title Case 的Responses、纯粹是文档分组变化,端点、schema、SDK 命名空间和文档 URL 都没变。这类条目对 HTTP 调用方不构成任何动作。 - 问题是间歇性的、只在部分请求上出现。 changelog 记录的是 OpenAPI 规范层面的 release 差异,不解释单次请求之间的差异。这类现象在 versioning 与 changelog 两页里找不到依据,得换个方向查。
最后一句老生常谈但必须写:这套流程本身也可能变,上面每一处字段名、标签名与地址都以官方文档最新内容为准。真正值得固化进你团队流程的只有两件事——订 RSS、按 Breaking 过滤,以及把客户端写成不怕新字段的样子。
本文依据 OpenRouter 官方文档(openrouter.ai/docs)于 2026-08-18 的公开内容整理。
该平台闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观与运行表现的任何描述。
该平台的供应商、模型与路由策略随时变动,文中不列具体供应商名单与模型清单;
价格、额度与限流的具体数值请以官方定价页与用量说明为准。