Rate limits and throughput
What the API tells you about your limits, and how to size a job.
The headers
Every response carries the current limit state.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The size of your burst allowance. |
X-RateLimit-Remaining | How much of it is left right now. |
X-RateLimit-Reset | Milliseconds until enough allowance has returned for the request that was refused. 0 when nothing is being refused. |
Retry-After | On a 429, how long to wait before trying again. |
Read these rather than hardcoding a rate. Limits are set per account and can be raised, so a number you compile in today will be wrong the day your plan changes.
When you get a 429, wait for Retry-After and try again. RATE_LIMITED and
QUOTA_EXCEEDED are not billed, so backing off correctly costs you nothing.
The limit is a burst allowance, not a fixed window
Your allowance refills continuously rather than resetting on a clock. Every request takes one unit, and units come back at a steady rate, so the allowance is full again shortly after you stop.
The practical effect is that X-RateLimit-Remaining only falls when you send
requests faster than they refill. Spacing calls a second or two apart will show
a full allowance on every response, because it refilled between them. Sending
twenty-five at once visibly draws it down.
This is what you want for this kind of API. A burst of parallel calls is exactly how you page through a feed or fan out across a list of profiles, and the allowance is sized for that rather than forcing you to trickle requests.
Sizing a job
Latency, not rate limiting, is usually what governs throughput. These endpoints read live pages, so a call takes seconds.
Measured across every endpoint on its documented example request:
| Time | |
|---|---|
| Fastest | 0.7s |
| Median | 3.7s |
| 90th percentile | 12.3s |
| 95th percentile | 21.7s |
| Slowest | 29.8s |
Sixteen endpoints answer in under two seconds, and seven take more than twenty. Each endpoint page shows its own measured time.
The practical consequence is that concurrency, not request rate, sets your throughput. Thirty concurrent workers at a four second median gives you roughly 450 calls a minute. Getting there with one worker in a loop would take four hours.
Running a large job
Set a client timeout of at least 120 seconds. A timeout shorter than the work turns a slow success into a failure, and you pay for the call either way.
Run calls concurrently with a bounded pool. A fixed pool of workers pulling from a queue gives you predictable throughput and a single place to add backoff.
Make the unit of work a single call, and record request_id against it. When
something needs investigating later, that id is what we search on.
Checkpoint your cursors. If a long paging job dies at page 40, you want to resume rather than restart, which matters most on the endpoints where paging is the expensive part.