企业级 Claude API 官方通道中转

API 429 Too Many Requests 怎么解决?先定位限流来源再安全重试

更新于 2026-10-25 · fishxcode.cyou

简答:API 429 Too Many Requests 表示请求在某个时间窗口内超过了服务端、网关或账户限制,但不等于一定是代码频率过高。先记录状态码、响应体、Retry-After、RateLimit 或 X-RateLimit 响应头和请求 ID,再区分临时限流、Token/并发限制、每日额度与支出上限;临时限流使用带随机抖动的指数退避,额度或支出限制则应降低用量、检查控制台或申请调整,不能无限重试。

定义:API 429 Too Many Requests 是 HTTP 状态码,表示服务端在一定时间内限制了当前客户端、凭证、项目、资源或组织的请求速率;RFC 6585 允许响应通过 Retry-After 告知客户端何时再次尝试。

先做三步分流:不要看到 429 就盲目重试

本站是独立的 AI API 接入指南站,并非 FishXCode 官方站;本文只依据公开文档和通用 HTTP 规则说明排查方法。

  1. 确认限流层:直接请求上游时查看上游响应;经过客户端、反向代理或 API Router 时,同时保存每一层的状态码、响应头和日志。若客户端日志显示 429、代理日志没有 429,而响应体带有上游请求 ID,优先检查上游;若代理直接返回 429,则检查代理的 IP、Key、路由和并发规则。
  2. 确认限制维度:检查请求数、并发连接、输入或输出 Token、每日额度、项目额度、组织支出上限,以及是否存在短时突发限制。不同平台的维度并不相同,不能把某个平台的 RPM 直接套到另一个平台。
  3. 确认是否可重试:有 Retry-After 或明确的短时限流信息时,可以在总截止时间内退避;若响应表示每日额度、月度支出上限或凭证被限制,应该停止自动重试,转为检查控制台、降低用量或申请调整。

HTTP 429 的标准含义和 Retry-After 语义可参考 RFC 6585;通用响应头说明也可参考 MDN 的 429 状态码说明。

429 速查表:临时限流、Token 限制和额度上限要分开

现象优先检查处理动作是否适合自动重试
短时间集中请求后出现 429Retry-After、请求速率、并发数降低并发,加入指数退避和随机抖动可以,但要限制次数和总时间
请求数量正常但大输入后出现 429输入 Token、输出 Token、模型或路由限制缩短上下文、降低输出上限、排队或拆分任务仅在服务端提示为临时限制时重试
每天或每月持续失败每日配额、项目额度、组织支出上限查看用量与账单页面,降低用量或申请调整通常不应持续重试
经过 API 网关后才出现 429网关按 IP、Key、用户或路由设置的规则查看网关日志、桶容量、并发和队列设置先确认网关是否允许重试
同时伴随 503 或 529服务端容量、上游可用性和请求 ID按服务商规则退避,并保留请求记录可按官方错误处理说明重试

例如,Google Gemini 文档将 429 的原因细分为 RPM、TPM、RPD、模型限制和支出限制;其官方排障页建议对可重试错误使用指数退避,并设置最大重试次数。请以 Gemini Rate limits 和 Gemini Troubleshooting guide 中当前显示的限制为准。

读取响应头和响应体:先留下能定位问题的证据

每次 429 至少记录以下字段,但不要记录完整 API Key:

OpenAI API 文档列出了可用于排查的请求 ID及多项 x-ratelimit-* 响应头,具体字段应以实际接口返回为准,参考 OpenAI API Reference。Anthropic 的错误响应包含顶层错误对象和 request_id;其 429 可能对应速率限制或支出上限,参考 Claude API errors。

指数退避怎么写:Retry-After 优先,缺失时再用退避公式

推荐的等待顺序是:服务端给出合法的 Retry-After 时优先采用;没有该字段时使用 baseDelay × 2^attempt + random(0,jitter),再限制到最大等待时间,并在等待前与总截止时间比较。随机抖动可以减少多个工作进程同时重试造成的再次拥塞。

