问答

社媒数据接口有什么频率限制?遇到 429 怎么处理?

简短回答

在 okdata 上,每个 key 可以先连发一小批,之后按每秒固定的数量恢复;账户信息和每次返回的响应头都会告诉你还剩多少。超了会返回 429,从不扣费,并告诉你要等几秒,等一下重发就行。大任务按恢复速度来排:1 万页 B站评论不管一小时跑完还是一天跑完,都是 $20.00。

自己爬平台时,429 是针对你账号的警告:用过 twscrape 的人都见过 Ban detected: 429, (88) Rate limit exceeded 这句。数据接口返回的 429 只说明一件事:你发得比 key 允许的快。不会封号,不会扣费,返回里还告诉你什么时候可以再试。

限制怎么运作

每个 key 有一个令牌桶:一次能连发的调用数(突发量),以及每秒稳定恢复的数量。

curl -s "https://api.okdata.online/v1/account" -H "Authorization: Bearer $OKDATA_API_KEY" | jq '.data.rate_limit'
# {"tier": "...", "burst": ..., "per_second": ...}

带 key 的每次返回还有这些响应头:

响应头 含义
X-RateLimit-Limit 突发上限
X-RateLimit-Remaining 桶里现在还剩几次
X-RateLimit-Reset 再过几秒桶会重新装满
Retry-After 仅 429 时出现:要等几秒

429 的返回体就是标准错误格式:code: "rate_limited",charged: false,retry: "yes",以及 retry_after_seconds。

先控节奏,别硬扛 429

import os, time, requests

H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
limit = requests.get("https://api.okdata.online/v1/account", headers=H, timeout=30).json()["data"]["rate_limit"]
gap = 1 / limit["per_second"]                     # 桶能长期维持的节奏

def get(path, **params):
    while True:
        r = requests.get(f"https://api.okdata.online{path}", headers=H, params=params, timeout=60)
        body = r.json()
        if body["code"] != "rate_limited":
            time.sleep(gap)
            return body
        time.sleep(body["retry_after_seconds"])     # 不扣费,等一下重发

for bv in ["BV1xx411c7mD"]:
    page = get("/v1/bilibili/videos/comments/list", id=bv, sort="hot")

并发的 worker 数不要超过突发上限;如果 X-RateLimit-Remaining 总是掉到 0,就再放慢一点。

大任务按时间排,不只按钱排

恢复速度是每秒 r 次的话,N 次调用大约要 N ÷ r 秒。费用和快慢无关:B站视频评论每页 $0.002,B站视频搜索每次 $0.002,微博搜索每次 $0.002,一口气发完还是分散到一整晚,价格都一样。如果截止时间需要的吞吐量超过你的 key 允许的范围,可以发邮件给 hello@okdata.online 问更高的档位。

容易踩的坑

  • 限制按 key 计算。两个脚本共用一个 key,就是共用一个桶。
  • 第一次请求还在执行时,用同一个 Idempotency-Key 重试,也会返回 rate_limited,等待 1 秒。
  • 上游平台本身也有限制。数据源满载时返回 upstream_busy,同样不扣费,退避的时间要更长一些。

每个返回码和对应的重试建议,见错误码。

更新于