我使用 HTTP Request 節點進行多個 API 呼叫,但過了一段時間後開始收到 429 Too Many Requests 錯誤。處理 API 速率限制的最佳方式是什麼。
嘿 @Victory1,在等待回應的同時,以下是一些可能對你有幫助的資源:
建議資源
自動符合你的問題。
文檔:
論壇:
@Yo_its_prakash、@MutedJam、@Niffzy - 你們之前幫助過類似的問題,可以看一下嗎?
由 n8n 的社群機器人自動建議。這是試驗版 - 請在此分享意見。
調整你的請求速度以避免 API 端點過載。
加入足夠的等待時間
429 錯誤表示您已超過 API 的請求限制。
嘗試在請求之間添加等待節點、使用迴圈遍歷項目(批次處理)分批處理項目,或實現帶指數退避的重試邏輯。
也值得查看 API 文檔的速率限制指南,以避免不必要的失敗。
在上述內容之上有一個小但重要的區別——HTTP Request 節點有其自己的批次選項,這與 Loop Over Items 批次不同,不需要額外的節點:
1. HTTP Request → Options → Add Option → Batching
Items per Batch 1、Batch Interval 1000 = 每秒一個請求。沒有 Loop Over Items、沒有 Wait 節點、無需重新接線。
2. 兩個值得了解的陷阱
- 在一個批次內,請求並行執行——Items per Batch
10是 10 個同步連接,而不是 10 個分散開的。如果 API 限制並發,請保持為 1。 - n8n 內建的失敗重試使用固定間隔,忽略
Retry-After——所以它不是指數退避。要實現真正的退避,你需要一個 IF + Wait 迴圈來自己讀取標頭。
3. 找出你首先遇到的限制
從一個成功的響應中記錄 x-ratelimit-limit / -remaining / -reset:
- 每秒突發限制 → 調整步速可以解決
- 每日配額 → 調整步速無法解決,你需要減少呼叫
- 並發上限 → 只有 Items per Batch 1 可以解決
相同的 429,三個不同的問題。
4. 如果工作流並行運行多次
在一次執行內調整步速無法幫助——每次運行都禮貌地等待,但它們仍然會一起淹沒 API。自託管:N8N_CONCURRENCY_PRODUCTION_LIMIT=1。
是哪個 API?很樂意提供更具體的幫助。
Hi @Victory1
如果 API 有批量或批次端點,請求計數本身可以減少。在 HTTP Request 節點之前放置一個 Aggregate 節點,並將 Aggregate 設定為 All Item Data,整個集合將作為一個項目到達 data 底下。在 JSON 主體中引用它:
{{ JSON.stringify($json.data) }}
五十個項目隨後會以一個請求發送,而不是五十個。
很棒的分析來自 @Hammad_gaming。補充他指出但沒有實作的部分,加上上述修復都沒有涵蓋的一類 429 錯誤。
Retry-After 迴圈,因為它被提到但沒有展示
HTTP Request 節點:關閉 Retry On Fail,並設定 Options > Response > Never Error = true。你需要 429 作為資料返回,而不是拋出錯誤,否則你無法讀取標題。
然後在 {{ $json.statusCode }} 等於 429 時使用 IF → Code 節點來計算等待時間 → Wait 節點 → 迴圈回到 HTTP 節點。
Code 節點:
const h = $json.headers || {};
let sec = 1;
if (h[‘retry-after’]) {
const v = h[‘retry-after’];
// Retry-After 可以是 delta-seconds 或 HTTP-date
sec = isNaN(v) ? Math.max(0, (new Date(v) - Date.now()) / 1000)
: Number(v);
} else if (h[‘x-ratelimit-reset’]) {
const r = Number(h[‘x-ratelimit-reset’]);
// 有些 API 傳送 epoch 秒數,有些傳送剩餘秒數
sec = r > 1e9 ? r - Math.floor(Date.now() / 1000) : r;
} else {
const n = $runIndex || 0;
sec = Math.min(2 ** n, 60);
}
// jitter — 這一行比看起來還重要
sec = sec * (0.5 + Math.random() * 0.5);
return [{ json: { waitSeconds: Math.ceil(sec) } }];
其中三件事很容易被忽略:
Retry-After 可以是 HTTP-date,不只是秒數。將其解析為整數會默默產生 NaN 和零秒等待,所以你會立即重試並再次獲得 429。
x-ratelimit-reset 在不同 API 間不一致 — GitHub 傳送 epoch 秒數,其他傳送剩餘秒數。大小檢查可以處理兩者。
jitter 這一行是人們會跳過的。不用它,如果你有 20 個項目同時都命中 429,它們都會等待完全相同的時長,並在同一時刻重試。你已經用額外步驟重建了原始問題。將等待時間隨機化可以分散它們。
重試風暴,速率限制無法解決的問題
值得明確說明:Retry On Fail 按項目重試。50 個項目、每個 3 次嘗試,以及在項目 20 命中的 429,意味著你可以向剛告訴你停止的 API 發送 90 個額外請求。有些 API 會透過延長阻止來回應這種情況。
如果你在批次上使用 Retry On Fail,請將 Max Tries 設定為 2,並用上面的迴圈處理真正的重試,其中速率限制在你的控制下。
沒人提過的一個:令牌限制,不是請求限制
@Victory1 — 如果這是 OpenAI、Anthropic、Cohere 或任何 LLM API,你很可能根本沒有命中請求限制。
這些提供商實施兩個獨立限制:每分鐘請求數和每分鐘令牌數。TPM 通常是你先命中的,上面的速率限制建議都不涉及它,因為限制是負載大小而不是呼叫頻率。
告訴你是哪一個的症狀:如果當你的輸入文件變長時 429 變得更糟,但呼叫次數沒有改變,那就是 TPM。
修復方式也不同 — 你減少令牌而不是減慢速度。修剪提示、如果你設定得很高就放下 max_tokens(有些提供商針對預留而不是實際輸出計入你的預算)、分割長文件,或將短呼叫路由到較小的模型。
回應標題如果你記錄它們會直接命名:x-ratelimit-remaining-requests vs x-ratelimit-remaining-tokens。先達到零的是你的實際限制。
以及浪費一個下午的那個
有些 API 根本不返回 429。Shopify 的 GraphQL 端點返回 200,但限制狀態在正文內。不止一個支付提供商也是這樣做。
如果你的工作流程「沒有出錯」但資料默默遺失,檢查回應正文而不是狀態碼。對 statusCode 的 IF 永遠不會捕捉到它。
是哪個 API?對於這三種情況中的每一種,正確的修復都不同。