Skip to content
unzoi docs

Rate limits

A per-minute allowance per tenant, refilling continuously.

PlanRequests per minuteRoughly
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=100 instead of limit=10 is 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.