Answers
What rate limits does a social data API have, and how should I handle 429 errors?
Short answer
On okdata each key can send a short burst of calls and then a steady number per second; your account details and the headers on every response show where you stand. Going over returns a 429 that is never charged and says how long to wait, so the fix is to wait and resend. Pace large jobs to the steady rate: 10,000 Bilibili comment pages cost $20.00 whether they run in an hour or a day.
When you scrape a platform yourself, a 429 is a warning about your account: twscrape users know the message Ban detected: 429, (88) Rate limit exceeded. From a data API, a 429 only means you are sending faster than your key allows. Nothing gets banned, nothing is charged, and the response tells you when to try again.
How the limit works
Each key has a token bucket: a burst of calls you can make at once, refilled at a steady rate per second.
curl -s "https://api.okdata.online/v1/account" -H "Authorization: Bearer $OKDATA_API_KEY" | jq '.data.rate_limit'
# {"tier": "...", "burst": ..., "per_second": ...}
Every keyed response also carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
burst size |
X-RateLimit-Remaining |
calls left in the bucket right now |
X-RateLimit-Reset |
seconds until the bucket is full again |
Retry-After |
on a 429 only: seconds to wait |
The 429 body is the standard error: code: "rate_limited", charged: false, retry: "yes" and retry_after_seconds.
Pace the job instead of fighting 429s
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"] # steady pace the bucket can sustain
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"]) # free; just wait and resend
for bv in ["BV1xx411c7mD"]:
page = get("/v1/bilibili/videos/comments/list", id=bv, sort="hot")
Run several workers only up to the burst size, and lower the pace if X-RateLimit-Remaining keeps hitting zero.
Plan big jobs by time, not just money
At a refill rate of r calls a second, N calls take about N ÷ r seconds. The cost does not change with speed: Bilibili video comments is $0.002 a page, Bilibili video search $0.002, and Weibo post search $0.002, whether you send them in a burst or spread over a night. If a deadline needs more throughput than your key allows, ask hello@okdata.online about a higher tier.
Gotchas
- Limits are per key. Two scripts sharing one key share one bucket.
- A retry with the same
Idempotency-Keywhile the first attempt is still running also returnsrate_limited, with a one-second wait. - Platform-side limits still exist upstream. When a source is saturated you get
upstream_busy, also free; back off longer.
The errors page lists every code with its retry advice.
Updated