Skip to content

Rate limits

Request ceilings, and how to back off cleanly when you hit one.

On this page

Limits are per endpoint and per identity, and more than one can apply to a single request. Exceeding any of them returns 429 with RATE_LIMITED and a Retry-After header.

"Per identity" means per user, on the credential you authenticated with. Two people on the same team have separate budgets; two tokens belonging to the same person share one.

Generation

EndpointLimit
Create generation10 per minute and 100 per hour
Preview cost60 per minute
Get run status60 per minute, and one poll per 2 seconds for any single request_id

Both create limits apply at once, so a burst of 10 is fine but sustaining it is not. The per-request status limit is separate from the per-minute one: polling one run every second trips it even when you are well inside 60 per minute. Honor retry_after_seconds from the create response before your first poll, and prefer a webhook over polling in production.

Tools

EndpointLimit
List tools, Get a tool120 per minute

Tool tags

EndpointLimit
List tool tags, Get a tool tag120 per minute

Library

EndpointLimit
List generations, Get a generation60 per minute
Delete a generation30 per minute

Boards

EndpointLimit
Reads: list, get60 per minute
Writes: create, update, delete, access, and all node edits30 per minute

Reads and writes are counted separately, so listing boards does not consume the budget for editing them.

Uploads

EndpointLimit
List uploads, Get an upload, Delete an upload60 per minute
Create an upload100 per hour

Identity

EndpointLimit
Get the current user60 per minute

/me does more work than its size suggests: it reads your profile and every account you belong to. Call it when a session starts or an account changes, not before every request.

Authentication

EndpointLimit
Start the device flow5 per minute, per IP
Complete the device flow10 per minute
Poll the device flowNo fixed ceiling. Poll at the interval the start response gave you. Polling faster returns SLOW_DOWN, and the fix is to double your interval rather than to retry.
Refresh a token20 per minute, per IP
Device management60 per minute

The two per-IP limits are the only ones not counted per identity, because at that point in the flow there is no identity yet.

Reporting

WindowMaximum requests
Per minute20
Per hour120

Both apply simultaneously, so 20 requests in the first minute of an hour is fine, but sustaining that rate is not. These are counted per company rather than per user, because a Company API Access Token names a company.

Reporting also runs one request at a time per company. A second request that starts while the first is still running is rejected with RATE_LIMITED, so run your exports in sequence rather than in parallel.

At the maximum page size of 100,000 rows, the hourly limit still covers 12 million rows per hour. If you are approaching that, request larger pages rather than more of them.

Handling a 429

Back off exponentially, and add jitter so that a fleet of clients retrying together does not synchronize into a second spike.

async function withRetry(request, { attempts = 5 } = {}) {
  for (let attempt = 0; attempt < attempts; attempt++) {
    const response = await request();
    if (response.status !== 429) return response;

    // Exponential backoff with jitter: 1s, 2s, 4s, 8s, plus up to 1s of noise.
    const base = 2 ** attempt * 1000;
    const wait = base + Math.random() * 1000;
    await new Promise(resolve => setTimeout(resolve, wait));
  }

  throw new Error('Still rate limited after retrying.');
}

Staying well under the limits

  • Request larger pages. One request for 50,000 rows costs the same against your budget as one for 50.
  • Page forward, do not re-query. Following next_cursor costs one request per page. Re-running the same window from the start to pick up new rows costs the whole export again.
  • Spread scheduled jobs. A nightly export that starts exactly on the hour competes with everything else that does the same.

View as Markdown