Rate limits
Each API key has a request budget, and every authenticated response tells you where you stand in the RateLimit headers.
Each API key has a request budget, 600 requests per minute with bursts up to 60 by default. It works
like a bucket of 60 tokens that refills at the per-minute rate: every request takes a token, and an empty
bucket means a 429. Every authenticated response tells you where you stand:
| Header | Meaning |
|---|---|
RateLimit-Limit |
Requests per minute allowed for this key. |
RateLimit-Remaining |
Tokens left in the bucket: how many requests you can send right now, at most the burst size. |
RateLimit-Reset |
Seconds until the bucket is full again. |
With an empty bucket, you get 429 rate_limited with a Retry-After
header. Separately, a workspace can only have a limited number of lookups in flight at once, and the whole
service has a ceiling too. Past either one you get 429 concurrency_limited.
Neither is charged.
Need a higher limit? Write to us. Limits can be raised per key.
Retrying well#
Retry only when error.retryable is true, wait at least Retry-After, and back off exponentially:
async function withRetry(send, tries = 5) {
for (let i = 0; ; i++) {
const res = await send();
if (res.ok) return res.json();
const body = await res.json();
if (!body.error?.retryable || i === tries - 1) throw Object.assign(new Error(body.error.message), body);
const wait = Number(res.headers.get("retry-after") ?? 2 ** i);
await new Promise((r) => setTimeout(r, wait * 1000));
}
}import time
def with_retry(send, tries=5):
for i in range(tries):
res = send()
if res.ok:
return res.json()
err = res.json()["error"]
if not err["retryable"] or i == tries - 1:
raise RuntimeError(f'{err["code"]}: {err["message"]}')
time.sleep(float(res.headers.get("Retry-After", 2 ** i)))Pair retries with an Idempotency-Key so a retry is never charged twice.