上周赶一个 AI 客服的 Demo,Claude Opus 4.6 效果确实不错,结果跑到一半控制台疯狂刷 429 Too Many Requests,整个服务直接瘫了。我盯着报错信息坐了半小时,心态差点崩——Demo 明天就要给老板看,限流了我能怎么办?
直接回答:Claude API 报错 429,是你的请求触发了 Anthropic 的速率限制。解决方向有四个:客户端指数退避重试、申请提升官方配额、本地请求队列削峰、换聚合 API 平台绕开单一供应商的限流天花板。 四种我都试了,下面把完整踩坑过程和代码贴出来。
先说结论
| 方案 | 实现难度 | 见效速度 | 适合场景 | 我的评价 |
|---|---|---|---|---|
| 指数退避重试 | ⭐ 低 | 即时 | 偶发 429,流量不大 | 治标不治本 |
| 申请提升配额 | ⭐ 低 | 1-3 天 | 长期稳定的高流量项目 | 得等,急不来 |
| 本地请求队列 | ⭐⭐⭐ 高 | 即时 | 自建服务,想精细控制 | 重,但稳 |
| 聚合 API 平台 | ⭐ 低 | 即时 | 想省事、多模型切换 | 我最终的选择 |
为什么会报 429?
很多人看到 429 就慌,其实 Anthropic 的限流策略是公开的。2026 年的速率限制分三个维度:
- RPM(Requests Per Minute):每分钟请求数
- TPM(Tokens Per Minute):每分钟 Token 吞吐量
- TPD(Tokens Per Day):每天 Token 总量
不同 Tier 的限制差异很大。免费试用 Tier 1 的 RPM 才 50,Tier 4 可以到 4000。大部分人踩坑都是因为还在 Tier 1,根本扛不住并发。
429 响应头里会带 retry-after 字段,告诉你多少秒后可以重试——这个信息很多人直接忽略了。
graph TD
A[你的请求] --> B{Anthropic 网关}
B -->|RPM 未超| C[正常响应 200]
B -->|RPM 超限| D[返回 429]
D --> E{retry-after 字段}
E -->|读取等待时间| F[等待后重试]
E -->|忽略直接重试| G[继续 429 ❌]
F --> B
方案一:指数退避重试(基础,必须有)
不管你最终用哪种方案,指数退避都应该作为兜底逻辑写进代码。
import time
import anthropic
client = anthropic.Anthropic(api_key="your-api-key")
def call_claude_with_retry(prompt: str, max_retries: int = 5):
"""带指数退避的 Claude API 调用"""
for attempt in range(max_retries):
try:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
except anthropic.RateLimitError as e:
if attempt == max_retries - 1:
raise # 最后一次直接抛出
# 优先读 retry-after,没有就用指数退避
retry_after = getattr(e, 'response', None)
if retry_after and hasattr(retry_after, 'headers'):
wait_time = int(retry_after.headers.get('retry-after', 2 ** attempt))
else:
wait_time = 2 ** attempt # 1, 2, 4, 8, 16 秒
# 加随机抖动,避免多个客户端同时重试(惊群效应)
import random
wait_time += random.uniform(0, 1)
print(f"[429] 第 {attempt + 1} 次重试,等待 {wait_time:.1f}s...")
time.sleep(wait_time)
return None
# 测试
result = call_claude_with_retry("用一句话解释什么是 Rate Limiting")
print(result)
偶发的 429 基本能扛住。但如果你是持续高并发——比如我那个客服 Demo,QPS 稳定在 20+——退避重试只会让延迟越来越高,用户体验直线下降。
踩坑:一开始没加随机抖动,5 个并发线程同时退避、同时重试,造成更大的瞬时峰值,429 反而更频繁。加了 random.uniform(0, 1) 之后才好。
方案二:申请提升官方配额
没什么技术含量,但很多人不知道可以申请。
操作路径:Anthropic Console → Settings → Limits → Request Increase
需要填:使用场景描述、预期月消费金额、预期 RPM/TPM 需求。
提交后等了 2 天才批下来,从 Tier 1 升到 Tier 2,RPM 从 50 提到 1000,基本够用。但有两个问题:第一等不起(我 Demo 明天就要),第二 Tier 升级跟消费金额挂钩,新账号充得少别指望一步到位。
方案三:本地请求队列削峰
在应用层做一个令牌桶或滑动窗口来控制发送速率,让请求排队而不是一股脑怼上去。
import asyncio
import time
from collections import deque
from openai import AsyncOpenAI
class RateLimitedClient:
"""滑动窗口限流器 + Claude API 客户端"""
def __init__(self, api_key: str, base_url: str, rpm: int = 45):
self.client = AsyncOpenAI(api_key=api_key, base_url=base_url)
self.rpm = rpm
self.window = deque() # 记录每次请求的时间戳
self.lock = asyncio.Lock()
async def _wait_for_slot(self):
"""等待可用的请求槽位"""
async with self.lock:
now = time.monotonic()
# 清理 60 秒之前的记录
while self.window and self.window[0] < now - 60:
self.window.popleft()
if len(self.window) >= self.rpm:
# 等待最早的请求过期
sleep_time = 60 - (now - self.window[0]) + 0.1
print(f"[限流] 队列已满,等待 {sleep_time:.1f}s")
await asyncio.sleep(sleep_time)
self.window.append(time.monotonic())
async def chat(self, prompt: str, model: str = "claude-sonnet-4-20250514"):
await self._wait_for_slot()
response = await self.client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=512
)
return response.choices[0].message.content
# 使用示例
async def main():
client = RateLimitedClient(
api_key="your-key",
base_url="https://api.anthropic.com/v1",
rpm=45 # 留 10% 余量,官方限制 50 就设 45
)
# 模拟 100 个并发请求
tasks = [client.chat(f"请回答:{i} + {i} = ?") for i in range(100)]
results = await asyncio.gather(*tasks)
print(f"完成 {len(results)} 个请求")
asyncio.run(main())
429 完全消失了。代价是排队后延迟飙升——100 个请求在 RPM=45 的限制下,最后一个要等 2 分多钟才能发出去。实时对话场景下这个体验不能接受。
踩坑:一开始用的 threading.Lock,在 asyncio 环境下死锁了。异步代码必须用 asyncio.Lock。写到凌晨 3 点脑子不转,犯了这种低级错误。
方案四:换聚合 API 平台(最终方案)
折腾了前面三种,我发现核心矛盾在于:单一供应商的速率限制是硬天花板,客户端怎么优化都是在天花板下面腾挪。
后来同事推荐我试了 ofox.ai,一个 AI 模型聚合平台,一个 API Key 可以调用 Claude Opus 4.6、GPT-5、Gemini 3、DeepSeek V3 等 50+ 模型,后端做了多供应商冗余(Azure、Bedrock、VertexAI 等),请求分散到多个供应商的配额池里,单点限流的问题自然缓解了。
改动量极小,换个 base_url 就行:
from openai import OpenAI
# 之前:直连 Anthropic
# client = OpenAI(api_key="your-anthropic-key", base_url="https://api.anthropic.com/v1")
# 现在:走聚合接口
client = OpenAI(
api_key="your-ofox-key",
base_url="https://api.ofox.ai/v1" # 兼容 OpenAI 协议
)
response = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[
{"role": "system", "content": "你是一个客服助手"},
{"role": "user", "content": "我的订单什么时候发货?"}
],
max_tokens=1024,
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
跑了一下午压测,QPS 20 的情况下没再出现一次 429。延迟大概 300ms,比直连 Anthropic 官方还快一点,可能跟路由优化有关。支持支付宝,按量计费,不用提前囤大额度。
方案一的指数退避我还是留在代码里了——不管用什么平台,兜底重试不能省。
几个坑顺手记一下
坑 1:429 和 529 傻傻分不清
Anthropic 除了标准的 429(你请求太多),还有个 529(Anthropic 自己过载了)。529 不是你的问题。处理策略不同:429 退避重试,529 建议直接降级到备用模型。
坑 2:Streaming 模式下 429 的表现不一样
普通请求 429 直接返回错误码,streaming 模式有时候连接已经建立了,中途才断开,SDK 抛的异常类型不一样。我一开始只 catch 了 RateLimitError,漏掉了 APIConnectionError。
坑 3:并发测试时本地端口耗尽
压测开了 200 个并发连接,本地端口不够用了(macOS 默认临时端口范围比较小)。这个跟 429 无关,但报错信息容易跟网络超时混淆,排查时浪费了不少时间。
小结
Claude API 429 就是限流,按优先级:
- 先加指数退避重试——5 分钟搞定,基本功
- 评估是否需要提升配额——适合长期项目,但要等
- 高并发场景上请求队列——重但稳,适合自建基础设施
- 嫌麻烦就用聚合平台——改一行 base_url,把限流问题甩给平台方
我现在的做法是方案四 + 方案一:聚合平台解决主要问题,指数退避兜底极端情况。跑了两周,429 再也没出现过。
有问题评论区聊。