Skip to content

Rate limits

A per-plan per-workspace budget, a per-IP flood guard, the rate-limit headers on every response, stricter export and crawl caps, and a sane backoff loop.

Available on every plan

v1 enforces two rate limits. They are independent, and either can reject a request.

The per-IP limit runs first, deliberately: it bounds floods of missing or invalid keys, which would otherwise reach a database lookup unthrottled. It sits at the highest plan tier (300/min), so it never caps a Business integration calling from one server IP below that workspace's own budget. The per-workspace limit is what bounds one valid key sprayed across many machines.

The per-workspace budget is per plan

Your workspace's per-minute budget follows its plan. A resold (reseller client) workspace carries its own client plan's tier.

So five servers sharing one Pro key are capped at 60 requests per minute in total; upgrading the plan raises the budget.

Stricter per-endpoint limits

A few endpoints carry their own limit on top of the global two, because each request is expensive:

All of them return the same 429 as the global limits.

The response

HTTP status 429. The per-workspace limit carries subcode: "rate_limited"; see Errors.

Rate-limit headers

Every response that got far enough to resolve a key — a success, a 429, or the access-gate 403 — carries three headers:

A 429 also carries Retry-After — the number of seconds to wait. On the per-workspace limit it is the time left in the window; on the per-IP flood guard (which rejects before a key resolves, so it reports no budget headers) it is the full window length, 60.

Prefer these headers to guessing: read Retry-After on a 429, and watch X-RateLimit-Remaining to slow down before you hit the wall.

Backing off

Retry with exponential backoff and jitter, and cap the number of attempts.

with-backoff.mjs

Two things to avoid:

  • Retrying immediately. The window is a minute; an instant retry just spends another request.
  • Retrying anything else. UNAUTHORIZED and FORBIDDEN will not resolve themselves. See Errors.

Staying under the limit

  • Use the maximum limit of 200 when paging, so a given dataset costs fewer requests. See Pagination.
  • Sync on a schedule rather than polling in a loop. Nothing in v1 changes fast enough to reward a tight poll.
  • For "tell me when something happens", use webhooks instead of polling — they push, and they cost you no requests.

Where to go next

Last updated