當應用程式在一段時間內發送的請求超過 API 在該時段內允許的請求數量時,API 會回傳 429 Too Many Requests 。 這個請求本身沒問題。 你發送太頻繁,API 就拒絕了。 如果你等著再寄一次,通常會成功。 回覆通常會附上 Retry-After 標頭,告訴你該等多久。 欲了解更多資訊,請參閱 RFC 6585,第4節。
429 在熱門 API 中的樣貌
每個 API 對速率限制的實作方式都不同。 狀態碼、標頭和錯誤主體都會有所不同,因此正確處理一個 API 的程式碼可能會錯誤處理下一個。
| API | Status | 如何判斷等待多久 | 小心 |
|---|---|---|---|
| GitHub |
403 或 429 |
retry-after 若存在,否則 x-ratelimit-reset(UTC 紀元秒數),當 x-ratelimit-remaining 為 0 時,否則至少 1 分鐘 |
403 可以是速率限制或缺少的權限。 看標題就能分辨兩者。 |
| OpenAI | 429 |
retry-after |
有些 429 錯誤,比如 credit_balance_exhausted,代表重試不會有幫助。 檢查 error.code。 |
| 人為 | 429 |
retry-after |
因支出上限而發生的 429 錯誤沒有 retry-after,且會持續失敗,直到存取恢復為止。 負載過高的 API 會回傳 529,而不是 429。 |
| Microsoft Graph | 429 |
Retry-After (秒) |
限制因服務而異,例如 SharePoint 和 Outlook。 |
如何處理 429 錯誤
- 決定是否要重試。 如果錯誤顯示你的配額、信用點數或消費上限已用盡,重試也沒用。 告訴使用者,並向自己發出警示。
- 依 API 要求等待相應時間。 如果回覆中有
Retry-After,就等那麼久。 它要麼是秒數,要麼是 HTTP 日期。 如果 API 使用速率限制標頭,就像 GitHub 的x-ratelimit-reset那樣,請等到重置時間。 - 否則,請退後。 沒有 API 提示時,再用指數退縮和隨機抖動重試,嘗試幾次後停止。
- 告訴使用者發生了什麼事。 「系統忙碌,5秒後重試」比永無止境的旋轉機好。
- 在下一次 429 錯誤之前放慢請求速度。 如果 API 會傳送速率限制標頭,剩下的計數就用來調整請求的節奏。
async function fetchWithRetry(url, options, attempts = 3) {
for (let attempt = 1; ; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429 || attempt === attempts) {
return response;
}
const retryAfter = response.headers.get('retry-after');
const waitMs = retryAfter
? (isNaN(retryAfter) ? new Date(retryAfter) - Date.now() : retryAfter * 1000)
: 2 ** attempt * 1000 + Math.random() * 1000;
await new Promise(resolve => setTimeout(resolve, Math.max(waitMs, 0)));
}
}
很多 SDK 會幫你重試 429。 例如,OpenAI Python SDK 預設會重試兩次,而 .NET 標準韌性處理器則重試三次並遵循 Retry-After。 當 SDK 用盡重試次數時,你的程式碼會收到錯誤,因此仍然需要因應方案。
如何測試你的應用程式是否能處理 429
在開發過程中,你很少會看到429。 API 很快,你是唯一的使用者,測試資料也很小。 所以你測試 429 處理的方式,決定了你是否比使用者先發現錯誤。
| Approach | 你發現的 | 你錯過的內容 |
|---|---|---|
| 等待正式環境 | 實際故障 | 在使用者點擊之前的一切 |
| 在測試中模擬 API,或讓你的編碼代理寫出模擬 | 不管你的重試分支是否執行 | API 的真實狀態碼、標頭和錯誤內容,以及你的 SDK 重試政策。 你的應用程式也需要一個只供測試使用的開關來連到模擬服務。 |
| 呼叫真正的 API,直到它對你進行速率限制 | 實際行為 | 你無法隨時觸發429,而且你的真實配額會用掉 |
| 攔截你應用程式的真實流量,並按需回傳 429 回應 | 真實網址、真實的 SDK 與重試政策,以及 API 自有的 429 格式 | 你的應用程式裡沒有任何變化,所以它不會在隔離的環境中測試你的程式碼。 把那個留給你的單元測試處理。 |
在你的應用程式中試試看
Dev Proxy 會攔截你應用程式對你選擇的 API 的請求,並回傳 429,附帶 API 自己的標頭和錯誤格式,而你的應用程式則持續呼叫真實的 URL。 它還會告訴你,您的應用程式何時會在 Retry-After 時間結束前重試。
下載你應用程式呼叫的 API 預設集,並用它啟動 Dev Proxy:
devproxy config get github-rate-limiting
devproxy --config-file "~dataFolder/configs/github-rate-limiting/.devproxy/devproxyrc.json"
| API | Preset |
|---|---|
| GitHub | github-rate-limiting |
| OpenAI | openai-throttling |
| Anthropic | anthropic-throttling |
Microsoft Graph(OneDrive 與 SharePoint:/drive, /shares, /sites) |
microsoft-graph-rate-limiting |
然後照常執行你的應用程式,觀察它的表現。 要安裝 Dev Proxy,請參見 設定 Dev Proxy。