Router One
返回博客

LLM API 429 限流报错:三种成因与对症修法

发布Router One Team

HTTP 429 是 LLM API 里信息量最低的报错:三种本质完全不同的故障共用这一个状态码,而三种故障的修法各不相同。重试代码只能修其中一种;对第二种,重试只是推迟必然的失败;对第三种,重试纯属浪费。这篇是错误码速查页背后的 429 深挖:怎么分辨你撞上的是哪一种,唯一值得重试的那种该怎么写生产级重试,以及让 429 从源头减少的结构性改法。

一个状态码,三种故障

实际发生了什么限制在哪一层修法
上游供应商限流(RPM/TPM 超限)模型厂商的基础设施有上限的指数退避 + 抖动;有 Retry-After 就照做
你自己给 Key 配的限额你的 Key 设置(rateLimit / tokenLimitTpm改仪表盘设置——重试代码无济于事
OpenAI 的 insufficient_quota官方 OpenAI API 的账单账户解决账单问题;重试毫无意义

误判方向的代价是双向的。把自己配的 Key 限额当上游问题,你会写出一套对着永远不动的天花板反复冲撞的重试逻辑;把 insufficient_quota 当瞬时限流,你会对着一个没有余额的账户白白重试几个小时。

成因一:上游供应商在限流

模型厂商都有每分钟请求数和每分钟 token 数的上限,负载高峰时还会主动用 429(以及 Anthropic 风格的 529 过载响应)甩流量——哪怕你自己的请求频率没变。这是唯一一种客户端重试算对症下药的情况,但必须是有边界的重试,三个要素缺一不可:

  • 带上限的指数退避。 每次失败等待时间翻倍,但要有封顶——不封顶的话,第五次重试可能为一个几秒就恢复的限流干等几分钟。
  • 抖动(jitter)。 每次延迟加随机量。没有抖动,同一批失败的客户端会在同一时刻集体重试,同步的重试波会再次触发限流。
  • 放弃路径。 固定次数之后把错误抛出去。无限循环不是韧性,是一队越攒越陈旧的任务。

响应里带 Retry-After 头时照做——服务端已经明确告诉你容量什么时候恢复,这时还用退避去猜只会拖长故障时间。下面是 Python 版本,用标准 OpenAI 客户端指向 Router One:

import os
import random
import time

from openai import OpenAI, RateLimitError

client = OpenAI(
    base_url="https://api.router.one/v1",
    api_key=os.environ["ROUTER_ONE_API_KEY"],
)

MAX_ATTEMPTS = 5
BASE_DELAY = 1.0  # 秒
MAX_DELAY = 30.0  # 封顶:最长等这么久


def chat_with_backoff(**kwargs):
    for attempt in range(1, MAX_ATTEMPTS + 1):
        try:
            return client.chat.completions.create(**kwargs)
        except RateLimitError as err:
            if attempt == MAX_ATTEMPTS:
                raise  # 放弃路径:抛出错误,不要无限循环

            retry_after = err.response.headers.get("retry-after")
            if retry_after is not None:
                # 服务端已明确说了容量何时恢复——不需要抖动
                time.sleep(min(float(retry_after), MAX_DELAY))
            else:
                delay = min(BASE_DELAY * 2 ** (attempt - 1), MAX_DELAY)
                # full jitter:把同步失败的客户端错开
                time.sleep(random.uniform(0, delay))


response = chat_with_backoff(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "ping"}],
)

五次尝试、30 秒封顶、带抖动的延迟、尊重 Retry-After、最后干净地失败。整个模式就这些——忍住别把它写得更"聪明"。

成因二:撞上了你自己配的限额

Router One 的 API Key 带可选的 key 级控制:rateLimit(请求频率)和 tokenLimitTpm(每分钟 token 数),外加 maxSpend 消费硬上限。它们存在的意义是让泄漏的 key 或失控的循环抽不干你的钱包——每客户一把 key 的转售模式重度依赖这套控制。但你六周前设的限额,在撞上它的客户端看来和上游 429 一模一样。

识别特征:这类 429 是确定性的。流量一过配置的阈值就开始报,而且不管几点、不管调哪个模型,每次都在同一个请求频率上复现。退避参数调得再精细也改变不了天花板——修法在 Dashboard → API Keys,调高或移除那把 key 的限额。(如果 key 停摆时报的是 402,那是 maxSpend 上限或钱包余额的问题——钱的事,不是速度的事,速查页写了两者的区别。)

调高限额之前先问一句:为什么会撞上?如果是某个后台任务把生产流量共用的 key 打满了,更好的修法是再开一把带独立限额的 key,把吵闹的负载隔离出去、单独可见。

成因三:insufficient_quota——套着 429 外衣的账单问题

如果你直连官方 OpenAI API,错误体里写着 insufficient_quota,请立刻停止重试。状态码虽然是 429,但它跟限流毫无关系:你的预付账单额度用光了,这个"限制"不会在几秒、几分钟后重置——账单不动它永远不动。每一次重试都是白打的请求,退避只是让你浪费得慢一点。

这个报错来自 OpenAI 平台,不是网关。insufficient_quota 修复指南完整讲了成因和解决路径,包括绑了支付方式但额度依然为零的情况。

怎么判断撞上的是哪一种

靠客户端症状猜不可靠,请求 trace 才可靠。在 Router One 上,Dashboard → Logs 展示每个请求的最终记录——状态、模型、Token、花费、延迟与路由决策——可按模型和时间范围过滤。诊断归结为三步:

  1. 先读错误体。 insufficient_quota 会自报家门。看到这个字符串就是账单问题——走成因三。
  2. 再查 Key 配置。 如果出错的 key 设了 rateLimittokenLimitTpm,把配置的阈值和日志里的实际请求频率对一下。对上了就是成因二,修法是改设置。
  3. 最后看模式。 上游限流(成因一)是突发的、和负载高峰相关——429 集中在特定时间窗,然后自行消散。自己配的限额则是平直、可预测的。

