OpenAI 返回 429 意味着你超过了五个上限中的一个——首先要做的是确定哪一个,因为解决方向正好相反。本页面介绍每种限制的衡量方式、如何直接从响应标头读取答案、真正能停止错误的重试逻辑,以及正确退避仍不够时该怎么办。
已根据OpenAI 的速率限制文档验证 2026年9月30日。
五种限制,任何一种都可能触发
| 指标 | 衡量内容 | 通常在何时触发 |
|---|---|---|
| RPM | 每分钟请求数 | 大量小请求——分类、嵌入、智能体循环 |
| TPM | 每分钟 token 数 | 少量大请求——带大量检索上下文的 RAG、长文档 |
| RPD | 每天请求数 | Free 和低 tier;耗尽每日配额的批处理任务 |
| TPD | 每天 token 数 | 相同,但以 token 数量衡量 |
| IPM | 每分钟图像数 | 图像生成工作负载 |
最先耗尽的限制会触发错误,因此“我们离 token 限制还很远”并不能排除速率限制——你可能离 TPM 还很远,却正好达到 RPM。限制按组织和模型应用,而不是按 key:创建额外 key 不会创建额外配额。
429 的表现形式
HTTP/1.1 429 Too Many Requests
retry-after: 12
x-ratelimit-limit-requests: 500
x-ratelimit-remaining-requests: 0
x-ratelimit-reset-requests: 12s
x-ratelimit-limit-tokens: 200000
x-ratelimit-remaining-tokens: 143820
x-ratelimit-reset-tokens: 17s
{
"error": {
"message": "Rate limit reached for gpt-5.4 in organization org-... on requests per min (RPM).",
"type": "requests",
"code": "rate_limit_exceeded"
}
}所需信息都在该响应中。正文会指出维度(“requests per min (RPM)”),标头则提供准确上限、剩余量以及恢复时间。
| 标头 | 含义 |
|---|---|
retry-after | 重试前至少等待的秒数 |
x-ratelimit-limit-requests | 耗尽限制前允许的最大请求数 |
x-ratelimit-remaining-requests | 耗尽前剩余的请求数 |
x-ratelimit-limit-tokens | 允许的最大 token 数 |
x-ratelimit-remaining-tokens | 剩余 token 数 |
x-ratelimit-reset-requests / -reset-tokens | 每个计数器重置前的时间——它们独立重置 |
使用 tier
你的上限由使用 tier 决定;随着累计消费增加,OpenAI 会自动提升 tier:
| 层级 | 资格条件 | 月度使用限额 |
|---|---|---|
| 免费 | 位于允许地区的用户 | $100 / 月 |
| 层级 1 | 已支付 $5 | $100 / 月 |
| 层级 2 | 已支付 $50 | $500 / 月 |
| 层级 3 | 已支付 $100 | $1,000 / 月 |
| 层级 4 | 已支付 $250 | $5,000 / 月 |
| 层级 5 | 已支付 $1,000 | $200,000 / 月 |
此处有意不列出每个模型的 RPM 和 TPM 数值。这些数值因模型而异,会随着模型发布而变化,也可能按账户调整,因此第三方页面发布的任何此类表格都只是带有日期的猜测。与你的账户相关的两个权威来源是 OpenAI 控制面板中的 limits 页面,以及你每次响应中已经收到的 x-ratelimit-* 标头。请读取标头。
解决方法:遵守 Retry-After,然后加入抖动
OpenAI 的文档建议使用带抖动的指数退避;如果响应包含 Retry-After,则应遵循它。两部分都很重要。没有该标头,你可能过早重试;没有抖动,所有在同一时刻失败的客户端都会在同一时刻重试并一起失败——这会形成惊群效应,把糟糕的一秒变成糟糕的一分钟。
import random, time
import openai
client = openai.OpenAI()
def call_with_backoff(fn, *, max_attempts=6, base=0.5, cap=30.0):
"""Retry 429s: honour Retry-After when present, jittered backoff otherwise."""
for attempt in range(max_attempts):
try:
return fn()
except openai.RateLimitError as err:
if attempt == max_attempts - 1:
raise
# The server's own answer beats any formula you invent.
retry_after = (err.response.headers or {}).get("retry-after")
if retry_after:
delay = float(retry_after)
else:
# Full jitter: sleep a random point in [0, 2^n * base], capped.
# Without the randomness every client that failed at the same
# instant retries at the same instant and fails again together.
delay = random.uniform(0, min(cap, base * 2**attempt))
time.sleep(delay)
resp = call_with_backoff(lambda: client.responses.create(
model="gpt-5.4",
input="Summarise this changelog in three bullets.",
))TypeScript 中的相同模式:
import OpenAI from "openai";
const client = new OpenAI();
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
export async function callWithBackoff<T>(
fn: () => Promise<T>,
{ maxAttempts = 6, baseMs = 500, capMs = 30_000 } = {},
): Promise<T> {
for (let attempt = 0; ; attempt++) {
try {
return await fn();
} catch (err) {
const status = (err as { status?: number }).status;
if (status !== 429 || attempt === maxAttempts - 1) throw err;
const retryAfter = (err as { headers?: Headers }).headers?.get("retry-after");
const delay = retryAfter
? Number(retryAfter) * 1000
: Math.random() * Math.min(capMs, baseMs * 2 ** attempt);
await sleep(delay);
}
}
}
const resp = await callWithBackoff(() =>
client.responses.create({ model: "gpt-5.4", input: "Hello" }),
);官方 SDK 已经会为你重试 429,因此大多数应用只有在使用自己的 HTTP 客户端封装调用,或希望采用不同策略时才需要这段逻辑——例如为后台任务设置更长的等待上限,或让面向用户的请求立即失败,因为等待 12 秒比返回错误更糟。
在故障发生前监控
这些计数器存在于每个响应中,而不只是失败响应中。记录剩余值,就能将速率限制从事故转变为仪表盘指标——你可以在上线前数天看到余量逐渐减少,并提前发现即将耗尽的风险。
# Log the remaining counters on every response, not just on failures.
# By the time you see a 429 the useful signal is already an hour old.
resp = client.responses.with_raw_response.create(model="gpt-5.4", input="…")
h = resp.headers
log.info(
"openai_quota model=%s req_left=%s tok_left=%s reset_req=%s reset_tok=%s",
"gpt-5.4",
h.get("x-ratelimit-remaining-requests"),
h.get("x-ratelimit-remaining-tokens"),
h.get("x-ratelimit-reset-requests"),
h.get("x-ratelimit-reset-tokens"),
)
parsed = resp.parse() # the normal response object另外两个值得养成的习惯:与其监控 429 次数,不如在 x-ratelimit-remaining-tokens 降至限制的某个比例以下时发出告警;为 调度 添加抖动,而不只是为重试添加抖动。所有任务都在同一时刻触发的 cron 会 :00自行制造突发流量。
退避策略无能为力的情况
正确的退避策略可以解决突发流量问题。但它无法解决持续需求超过上限的问题——在这种情况下,重试只会把失败推迟。结构性解决方案大致按实施成本排序如下:
- 限制输出。 推理 token 会计入并按输出计费,因此无上限生成是最快耗尽 TPM 的方式。
- 精简检索上下文。 在 TPM 上限下,将检索到的文本块减半,就能在不增加成本的情况下使吞吐量翻倍。
- 选择合适规模的模型。 分类步骤不需要前沿模型,而且小型模型拥有各自独立的预算。
- 分离工作负载。 批处理任务和对延迟敏感的流量争用同一个组织级上限,是此问题最常见的自我诱发形式。
- 提升等级。 等级会随累计消费额提升,因此这件事通常已经在发生。
跨模型系列分散负载
列表中的最后一项正是网关发挥价值的地方。Kunavo 提供兼容 OpenAI 的 API——相同的 SDK、相同的调用格式、一个密钥——覆盖多个模型系列,因此,将工作负载从已饱和的上限迁移出去,只需修改模型字符串,无需进行第二次集成:
from openai import OpenAI
client = OpenAI(
api_key="sk-kn-...",
base_url="https://api.kunavo.com/v1",
)
# Same SDK, same call shape — the model string chooses the family.
client.chat.completions.create(
model="gpt-5-6-terra", # or claude-sonnet-5, claude-haiku-4-5, …
messages=[{"role": "user", "content": "Hello"}],
)具体来说:原本与生产流量竞争资源的批量摘要任务,可以运行在 claude-haiku-4-5,价格为每 1M $0.70 / $3.50;对延迟敏感的路径则继续运行在 gpt-5-6-terra($0.70 / $4.20)或 claude-sonnet-5($1.40 / $7.00)上。不同的系列,不同的队列。
需要明确这能做什么、不能做什么。它消除了“单账户单模型”的瓶颈,并为故障转移提供了目标。但它不会凭空创造容量:如果你的总量确实超过任何一个等级允许的容量,答案仍然是提升等级或减少工作量。所有模型的费率都列在定价页面上,关于 Anthropic 限制的对应操作指南见 Claude API 429 rate_limit_error。
常见问题
OpenAI API 的速率限制是什么?
OpenAI 同时限制五个维度——RPM(每分钟请求数)、TPM(每分钟 token 数)、RPD(每天请求数)、TPD(每天 token 数)和 IPM(每分钟图像数);一旦任何一个维度超限,就会返回 HTTP 429。实际上限取决于你的使用 tier 和具体模型,因此你账户的权威数值应以 OpenAI 控制面板中组织的 limits 页面,以及每个响应中的 x-ratelimit-* 标头为准,而不是任何公开表格。
OpenAI 的使用 tier 有哪些?
截至 2026年9月30日,OpenAI 文档列出六个 tier,每个 tier 都通过累计消费解锁,并设有月度使用限额:Free(在受支持的地区可用,$100/月)、Tier 1(累计支付 $5 后,$100/月)、Tier 2(累计支付 $50 后,$500/月)、Tier 3(累计支付 $100 后,$1,000/月)、Tier 4(累计支付 $250 后,$5,000/月)以及 Tier 5(累计支付 $1,000 后,$200,000/月)。随着消费累积,升级会自动进行。
如何修复 OpenAI 的 429 rate_limit_exceeded?
如果响应包含 Retry-After 标头,请遵守该标头;否则使用带随机抖动的指数退避重试——这是 OpenAI 官方文档的建议。官方 SDK 已经会自动重试;手写 HTTP 客户端必须自行实现。如果正确退避后 429 仍持续出现,说明你确实超出了配额,而不是短时间突发流量;结构性解决方案包括:缩小批次、限制最大输出 token 数、将计划任务分散到一分钟内,或升级到更高层级。
我实际触发的是哪种速率限制?
读取标头。x-ratelimit-remaining-requests 为零表示触发了请求限制;x-ratelimit-remaining-tokens 为零表示触发了 token 限制。两者独立重置——x-ratelimit-reset-requests 和 x-ratelimit-reset-tokens 会告诉你各自何时恢复。消息正文也会指出对应维度。猜测是哪一种会浪费时间,因为解决方法相反:请求限制需要排队,token 限制需要缩短 prompt。
速率限制按 key 还是按组织应用?
按组织和模型应用,而不是按 key。创建额外的 API key 不会增加配额,这就是为什么同一组织中的繁忙生产工作负载和批处理任务会争用同一个上限,也说明隔离它们比增加 key 更重要。
网关能帮助解决 OpenAI 速率限制吗?
如果瓶颈是某个账户的单模型上限,网关会有所帮助,因为它允许你在同一个 key 和同一个 SDK 后面,将任务转移到不同的模型系列——批量摘要任务不必与延迟敏感型流量排在同一个队列中。但网关不会凭空创造容量:如果总量确实超过任何单个 tier 所允许的范围,仍然需要升级 tier 或减少工作量。
为什么我的新账户也会被限速?
Free 和 Tier 1 账户有较高 tier 没有的每日上限(RPD 和 TPD),因此一个适度的测试脚本也可能在一个下午耗尽当天配额。Tier 1 在累计支付 $5 后解锁。
429 会产生费用吗?
不会——被拒绝的请求不会被处理,也不会计费。它造成的成本是延迟,以及你的重试逻辑如何处理这种延迟。
增加 API key 会提高吞吐量吗?
不会。限制按组织和模型应用。额外的 key 可用于归因和撤销,但不会增加容量。
我应该捕获 429,还是让 SDK 处理?
常见情况交给 SDK 处理;如果默认行为不适合你,再自行捕获:例如面向用户的请求应快速失败,或者后台任务可以承受比默认值长得多的等待上限。
如果 429 实际上是配额耗尽呢?
耗尽月度使用限额同样会表现为 429,而再多退避也无法清除它——消息正文会区分这两种情况。如果标头中的计数器状态正常,但请求仍被拒绝,请先检查计费,不要急着修改重试代码。