Rate limits
The Open API is throttled per token:
| Value | |
|---|---|
| Limit | 60 requests |
| Window | 60 seconds |
| Counted by | Token (not IP, not company) |
| On exceeding | HTTP 429 |
The window is fixed: the counter starts on your first request and resets 60 seconds later. Burning all 60 requests in one second and then waiting 59 seconds is possible — spreading requests evenly works better.
1. The 429 response
{
"message": "Rate limit exceeded. Max 60 requests per minute.",
"accept": false,
"errors": null,
"data": null
}
:::note No rate-limit headers yet
X-RateLimit-Remaining and Retry-After are not returned today. Track your own usage, or
treat 429 as the signal to back off.
:::
2. Staying within the budget
2.1 Ask for bigger pages
More rows per request means fewer requests:
{ "page": 1, "size": 100, "orders": { "ID": "DESC" } }
2,700 rows is 27 requests at size: 100 — but 180 requests at size: 15.
:::warning The maximum is per endpoint, not a global 100
size: 100 is a 400 on the endpoints that cap lower — leads and employees stop at 50, lead
tasks at 30. Use the per-endpoint table in
Formats & conventions § 3.2 and ask for that
endpoint's maximum rather than a hard-coded 100.
:::
2.2 Sync incrementally
Fetch what changed since your last sync instead of everything, every time. That is exactly what the timestamp range filters are for:
{
"page": 1,
"size": 100,
"registeredTimestampFrom": 1766131200,
"orders": { "REGISTERED_DATE": "ASC" }
}
2.3 Prefer webhooks over polling
Asking "anything new?" every minute burns the budget for nothing. Webhooks push the event to you; call the Open API only when you need additional detail.
2.4 Retry 429 properly
- Use exponential backoff with jitter: 1s → 2s → 4s → 8s, plus a random 0–500 ms.
- A tight retry loop only fills the window further.
- Cap concurrency when bulk-loading — 2–4 in-flight requests per token is usually plenty.
async function withRetry(fn, max = 5) {
for (let attempt = 0; attempt < max; attempt++) {
const res = await fn();
if (res.status !== 429) return res;
const wait = Math.min(2 ** attempt * 1000, 30_000) + Math.random() * 500;
await new Promise((r) => setTimeout(r, wait));
}
throw new Error("Rate limited: out of retries");
}
2.5 Don't shard across tokens
Even though the limit is per token, minting several tokens for one integration to spread the load is treated as deliberate circumvention. If your workload genuinely needs more headroom, talk to us.
3. Other limits
| Limit | Value |
|---|---|
Filter size maximum | 30–100, per endpoint (table) |
| Authorization code lifetime | 10 minutes, single use |
| PAT lifetime | up to 365 days |
| OAuth access token lifetime | 1 hour (expires_in) |
| OAuth refresh token lifetime | 30 days |
Async request status (requestId) retention | 24 hours, readable only by the token that created it |
Notes per POST /v1/open-api/lead-note/{leadId}/list call | 1–10 |
| Webhooks: delivery attempts | initial + 3 retries (30s → 5m → 30m) |
| Webhooks: in-flight deliveries per company | 20 |
4. FAQ
Is the limit per company? No — per token. If several of your servers share one token, they share the budget.
Does a rejected request still count?
Yes — a 429 increments the counter too, so never retry immediately, and a 400 or 403 spends
budget like any other call. The one exception is a request the platform cannot attribute to a
token: an invalid or expired token is rejected with 401 before the counter is touched.
Is the counter per token value or per connection? Per token value. Rotating a PAT or refreshing an OAuth access token starts a fresh counter under the new value — which is a side effect, not a strategy (see 2.5).
Can I see how many requests are left? Not today — the response carries no rate-limit headers.
Can the limit be raised? If your load reflects a real need, get in touch. Try the optimizations in 2.1–2.3 first; they usually solve it.