/api/v1, over a rolling one minute window.
They are counted differently, and the difference matters to how you scale.
There are 68 resource families, each with its own family bucket. Products, orders,
customers, inventory, pricing, discounts, webhooks and so on each count separately.
What “per API key” means for you
Your family budget follows the credential, not the machine. Running the same key from ten workers does not give you ten times the budget: all ten share one counter. This is deliberate, and it cuts both ways in your favour:- You get the budget you were told, on every host, and it does not change when you add or move servers.
- Another company behind the same NAT, cloud egress range or corporate proxy cannot consume your budget, and you cannot consume theirs.
The global ceiling is still counted per IP address, because it applies before we know
which key you are using. It exists to bound unauthenticated traffic, and it is set
well above the family budget so a normal authenticated caller never meets it first.
Why both limits exist
The per-family budget stops one workload starving another. A catalogue sync hammering products cannot throttle an unrelated integration that only reads reviews. The global ceiling bounds the total you can extract, which per-family limits alone cannot do. Without it a caller simply multiplies their budget by the number of families, and 68 families at 600 each is 40,800 requests a minute.The global ceiling is deliberately higher than the per-family figure. If they were
equal the outer limit would always bind first and the per-family buckets would never
do anything.
Budget headers
Every response, not only a refusal, tells you where you stand:RateLimit-Reset is delta seconds, not a unix timestamp. Sleep for that many
seconds. You do not need your clock to agree with ours.
When the two limiters stack, the headers describe whichever one is binding: the
smaller remaining wins. So RateLimit-Remaining: 3 means three more requests, whether
that came from your family budget or the global ceiling. All three fields move
together, so a response never describes half of one limiter and half of another.
Being refused
Over budget returns429 with the standard envelope, plus Retry-After in whole
seconds:
Retry-After is rounded up and never below 1. A Retry-After: 0 would tell a client
to retry immediately into a refusal it is guaranteed to hit again, turning a rate limit
into a hot loop against the thing it was meant to protect.
Backing off correctly
1
Honour Retry-After
Sleep for exactly the seconds given. Do not retry sooner, and do not use a fixed
one second sleep, which will simply be refused again.
2
Add jitter
If you run several workers, add a random fraction of a second so they do not all
wake at the same instant and re-trigger the limit together.
3
Watch RateLimit-Remaining and slow down before you are refused
Throttling yourself at a low remaining is cheaper than being refused and retrying.
A sync loop that pauses at
RateLimit-Remaining: 5 never sees a 429 at all.4
Page with a larger limit
?limit=100 fetches five times as many records per request as the default 20, for
the same one request against your budget. Most rate limit problems on a bulk read
are really a paging size problem.Failed authentication is limited separately
Repeated bad credentials are counted in their own bucket, independent of your request budget. A key that has been revoked or mistyped will start being refused for rate limiting rather than authentication if you retry it in a loop. Fix the credential rather than retrying it.A 429 does not consume your idempotency key
If a write is refused for rate limiting, theIdempotency-Key you sent is released
rather than spent. Retry the same operation with the same key once your window
resets. See Idempotency.