Claude API 报错 429 怎么办?4 种方案实测,最后一种改一行代码就搞定

5 阅读7分钟

上周赶一个 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 就是限流,按优先级:

  1. 先加指数退避重试——5 分钟搞定,基本功
  2. 评估是否需要提升配额——适合长期项目,但要等
  3. 高并发场景上请求队列——重但稳,适合自建基础设施
  4. 嫌麻烦就用聚合平台——改一行 base_url,把限流问题甩给平台方

我现在的做法是方案四 + 方案一:聚合平台解决主要问题,指数退避兜底极端情况。跑了两周,429 再也没出现过。

有问题评论区聊。