Using the APIRate limits

Using the API

Rate limits

Per-key request limits, Free fair-use limits, email and AI limits, the rate-limit headers, and how to back off when you get 429.

Rate limits keep the service fast and fair for everyone, and stop a runaway script or a stolen key from doing much harm. Most integrations never reach them. When you do, the API tells you exactly how long to wait.

All limits are settings that we can tune without a release, so the numbers on this page are the starting values planned for launch (proposed). Read the headers on each response rather than hard-coding a number.

The limits

Per API key or MCP token

Each API key and each MCP token has its own budget of requests per minute. The planned starting value is 600 requests per minute per key. A key can also be given its own lower limit, which is useful for a script you want to keep gentle.

Because the budget is per key, giving each integration its own key also gives each its own budget, and a busy integration cannot slow down another.

Per account: fair use on Free

The Free plan includes every feature. To stop abuse, Free accounts have monthly fair-use limits on:

LimitFree (proposed)Team (proposed)
API calls per month10,000100,000
File storage1 GB10 GB
Sends per minute1010

You can see your usage against these limits in Settings > Usage, or with Get usage. A warning flag is set at 80%. Passing a monthly limit returns plan_limit_reached. See Fair-use limits.

Sending email

The platform mailer has its own limits: a per-minute limit (planned at 10 per minute per account) and the monthly quota of platform emails, 20 on Free and 100 on Team. When the quota is used up, sends return quota_exceeded. Your own SMTP server is not limited by the monthly quota. See Platform email.

AI agent

The AI invoice agent is limited to about 10 requests per minute per account (proposed), on top of credits and a monthly ceiling per account. When credits run out you get ai_credits_exhausted.

Sign-in

Password sign-in is limited per IP address and locks an account for a short time after repeated failures (planned: 5 failures, then a 15-minute lock). Magic-link and password-reset requests are also limited.

Public routes

Routes that need no account are limited per IP address (proposed):

RouteLimit
No-auth Generate a PDF30 per hour per IP
Token pages: pay page, verification, estimate, portal60 per minute per IP
"I have paid" with a proof upload5 per hour per token

Rate-limit headers

Responses include three headers (proposed names, from the IETF draft for rate-limit headers):

HeaderMeaning
RateLimit-LimitThe size of the current budget
RateLimit-RemainingHow many requests are left in the current window
RateLimit-ResetSeconds until the window resets
Response headers
HTTP/1.1 200 OK
RateLimit-Limit: 600
RateLimit-Remaining: 41
RateLimit-Reset: 17
X-Request-Id: req_0193f2a2c0e5

Use RateLimit-Remaining to slow down before you hit the limit.

When you are over the limit

You get HTTP 429 with the code rate_limited and a Retry-After header in seconds:

429 response
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Try again in 17 seconds.",
    "request_id": "req_0193f2a2c0e6"
  }
}

Nothing was done for a rejected request, so it is always safe to retry it. Wait at least Retry-After seconds, then send the same request with the same Idempotency-Key.

async function call(url, options, attempt = 0) {
  const response = await fetch(url, options);
  if (response.status !== 429 || attempt >= 5) return response;
  const wait = Number(response.headers.get("Retry-After") ?? 1);
  await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  return call(url, options, attempt + 1); // same body, same Idempotency-Key
}

Staying under the limits

  • Use one key per integration. Each key has its own per-minute budget.
  • Page with limit=200 for bulk reads. Fewer, larger pages use fewer requests. See Pagination and filters.
  • Use webhooks instead of polling. Subscribe to webhook events and fetch only what changed.
  • Use updated_after to sync only records that changed since your last run.
  • Queue writes in your own system and send them at a steady pace rather than in bursts.
  • Do not retry in a tight loop. Always honour Retry-After, and add backoff for other temporary errors.