看到 OpenRouter 回傳 429,不代表你的 OpenRouter 帳號一定已經碰到額度上限;也可能是你選用的上游供應商正在限流。先把一筆完整的失敗回應保存下來:HTTP 狀態碼、回應標頭與 JSON 內容,足以判斷你真正該處理的是哪一種限制。
先看一筆 429 回應,再決定怎麼處理
別急著買點數、換 API 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 問題時才重新輸入 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));
}
}
這個函式處理的是非串流回應;共用佇列或 token-bucket 併發控制應放在函式之外,避免大量等待中的 worker 又同時重新啟動。
一旦 Server-Sent Events 已以 HTTP 200 開始串流,狀態碼就不可能再變成 429。OpenRouter 錯誤文件指出,後續失敗會以串流中的錯誤與 finish_reason: "error" 傳回;應將這次 completion 標示為失敗,且僅在內嵌類型為 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" 結束;請檢查內嵌錯誤類型,確認是否為速率限制。