如果具体服务商提供 Batch 能力,可以评估将不要求实时返回的任务改为批处理;是否支持、限制和计费应以该服务商当前文档为准,不能据此假定所有 API 都有相同能力。

Python 可复制示例:处理秒数、HTTP-date、deadline 和幂等判断

下面示例使用标准库,默认只对明确标记为幂等的请求自动重试;非幂等 POST 不应仅因为收到 429 就无条件重复提交。示例会释放响应体,并在最终失败时抛出最后一次 HTTP 错误。

Python:import email.utils, random, time, requests def retry_after_seconds(value): if not value: return None try: return max(0.0, float(value)) except ValueError: try: return max(0.0, email.utils.parsedate_to_datetime(value).timestamp() - time.time()) except (TypeError, ValueError, OverflowError): return None def request_with_retry(method, url, *, headers=None, json=None, idempotent=False, max_retries=4, total_timeout=30): deadline = time.monotonic() + total_timeout last = None for attempt in range(max_retries + 1): remaining = deadline - time.monotonic() if remaining <= 0: raise TimeoutError('retry deadline exceeded') response = requests.request(method, url, headers=headers, json=json, timeout=remaining) if response.status_code != 429 or not idempotent: response.raise_for_status() return response last = response response.close() if attempt == max_retries: break delay = retry_after_seconds(response.headers.get('Retry-After')) if delay is None: delay = min(8.0, 0.5 * (2 ** attempt)) + random.uniform(0, 0.25) delay = min(delay, max(0.0, deadline - time.monotonic())) if delay <= 0: break time.sleep(delay) raise RuntimeError(f'429 after {max_retries + 1} attempts; last={last.status_code if last else None}')

JavaScript 可复制示例:Retry-After 支持 HTTP-date,等待不超过总截止时间

该实现使用原生 fetch,显式处理最终失败分支。对于非幂等请求,调用时传入 idempotent: false;若业务需要重试写入操作,应配合服务商支持的幂等键和业务去重。

