> https://router.one/zh/blog/llm-api-429-rate-limit-fix 的 Markdown 镜像，供 AI 助手与爬虫使用。Router One 是 OpenAI 兼容的统一 LLM API 网关。
> 发布：2026-08-02 · 修订：2026-09-19 · 作者：Router One Team

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

_429 状态码背后是三种不同故障：上游限流、网关对 Key 或账户的限额、账单额度耗尽。讲清判别方法、生产级退避重试写法，以及从源头减少 429 的并发整形与网关故障转移。_

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

## 一个状态码，三种故障

| 实际发生了什么 | 限制在哪一层 | 修法 |
| --- | --- | --- |
| 上游供应商限流（RPM/TPM 超限） | 模型厂商的基础设施 | 有上限的指数退避 + 抖动；有 `Retry-After` 就照做 |
| 网关对 Key 或账户的限额 | Key 的 `rateLimit` / `tokenLimitTpm`，或账户共享的限额，由 `X-RateLimit-Scope` 指明 | 分散或隔离负载，或申请调高限额——退避只能让你在限额以下排队 |
| OpenAI 的 `insufficient_quota` | 官方 OpenAI API 的账单账户 | 解决账单问题；重试毫无意义 |

误判方向的代价是双向的。把网关限额当作会自行消散的上游问题，你会写出一套对着永远不动的天花板反复冲撞的重试逻辑；把 `insufficient_quota` 当瞬时限流，你会对着一个没有余额的账户白白重试几个小时。

## 成因一：上游供应商在限流

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

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

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

```python
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="openai/gpt-5.5",
    messages=[{"role": "user", "content": "ping"}],
)
```

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

## 成因二：撞上了网关对 Key 或账户的限额

每把 Router One API Key 都有每分钟请求数限额（`rateLimit`）和每分钟 token 限额（`tokenLimitTpm`），同一账户的所有 Key 还共享账户级的请求与 token 限额，其中包括单个模型的请求限额。这些限额按平台默认值生效——正常付费使用不设低额度固定上限——作用是不让泄漏的 key 或失控的循环把整个账户的吞吐打满。它们和 `maxSpend` 不同：消费硬上限由你在 Dashboard → API Keys 里按 Key 设置，而吞吐限额不在控制台修改。[每客户一把 key 的转售模式](https://router.one/zh/blog/resell-llm-api-spend-capped-keys)依靠的是按 Key 的消费上限，吞吐限额在它下面兜底。

识别特征：响应会点名是哪一种限额。网关返回的 429 带 `X-RateLimit-Scope`——`api_key` 是这把 Key 的限额，`subject` 是整个账户的限额，`subject_model` 是账户在单个模型上的限额——同时带 `X-RateLimit-Limit`、`X-RateLimit-Remaining`、`X-RateLimit-Reset` 和 `Retry-After`；错误码为 `RATE_LIMIT_EXCEEDED`（请求数）或 `TOKEN_QUOTA_EXCEEDED`（每分钟 token 数）。这类 429 是*确定性的*：不管几点，都在同一个频率上复现。每分钟 token 限额在请求开始时按估算值（提示词长度加 `max_tokens`）计入，结算后再按实际用量修正，所以一批大提示词可能在实际用量达到之前先触到它。（如果 key 停摆时报的是 402，那是 `maxSpend` 上限或钱包余额的问题——钱的事，不是速度的事，[速查页](https://router.one/zh/llm-api-error-codes)写了两者的区别。）

申请调高之前先问一句：为什么会撞上？如果是某个后台任务把生产流量共用的 key 打满了，更好的修法是再开一把 key，把吵闹的负载隔离出去、单独可见——但同一账户的 Key 仍共享账户级限额。负载确实需要更高吞吐时——压测、批量回填、中转站的汇总流量——Key 与账户限额都可以申请调高：发邮件到 support@router.one，写明账户 ID、Key、模型，以及预计峰值的每分钟请求数与 token 数。网关限额之上仍受上游容量约束。

## 成因三：`insufficient_quota`——套着 429 外衣的账单问题

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

这个报错来自 OpenAI 平台，不是网关。[insufficient_quota 修复指南](https://router.one/zh/openai-insufficient-quota)完整讲了成因和解决路径，包括绑了支付方式但额度依然为零的情况。

## 怎么判断撞上的是哪一种

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

1. **先读错误体。** `insufficient_quota` 会自报家门。看到这个字符串就是账单问题——走成因三。
2. **再读 `X-RateLimit-Scope`。** `api_key`、`subject` 或 `subject_model` 就是成因二——网关对 Key 或账户的限额，`X-RateLimit-Limit` 告诉你天花板在哪；`upstream_provider` 指向成因一。
3. **最后看模式。** 上游限流（成因一）是突发的、和负载高峰相关——429 集中在特定时间窗，然后自行消散。网关限额则是平直、可预测的。

同一份每请求记录还能回答后续问题——那段故障窗口的重试白烧了多少 token——这就进入[成本追踪](https://router.one/zh/llm-cost-tracking)的范畴了。

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

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

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

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

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

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

## 常见问题

**怎么判断 429 来自我的 Key 限额还是上游供应商？**
读 X-RateLimit-Scope 响应头。api_key、subject 或 subject_model 表示网关对这把 Key、整个账户或账户在单个模型上的限额，错误码为 RATE_LIMIT_EXCEEDED 或 TOKEN_QUOTA_EXCEEDED；upstream_provider 表示模型厂商的限制。上游 429 是突发的、跟负载高峰相关、会自行消散；网关限额则在同一频率上确定性地复现。Dashboard → Logs 里能看到每个请求的状态和时间。

**每个 429 都应该重试吗？**
不。只有限制真的会重置的情况——上游限流——才值得用有上限的指数退避加抖动去重试。网关限额每分钟重置，按 Retry-After 节奏重试可以排队通过，但持续高于限额的流量会一直失败，直到负载降下来或限额调高；OpenAI 的 insufficient_quota 是账单额度耗尽，任何重试节奏都填不回余额。

**Router One 会对我的请求加自己的限流吗？**
正常付费用量没有低固定上限，但每把 Key 和每个账户都按默认的每分钟请求数与 token 数限流，上游供应商约束也仍会生效。这些限额不在控制台修改，可以发邮件到 support@router.one 申请调高，写明账户 ID、Key 和预计峰值流量。

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

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

## 排查清单

429 出现时：先读错误体（`insufficient_quota` 等于账单问题），再读 X-RateLimit-Scope（网关对 Key 或账户的限额——分散负载，或申请调高），最后才当作真正的上游限流处理——有上限的退避加抖动、尊重 `Retry-After`、并发封顶。其余状态码见[错误码速查页](https://router.one/zh/llm-api-error-codes)，Key 与响应头的完整说明见 [API 文档](https://router.one/zh/docs)。换一个 base URL，你就有了让诊断从猜测变成查表的请求 Trace。

## 相关页面

- 本页规范地址：https://router.one/zh/blog/llm-api-429-rate-limit-fix
- LLM API 网关与路由：https://router.one/zh/llm-api-gateway
- 全部博客文章：https://router.one/zh/blog
- 模型与每模型 token 价格：https://router.one/zh/models （markdown：https://router.one/zh/models.md ）
- 定价：https://router.one/zh/pricing
- API 文档（markdown）：https://router.one/zh/docs.md
- 公司事实（markdown）：https://router.one/zh/facts/company.md
