Getting started
Rate limits
What the gateway allows, what counts against it, and how to stay well under the cap.
The budget
Every request to /v1/* debits two token buckets that refill continuously:
What happens when you’re over
The gateway will hold your request for up to 2 seconds waiting for a token. If a token frees up in that window, your request runs normally. If not, you get:
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
{
"type": "https://gateway/errors/rate-limited",
"title": "Rate-limit budget for purpose 'tenant' exhausted",
"status": 429,
"instance": "req_01HX…"
}The response does not include a Retry-After header. Retry with an exponential backoff and jitter — starting around 1 second is a good default.
Using the API efficiently
A few habits will keep you well under the cap:
- Cache lookup results. Customers, sites, projects, job types, statuses, and the custom-field map change rarely — fetch each one once at startup (or on a daily refresh) and reuse the IDs across all subsequent calls. A naive integration that re-fetches
/v1/sitesbefore every job-create call will blow the budget within seconds. - Filter, don’t scan. When polling for changes, pass
updated_at_fromso each call returns only what’s new since your last poll. Without that you’ll re-pull the same jobs every time. - Paginate sensibly. Larger
page_sizevalues mean fewer round trips. The maximum is 100; sticking to the default of 25 is only sensible for low-volume tenants. - Prefer webhooks over polling. If you only care about status changes, subscribe via webhooks rather than polling
/v1/jobson a tight loop. Polling at 1 Hz consumes 60 requests/minute on its own — your entire budget. - Space out bulk writes.If you need to create 200 jobs from a batch import, pace the calls (e.g. one every ~1.5 sec to match refill) rather than firing them all at once. Bursts can briefly waste capacity on the 2-second wait queue.
- Handle 429 once, not in a tight loop. A single retry after 1–2 seconds (with a small random jitter) covers transient spikes. Looping faster than that just keeps the bucket drained.
A worked example
Importing 500 jobs nightly: cache /v1/sites, /v1/job-types, and /v1/custom-fields once at the start of the run (3 requests), then create jobs at ~30/minute (one every ~2 seconds). The whole run finishes in under 20 minutes and leaves comfortable headroom in the bucket for everything else.