Rate limits
How many requests you may make, how to see where you stand and how to back off gracefully.
Limits
| Limit | Amount | Description | Window (seconds) |
|---|---|---|---|
key_minute | 120 | requests per minute for this key | 60 |
company_minute | 300 | requests per minute for this company | 60 |
company_writes | 60 | write actions per minute for this company | 60 |
company_heavy | 10 | heavy actions per minute for this company | 60 |
company_day | 50.000 | requests per day for this company | 86400 |
auth_failures | 20 | failed sign-ins per minute from this IP address | 60 |
The per-company limit applies to all keys of your company together. Write actions (POST, PATCH, DELETE) also count towards the write limit. The windows are fixed: a per-minute limit starts again at every whole minute, the daily limit at midnight UTC. A refused request counts as well. Need more on a structural basis? Contact support.
Heavy actions have their own, tighter limit: mailing a quote or invoice, fetching a PDF, sending a message in the inbox, sending a review request, sending a customer a message about an appointment, and sending a test message to a webhook endpoint.
There are also a few limits that have nothing to do with the pace of your requests, but that you still see returned as 429 rate_limited:
- at most 200 uploads (attachments and work-order photos) per company per day, as
429 rate_limited; - at most 20 messages per hour in the same conversation, as
429 rate_limited; - at most 250 outgoing messages and mails to customers per company per hour (inbox, quote and
invoice email together) and no more than 20 sends of the same document per 24 hours. In the inbox
this comes back as
429 rate_limited; when mailing a quote or invoice as403 forbidden.
Headers
Every response tells you where you stand:
| Header | Meaning |
|---|---|
RateLimit-Policy |
All limits that apply to this request |
RateLimit |
Remaining requests and seconds until the reset, for the tightest limit |
X-RateLimit-Limit |
Size of the tightest limit |
X-RateLimit-Remaining |
Still available in the current window |
X-RateLimit-Reset |
Unix time at which the window starts again |
On a 429
A 429 rate_limited has a Retry-After header with the number of seconds you have to wait. The limit field in the response names the limit you hit.
async function request(url, options, attempt = 0) {
const response = await fetch(url, options);
if (response.status === 429 && attempt < 5) {
const seconds = Number(response.headers.get('Retry-After') ?? 1);
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
return request(url, options, attempt + 1);
}
return response;
}Tip
Spread large synchronisations over time and use limit=100, so you fetch more with fewer requests.
Last updated on September 21, 2026