Limits by plan
| Plan | Limit |
|---|---|
| Free | No API access (402) |
| Starter | No API access (402) |
| Growth | No API access (402) |
| Agency | 60 requests / minute |
| Scale | 120 requests / minute |
| Enterprise | 300 requests / minute |
How the limit is counted
- Per workspace, not per key. Creating more keys does not raise the limit.
- Per calendar minute. The count resets at the start of every minute (UTC), not on a sliding window.
- Every authenticated request counts, including ones that end in
404or500. Requests refused with401or402are rejected before the limiter and do not count. - The count is atomic in the database, so parallel requests cannot slip past it. With a limit of 60, the 61st request in a minute is refused however many arrive at once.
Response headers
Every response to an authenticated request carries the current state:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed per minute for this workspace. |
X-RateLimit-Remaining | Requests left in the current minute. |
X-RateLimit-Reset | Unix time in seconds when the current minute ends. |
Retry-After | Only on 429: seconds to wait before retrying. |
When you hit the limit
The API answers 429 with the error code rate_limited. Wait the number of seconds in Retry-After, then retry.
async function getWithRetry(url, init, attempts = 3) {
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, init);
if (res.status !== 429) return res;
const wait = Number(res.headers.get("Retry-After") ?? "1");
await new Promise((r) => setTimeout(r, wait * 1000));
}
throw new Error("Still rate limited after retries");
}import time
import requests
def get_with_retry(url, headers, attempts=3):
for _ in range(attempts):
res = requests.get(url, headers=headers, timeout=30)
if res.status_code != 429:
return res
time.sleep(int(res.headers.get("Retry-After", "1")))
raise RuntimeError("Still rate limited after retries")You should rarely get near the limit. The data changes once a week, so a daily sync that lists brands and reads each endpoint once needs a handful of requests per brand. If you need more, cache the responses. They will not change until the next cycle.
