排查手册
429 不止一种:限流、配额耗尽、平台过载的响应体各不相同,处理方式也完全不同。这份手册教你 30 秒定位 429 的真实原因,并给出可直接复制的指数退避重试代码。
收到 429 Too Many Requests 时,第一件事不是重试,而是读响应体: 429 至少有三种完全不同的成因,处理方式互不通用。盲目重试会让限流雪上加霜,充值也解决不了频率超限。
| error.code / type | 含义 | 正确处理 |
|---|---|---|
rate_limit_exceeded | RPM(每分钟请求数)或 TPM(每分钟 token 数)超限 | 指数退避重试 + 降低并发 |
insufficient_quota | 配额耗尽:余额不足、免费额度用完或未开通付费 | 重试无效,检查余额与计费状态 |
无 body / slow_down | 平台级过载或防护性限流 | 按 Retry-After 等待后重试,持续出现则切换模型或线路 |
典型的频率超限响应长这样,重点看 error.code 与 error.message 里的限额数字:
{
"error": {
"message": "Rate limit reached for gpt-4o in organization org-xxx on requests per min (RPM): Limit 500, Used 500.",
"type": "requests",
"code": "rate_limit_exceeded"
}
}而配额型 429 的 code 是 insufficient_quota,message 通常包含 “check your plan and billing details”——这种情况重试多少次都没用。
优先遵循响应头 Retry-After;没有时用指数退避加随机抖动,并设置重试上限:
import time, random
from openai import OpenAI, RateLimitError
client = OpenAI(base_url="https://zerofa.ai/v1", api_key="YOUR_API_KEY")
def chat_with_retry(messages, max_retries=5):
for attempt in range(max_retries):
try:
return client.chat.completions.create(
model="gpt-4o", messages=messages)
except RateLimitError as e:
if attempt == max_retries - 1:
raise
retry_after = getattr(e, "response", None)
wait = float(retry_after.headers.get("retry-after", 0)) if retry_after else 0
wait = wait or (2 ** attempt) + random.random()
time.sleep(wait)insufficient_quota 意味着账户层面出了问题:余额耗尽、免费额度到期、或绑定的支付方式失效。 处理顺序:查余额 → 查计费状态 → 确认用量面板里配额归零的时间点。这类 429 在代码里应当直接告警而不是重试。
如果你的业务同时调多家模型,逐家处理限流规则的成本很高。通过 OpenAI 兼容网关接入是更省力的做法: 每个 API Key 可以单独配置 RPM 与日预算、失败调用不扣费、模型之间切换只改 model 字段。 细节见错误码与计费规则,五分钟接入见快速开始,全部可用模型与限额见模型广场。
优先读响应头 Retry-After(秒)。没有该头时用指数退避:首次等 1 秒,之后每次翻倍并加随机抖动,最多重试 5 次。收到 429 立刻原样重发只会让限流窗口不断刷新。
先看响应体里的 error.code。rate_limit_exceeded 是请求频率超限,与余额无关;只有 insufficient_quota 才和配额/余额有关。频率超限要降并发或做退避,充值解决不了。
官方平台的限流通常按账号/组织计,而不是按 Key,轮询同账号的多个 Key 没有效果,还可能违反服务条款。正确做法是控制并发、退避重试,或走聚合网关按需分配额度。
会。429 发生在建立请求时,与是否流式无关。流式请求被限流时连接会直接以 429 结束,处理方式与普通请求相同:读响应体分类后退避重试。