遇到 OpenRouter 429,未必是你的 OpenRouter 账户用到了限额。另一种常见情况是:所选的上游服务商正在限制请求。别急着改配置,先完整保存一条失败响应:HTTP 状态码、响应头和 JSON 响应体,足以判断究竟该处理哪一层的限制。
先读懂一条 429 响应,再动手修改
在购买额度、更换 Key 或加入重试逻辑前,先给错误分类。相比可能透传上游服务商文本的可读错误消息,OpenRouter 的结构化字段和响应头更值得信赖。
| 判断依据 | 最可能的来源 | 下一步操作 |
|---|---|---|
HTTP 429,同时包含 X-RateLimit-Limit、X-RateLimit-Remaining 和 X-RateLimit-Reset | OpenRouter 平台限额 | 等待限额重置,再降低请求速率或并发数 |
error.metadata.error_type 为 rate_limit_exceeded,且带有 provider_code 等服务商信息 | 上游服务商 | 等待、允许切换其他服务商,或使用模型回退 |
存在 Retry-After | 所有尝试过的服务商都提供了重试建议 | 等待指定时间后再发起下一次请求 |
| HTTP 402 | 余额不足,或单个 Key 的额度上限已耗尽 | 充值或调整 Key 的额度上限;退避重试无法解决 |
先返回 HTTP 200,随后 SSE 报错且出现 finish_reason: "error" | 流式输出开始后的失败 | 将该流视为失败,并检查其中嵌入的错误类型 |
OpenRouter 的错误与调试参考文档定义了 error.code、error.message 及可选的 error.metadata 响应结构,其中包括 error_type = "rate_limit_exceeded";文档还指出,成功响应通常不会包含 X-RateLimit-*。服务商过载则使用单独的 provider_overloaded 类型,通常对应 503。
处理 OpenRouter 平台层面的 429
OpenRouter 平台层面的 429,取决于账户及模型类别对应的平台配额。根据 2026 年 7 月 31 日核对的官方限流文档,以 :free 结尾的免费模型变体同时受到每分钟和每日限额约束。
| 免费模型配额 | 当前限额 |
|---|---|
| 每分钟请求数 | 20 RPM |
| 累计购买额度不足 $10 时的每日请求数 | 50 RPD |
| 累计购买额度至少为 $10 时的每日请求数 | 1,000 RPD |
限额政策明确说明,额外创建账户或 Key 并不能提高全局管理的容量。因此,替换一个有效 Key 不会重置平台限流。
可以使用 GET /api/v1/key 端点查看用量和额度限制:
curl https://openrouter.ai/api/v1/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
如果确认是平台 429,请以错误响应中的重置响应头为准,并按以下顺序处理:
- 停止立即重试,等待至
X-RateLimit-Reset指定的时间。 - 降低并发请求数,而不只是控制每秒请求数。并行 Worker 突发请求时,可能在任何一个 Worker 看到首个 429 前就已触发限制。
- 将任务放到共享限流器后的队列中,避免所有 Worker 同时醒来、同时重试。
- 如果工作负载无法满足免费模型配额,将这部分流量迁移到合适的付费模型变体。
负余额或单个 Key 的额度上限耗尽,应当返回 402;即使账户已充值,上游服务商仍然可能返回 429。有关成本和额度的独立问题,可参考这篇 OpenRouter 定价指南。
处理“Provider Returned Error” 429
服务商返回的 429,意味着 OpenRouter 已将请求发往某个上游推理服务商,但对方当时无法接收该请求。重点检查结构化的限流类型和服务商元数据;给 OpenRouter 充值并不会为该服务商增加容量。
官方限额参考指出,路由在返回错误前可能已经尝试了其他服务商;如果所有尝试过的服务商都提供了重试建议,响应中会附带 Retry-After。实际处理方式如下:
- 遵守
Retry-After,不要立刻重新生成同一请求。 - 如果严格的服务商限制让请求只能走一条拥堵线路,应适当放宽限制。
- 确认该请求允许服务商回退。
- 如果任务完成比指定模型更重要,配置模型回退。
一名已充值的 Zed 用户在使用 moonshotai/kimi-k2:free 时遇到了上游限流,重新生成 Key 并没有帮助。一位 Zed 贡献者解释道:
“这不是 Zed 的错误,而是 OpenRouter 在告诉你:你使用的上游服务商正在对你实施限流。” 来源:zed-industries/zed issue #35153
如果 Retry-After 很短,就耐心等待;仅当完成任务比坚持使用指定模型更重要时,才使用免费模型回退。
在 Janitor AI、Zed 或 SillyTavern 中看到此错误时
保留原始错误信息,避免反复重新生成;若属于服务商侧故障,请修改模型或可用路由。只有在修正客户端存储问题时才需要重新输入 Key,它不会重置可用容量。
别让重试逻辑制造 429 循环
只对限流失败执行重试,限制最大尝试次数,并优先遵从服务器要求的等待时间。如果服务端没有提供重试建议,则采用带随机抖动的封顶指数退避,避免并行客户端同步后再次形成请求尖峰。
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
function retryDelayMs(response, attempt) {
const retryAfter = response.headers.get("retry-after");
if (retryAfter) {
const seconds = Number(retryAfter);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
const dateMs = Date.parse(retryAfter);
if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());
}
const capMs = 30_000;
const exponentialMs = Math.min(capMs, 1000 * 2 ** attempt);
return Math.random() * exponentialMs; // Full jitter
}
async function createChatCompletion(body, maxAttempts = 4) {
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const response = await fetch(
"https://openrouter.ai/api/v1/chat/completions",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
},
);
const raw = await response.text();
let payload;
try {
payload = raw ? JSON.parse(raw) : null;
} catch {
payload = null;
}
if (response.ok) return payload;
const isRateLimit =
response.status === 429 ||
payload?.error?.metadata?.error_type === "rate_limit_exceeded";
if (!isRateLimit || attempt === maxAttempts - 1) {
const error = new Error(payload?.error?.message || raw || `HTTP ${response.status}`);
error.status = response.status;
error.details = payload?.error;
throw error;
}
await sleep(retryDelayMs(response, attempt));
}
}
这段函数处理的是非流式响应。共享队列或令牌桶并发控制应放在函数外部,避免大量等待中的 Worker 同时恢复执行。
一旦 Server-Sent Events 以 HTTP 200 开始传输,状态码就不可能再变成 429。OpenRouter 错误参考说明,后续失败会作为流中的 error 出现,并以 finish_reason: "error" 结束;应将此次补全标记为失败,且仅当嵌入的类型为 rate_limit_exceeded 时才重试。除非应用明确支持部分结果,否则不要把已累积的文本当作成功结果返回。
常见问题
OpenRouter 中的 429 provider returned error 是什么意思?
这表示上游推理服务商因自身的速率或容量限制拒绝了请求。可通过 error.metadata.error_type 和服务商元数据进行确认。
为什么我还有额度,却收到了 OpenRouter 429?
已充值账户同样可能收到服务商侧的 429;余额不足或单个 Key 的额度上限通常会返回 402。
创建新的 OpenRouter API Key 能重置限流吗?
不能。额外的 Key 不会提高全局管理的限额;只有在认证或客户端存储出现问题时,才需要更换 Key。
重试 OpenRouter 前应该等多久?
存在 Retry-After 时,以它为准。平台限流则使用 X-RateLimit-Reset;两者都没有时,采用带随机抖动的封顶指数退避,并设置较小的最大尝试次数。
OpenRouter 会返回 HTTP 200,但实际仍因 429 失败吗?
会,发生在流式传输已经开始时。HTTP 状态会保持为 200,而 SSE 流会报告错误并以 finish_reason: "error" 结束;检查嵌入的错误类型,即可判断是否由限流导致。