最稳妥做法是解析retry-after响应头并休眠;合规api通常返回x-ratelimit-limit、x-ratelimit-remaining、retry-after等头部字段;遇429时应优先按retry-after休眠,其次查响应体retry_after,最后回退指数退避。

直接等,别硬冲——Rate Limit 触发后最稳妥的做法是解析 Retry-After 响应头并休眠,而不是靠猜或固定延时。
怎么看API是否返回了Rate Limit信息
多数合规API会在响应头里暴露限流状态,重点盯三个字段:X-RateLimit-Limit(总配额)、X-RateLimit-Remaining(剩余额度)、Retry-After(建议重试时间)。有些还带 X-RateLimit-Reset(时间戳,秒级或毫秒级)。用 response.headers.get("X-RateLimit-Remaining") 拿值最直接;如果返回 None,说明该API没按标准返回,得查文档确认格式(比如 GitHub 用 ratelimit-remaining 小写)。
常见错误现象:代码没检查响应头,连续请求直到收到 429 Too Many Requests,再临时处理,已经丢了几轮请求。
- 用
requests发请求后,立刻读response.headers,别等进业务逻辑再查 - 注意大小写和连字符,不同API差异大,比如 Cloudflare 返回
cf-ray,但限流头未必遵循同一风格 - 某些 API(如 Slack)在
429响应体 JSON 里塞retry_after字段,此时响应头可能为空,必须同时解析response.json()
遇到429状态码怎么安全重试
收到 429 不能立即重发,必须尊重服务端给出的冷却时间。优先级顺序是:Retry-After 响应头 > 响应体中的 retry_after 字段 > 回退到指数退避(如 1s → 2s → 4s)。
示例逻辑片段:
import time
import requests
<p>def call_api_with_backoff(url):
for i in range(3): # 最多重试3次
r = requests.get(url)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", "1"))
time.sleep(wait)
continue
elif r.status_code == 200:
return r.json()
raise Exception("API rate limit exceeded after retries")</p>
-
Retry-After值可能是整数秒,也可能是 HTTP-date 格式(如Wed, 21 Oct 2025 07:28:00 GMT),需用email.utils.parsedate_to_datetime()转换 - 不要用
time.sleep(1)硬编码,哪怕文档说“每秒1次”——实际窗口可能是滑动窗口或每分钟配额,硬休眠会浪费额度或触发更严限制 - 重试时建议加 jitter(随机偏移,如
+0.1~0.3s),避免多个客户端同步苏醒造成二次冲击
如何提前预防429,而不是被动等报错
靠“出错了再睡”效率低、体验差。更优策略是在每次请求前预判是否还有额度,结合本地计数 + 时间窗口做节流。
例如用 time.time() 记录上一次请求时间,配合已知的窗口周期(如 60 秒内最多 10 次)做简单滑动计数;或者用 redis 存 INCR + EXPIRE 实现分布式限频。Python 标准库没内置滑动窗口,但 ratelimiter 包或手写一个带 TTL 的字典即可覆盖大部分场景。
- 对单用户调用场景,用
functools.lru_cache缓存结果 + 时间戳判断,比反复请求更省额度 - 并发请求时,
threading.Semaphore或asyncio.Semaphore可控并发数,但无法替代服务端限流逻辑,仅作辅助 - 某些 API(如 Twitter v2)要求在请求头带
X-Client-User或User-Agent才分配独立额度,漏掉会导致共享全局限额
真正难的不是写个 time.sleep,而是搞清你调的这个 API 的限流模型:是固定窗口、滑动窗口、还是漏桶?响应头字段名是否自定义?有没有隐式限流(比如无错误但响应变慢)?这些不查文档、不抓包验证,光靠通用方案容易踩空。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











