外觀
429 Too Many Requests
429 Too Many Requests 代表你在單位時間內送出的請求太多,超過服務允許的速率。這是暫時性錯誤,間隔後重試通常會成功。
標準語意
這是通用的 HTTP 狀態碼,意義為「請求頻率超過限制」。服務通常會透過 Retry-After 標頭告知建議等待時間。
常見原因
| 原因 | 說明 |
|---|---|
| 短時間內請求過多 | 迴圈中未節流,一次送出大量請求 |
| 併發過高 | 同時開啟過多連線 |
| 多個程式共用金鑰 | 多個服務或團隊成員共用同一把金鑰,合計超限 |
| 重試風暴 | 失敗後立刻無間隔重試,讓情況惡化 |
| 用量暴增 | 促銷活動、爬取或批次任務造成流量尖峰 |
| 共用出口 IP | 多個使用者經同一 NAT/Proxy 出口 |
待補充
瓜瓜AI 的實際速率限制數值(RPM/TPM/併發數)、計算維度(依金鑰、IP 或帳號)與是否回傳 Retry-After/配額標頭,尚待官方確認。本頁不提供任何未經確認的數字。
排查步驟
1. 確認是否為速率限制
bash
curl -i https://api.guagua5487.xyz/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $GUAGUA_API_KEY" \
-d '{"model":"MODEL_ID","messages":[{"role":"user","content":"ping"}]}'觀察:
- 狀態碼是否為
429; - 是否有
Retry-After或速率相關標頭; - 回應主體是否說明限制類型。
與 403 的差別
403 是「沒有權限」(重試無用),429 是「暫時太多」(重試有效)。詳見 403 Forbidden。
2. 判斷觸發模式
| 觀察 | 可能原因 |
|---|---|
| 偶發、短暫出現 | 流量尖峰或上游擁擠 |
| 固定時間後必出現 | 週期性批次任務撞到限制 |
| 一上線就出現 | 併發設定過高 |
| 特定時間段才出現 | 與其他系統共用金鑰 |
3. 立即緩解
- 暫停批次任務,改為分批處理。
- 降低併發數(例如同時最多 2~4 個請求)。
- 在請求之間加入延遲。
正確的重試方式
使用指數退避 + 隨機抖動(jitter),並優先遵循 Retry-After:
python
import random
import time
from openai import OpenAI, APIStatusError
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.guagua5487.xyz/v1",
)
def chat_with_backoff(prompt: str, attempts: int = 5) -> str:
for i in range(attempts):
try:
resp = client.chat.completions.create(
model="MODEL_ID",
messages=[{"role": "user", "content": prompt}],
)
return resp.choices[0].message.content or ""
except APIStatusError as e:
if e.status_code != 429 or i == attempts - 1:
raise
# 優先採用伺服器建議的等待時間
retry_after = e.response.headers.get("retry-after")
if retry_after:
delay = float(retry_after)
else:
delay = min(2 ** i, 30) + random.uniform(0, 0.5) # 指數退避 + 抖動
time.sleep(delay)
return ""javascript
async function chatWithBackoff(prompt, attempts = 5) {
for (let i = 0; i < attempts; i++) {
try {
const resp = await client.chat.completions.create({
model: 'MODEL_ID',
messages: [{ role: 'user', content: prompt }]
})
return resp.choices[0].message.content ?? ''
} catch (err) {
if (err.status !== 429 || i === attempts - 1) throw err
const retryAfter = err.headers?.['retry-after']
const delay = retryAfter
? Number(retryAfter) * 1000
: Math.min(2 ** i, 30) * 1000 + Math.random() * 500
await new Promise((r) => setTimeout(r, delay))
}
}
return ''
}不該做的事
| 反模式 | 問題 |
|---|---|
| 無間隔立刻重試 | 加重限制,可能導致更長時間被擋 |
| 無限重試 | 請求堆積、資源耗盡 |
| 多執行緒同時重試 | 形成重試風暴 |
忽略 Retry-After | 過早重試仍然失敗 |
避免觸發限制
| 做法 | 說明 |
|---|---|
| 控制併發 | 設定併發上限與佇列 |
| 批次加間隔 | 批次任務分批送出,加入固定延遲 |
| 請求合併 | 能用一次請求處理就別拆成多次 |
| 快取結果 | 相同輸入直接回快取 |
| 分散金鑰 | 不同服務使用不同金鑰(若方案允許) |
| 監控用量 | 記錄請求數與 429 發生率,提早發現趨勢 |
| 串流降低壓力 | 串流不會減少請求數,但可改善使用者感受 |