JavaScript:function retryAfterMs(value) { if (!value) return null; const seconds = Number(value); if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000); const date = Date.parse(value); return Number.isFinite(date) ? Math.max(0, date - Date.now()) : null; } const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); async function fetchWithRetry(url, options = {}, { idempotent = false, maxRetries = 4, totalTimeoutMs = 30000 } = {}) { const deadline = Date.now() + totalTimeoutMs; let lastResponse = null; for (let attempt = 0; attempt <= maxRetries; attempt++) { const remaining = deadline - Date.now(); if (remaining <= 0) throw new Error('retry deadline exceeded'); const response = await fetch(url, { ...options, signal: AbortSignal.timeout(remaining) }); if (response.status !== 429 || !idempotent) { if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`); return response; } lastResponse = response; await response.arrayBuffer(); if (attempt === maxRetries) break; let delay = retryAfterMs(response.headers.get('retry-after')); if (delay === null) delay = Math.min(8000, 500 * (2 ** attempt)) + Math.random() * 250; delay = Math.min(delay, Math.max(0, deadline - Date.now())); if (delay <= 0) break; await sleep(delay); } throw new Error(`429 after ${maxRetries + 1} attempts; last=${lastResponse?.status ?? 'none'}`); }

Java 与 Go 的实现要点:把重试放在请求边界,不要重复提交未知结果

Java 使用 HttpClient 时,应为每次请求设置 HttpRequest 超时,并用 Instant.now() 计算总 deadline;读取并关闭响应体后,再解析 Retry-After 的秒数或 HTTP-date。Go 使用 context.WithTimeout 传递总截止时间,读取并关闭 resp.Body,用 http.Header.Get("Retry-After") 解析等待值。两种语言都应在重试前判断请求是否幂等,并在循环结束后返回最后一次错误,不能只记录日志后静默返回。

如果请求可能已经在服务端执行但客户端没有收到结果,尤其是支付、创建资源或写入操作,应优先使用服务商提供的幂等键、业务唯一号或查询接口确认结果,再决定是否重试。HTTP 429 本身不能证明上一次非幂等操作没有执行。

完整排查表:429、403、503 和超时分别怎么处理

错误通常表示什么首要动作不应直接做什么
401Key、签名或认证信息有问题核对 Key、请求头和环境变量不要靠重试解决鉴权错误
403权限、项目状态、策略或资源访问被拒绝检查项目、组织、工作区和权限不要把 403 当成 429 延迟重试
404路径、版本、资源或模型标识不存在核对 Base URL、API 版本和资源路径不要通过增加并发重试
429速率、并发、Token、配额或支出限制读取响应头和错误体,分辨临时限制与额度上限不要无限重试或同时放大并发
503服务暂时不可用或容量不足按官方建议退避,记录请求 ID和时间不要把它简单归因于账户额度
504 或客户端超时在截止时间内没有拿到响应缩短请求、调整超时并确认上游是否已执行不要对未知结果的非幂等写入盲目重放

Anthropic 当前错误文档将 429、503、504 和 529 分开说明,并指出其官方 SDK 对部分瞬时失败会自动退避;如果你使用 SDK,先确认 SDK 的默认重试次数,避免与业务层重试叠加。具体行为以 Anthropic 错误文档 为准。Google 的错误文档也将 429 的速率限制、每日配额和支出限制分开列出,参考 Gemini API errors。

修复顺序:从最小改动开始,避免把问题扩大

  1. 先用单请求复现,并保存状态码、响应体、响应头和请求 ID。
  2. 确认 Base URL、模型、Key 和请求路径没有在多个配置层重复拼接或错误路由。
  3. 把并发降到单线程或小队列,观察 429 是否消失。
  4. 加入有限次数的指数退避、随机抖动、单请求超时和总 deadline。
  5. 对输入 Token、输出上限、分页和轮询频率做减量。
  6. 若仍持续返回 429,检查项目、组织或工作区的用量、配额和支出控制;这类情况通常不是等待几秒就能解决。
  7. 将最终失败任务放入死信队列,保留原始请求摘要、错误类型和追踪 ID,避免丢失任务或重复执行。

对于 OpenAI 等平台,响应头中的限流字段和请求 ID适合接入监控;对于 Anthropic 和 Gemini,应同时解析官方错误体中的类型、消息及配额提示。字段可能随 API 版本变化,生产代码应对缺失字段保持兼容。

参考资料

常见问题

API 429 Too Many Requests 一定是请求太快吗?

不一定。429 可能来自请求速率、并发数、输入或输出 Token、每日配额、模型限制、项目额度或支出上限。应先查看 Retry-After、限流响应头、响应体和控制台用量,不能只降低循环速度。

没有 Retry-After 时,429 应该等待多久?

没有统一秒数。可以使用带随机抖动的指数退避,例如从较短等待开始逐步增加,并设置最大等待、最大重试次数和总截止时间;如果错误体明确表示额度或支出上限,则停止自动重试。

429 需要换 Key 或换模型吗?

不要把换 Key 或换模型当作默认修复方案。先确认限制是按 IP、Key、项目、组织、模型还是路由计算;如果是账户额度或组织支出上限,换 Key 可能无效,也可能造成用量记录混乱。

POST 请求收到 429 可以直接重试吗?

只有在请求幂等、服务商支持幂等键,或你能通过查询接口确认上一次没有执行时才适合自动重试。支付、创建资源和写入操作可能出现客户端超时但服务端已执行的情况,应先做去重或查询。

为什么加了 SDK 重试后仍然持续 429?

SDK 的重试通常只适合临时错误;如果触发的是每日配额、月度支出上限或持续的加速限制,继续重试不会改变限制。还要检查 SDK 默认重试与业务层重试是否叠加,避免实际请求量进一步增加。

API 网关返回 429,应该先查上游平台吗?

先看响应来自哪一层。若网关日志显示本地规则直接拒绝,应查 IP、Key、用户、路由和并发配置;若网关只是转发上游 429,则结合上游响应体、请求 ID和限流头排查。

相关阅读

参考资料

  1. MDN:429 Too Many Requests
  2. OpenAI:Rate limits
  3. OpenAI:API Reference
  4. Google Gemini:Rate limits
  5. Google Gemini:Troubleshooting guide
  6. Google Gemini:API errors