Rate limits
A per-minute allowance per tenant, refilling continuously.
| Plan | Requests per minute | Roughly |
|---|---|---|
| Free | 120 | 2/second |
| Build | 600 | 10/second |
| Scale | 3,000 | 50/second |
| Archive | 12,000 | 200/second |
The limit is per tenant, not per key. Issuing a second key does not buy a second allowance — keys exist for revocation and attribution.
Reading the headers
x-ratelimit-limit: 600
x-ratelimit-remaining: 597
x-ratelimit-reset: 60 reset is 60 on every response, and that is not a bug. The bucket refills continuously
rather than emptying and resetting at a wall-clock boundary, so there is no instant to count down to — the useful
figure is the refill period. Do not build a countdown out of it.
When you hit it
HTTP/1.1 429 Too Many Requests
retry-after: 1
x-ratelimit-limit: 600
x-ratelimit-remaining: 0 A correct client
async function call(url: string, key: string): Promise<Response> {
for (let attempt = 0; ; attempt++) {
const response = await fetch(url, { headers: { "x-api-key": key } });
if (response.status !== 429) return response;
// Two different conditions share this status. The quota one is not
// transient, and retrying it just burns the rest of your day.
if (response.headers.get("x-quota-remaining") === "0") {
throw new Error("monthly quota exhausted; upgrade or wait for the reset");
}
if (attempt >= 5) return response;
const wait = Number(response.headers.get("retry-after") ?? 1);
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
The x-quota-remaining check is the part people leave out. Both conditions are 429, and
only one of them is worth waiting for. Quotas and overage covers the other.
Staying under it
- Ask for bigger pages.
limit=100instead oflimit=10is one tenth of the requests for the same results. - Use /stories. One request answers what several searches plus client-side deduplication otherwise would.
- Serialise, do not fan out. Twenty concurrent requests hit a per-minute cap in the first second. A small concurrency limit with a queue behind it beats a burst plus retries.
- Cache what does not change. An article fetched by id is immutable; there is no reason to fetch it twice.
How it is enforced
The API runs several replicas behind a load balancer, and the bucket is shared across them, so the published figure is the figure — not the figure multiplied by however many replicas happen to be running. If the shared counter is ever unavailable, the limit degrades to being enforced per replica (looser, never stricter) rather than failing requests, and that condition is monitored rather than silent.