同一份每请求记录还能回答后续问题——那段故障窗口的重试白烧了多少 token——这就进入成本追踪的范畴了。

重试风暴:天真的重试如何把限流越搞越糟

限流是系统满载的信号。天真的响应——立刻重试、无限重试——把这个信号变成了放大器:每个被拒的请求变成两个,再变成四个,总到达率恰好在系统最需要降压的时刻不断爬升。这就是重试风暴,它能把服务在原始高峰早已过去之后继续按在水下。有上限的尝试次数和抖动(见上)是重试侧的防御。

结构性的修法是并发整形:用信号量或固定 worker 池限制同时在途的请求数,而不是每条任务起一个并发、让退避去收拾残局。8 个 worker 消化 10,000 条任务的队列,产生的是一条平稳可预测、天然低于限流线的请求速率;同样 10,000 条任务一次性并发发出,得到的是一堵 429 墙加一波同步重试潮。退避是安全带,并发上限才是把车速控制在能活命的区间。

网关故障转移在哪一层帮上忙

走 Router One 调用,在错误到达你之前多了一层:符合条件的 429 或 529,在存在另一个提供同一请求模型的健康供应商时,可以在那条路由上重试。限定词很重要——并非每个 429 都可重试(你自己配的 key 限额、insufficient_quota 这类账单故障都不在候选之列),故障转移也不是零停机保证:没有兼容路由完成请求时,应用仍会收到错误。所以客户端的退避循环无论如何都要保留;故障转移的判定逻辑另有专页。

至于 Router One 自己的限流口径:正常付费用量没有低固定上限——但滥用防护、账户级保护限制和上游约束仍然存在。如果正当的负载撞上了天花板,那是找支持团队聊的事,不是架构问题。

常见问题

怎么判断 429 来自我的 Key 限额还是上游供应商? 查 Dashboard → Logs 的请求 trace 和 Key 配置。如果出错的 key 设了 rateLimit 或 tokenLimitTpm,且日志里的请求频率正好对上阈值,那就是你自己配的限额——去仪表盘改。上游 429 是突发的、跟负载高峰相关、会自行消散;自己配的限额则在同一请求频率上确定性地复现。

每个 429 都应该重试吗? 不。只有限制真的会重置的情况——上游限流——才值得用有上限的指数退避加抖动去重试。自己给 key 配的 rateLimit / tokenLimitTpm 触发的 429,设置不改就不会消失;OpenAI 的 insufficient_quota 是账单额度耗尽,任何重试节奏都填不回余额。

Router One 会对我的请求加自己的限流吗? 正常付费用量没有低固定上限。滥用防护、账户级保护限制和上游供应商约束仍可能生效。key 级的 rateLimit 和 tokenLimitTpm 是可选控制项——只对你主动配置过的 key 生效。

只做指数退避够吗? 必要但不结构性。退避处理的是单个失败请求,拦不住一个无界批量任务从源头制造 429。用信号量或固定 worker 池限制在途并发,让总请求速率从构造上就低于限流线,退避留作安全网。

网关故障转移能让 429 彻底消失吗? 不能。符合条件的 429 或 529 在存在另一个提供同一请求模型的健康供应商时可以重试,这能吸收相当一部分上游限流事件——但并非每个 429 都可重试,这也不是零停机保证。应用仍应准备好用有上限的退避处理最终返回的 429。

排查清单

429 出现时:先读错误体(insufficient_quota 等于账单问题),再查 key 配置的限额(仪表盘一改就好),最后才当作真正的上游限流处理——有上限的退避加抖动、尊重 Retry-After、并发封顶。其余状态码见错误码速查页,Key 配置的完整说明见 API 文档。换一个 base URL,你就有了让诊断从猜测变成查表的请求 trace。

相关权威页面

这篇文章归入「LLM API 网关与路由」主题,以下页面作为商业页、配置文档、证据页和信任事实源。

商业主页面Router One API 网关承接统一模型调用、路由、fallback、预算和观测的产品首页。API 文档Router One API 文档OpenAI 兼容端点、CLI 配置和模型调用示例。证据页智能路由方法论路由信号、最终模型与 provider,以及客户侧 trace 的字段边界。对比页OpenRouter 替代方案专业对比全球模型目录与中国友好路由、支付能力的差异。信任页可引用事实表面向搜索爬虫、AI 答案引擎和客户的稳定事实源。数据留存数据留存政策prompt/completion 留存边界和请求元数据政策。网关页面统一 LLM API 网关一个 OpenAI 兼容端点接入整个模型目录,含路由、fallback 与预算控制。路由页面智能模型路由候选排序如何使用延迟、公示成本与可靠性信号。故障转移页面LLM 供应商故障转移什么样的请求才符合在另一条健康供应商路由上重试的条件。可观测页面逐请求 Trace 日志每个请求的最终模型与供应商、Token、延迟、状态与报错。兼容性页面OpenAI 兼容端点沿用 OpenAI SDK,只改 base URL 即可触达各个模型系列。成本追踪页面LLM 成本追踪按 Key、按模型、按请求的花费归因,配合硬性消费上限。转售方页面在 Router One 上搭你自己的 API 服务带消费上限的客户 Key、按 Key 的用量归因,以及明确的「不提供」清单。客户端接入SDK 与客户端配置指南把 OpenAI SDK、LangChain、Vercel AI SDK、Cline、Aider、Dify、Roo Code 指向同一个端点。模型对比模型价格与上下文两两对比每百万 token 单价、上下文窗口与能力,渲染自实时模型目录。

相关阅读