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 several token buckets that refill continuously. The one that binds your integration is your per-key bucket — size your traffic against it:

Your key
20 requests / minute, burst up to 20.
Each key has its own bucket that refills one token roughly every 3 seconds (~0.33/sec). This is the binding limit for your integration — plan for ~20 requests/minute sustained.
Shared pools
40/min across all client keys · 60/min upstream.
Above your per-key bucket sit two shared caps: an aggregate client pool (40/min) and a global ceiling protecting FieldInsight itself (60/min). You won’t normally hit these before your own 20/min bucket, but heavy traffic from other keys can occasionally make a request wait.

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/sites before every job-create call will blow the budget within seconds.
  • Filter, don’t scan. When polling for changes, pass updated_at_from so 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_size values 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/jobs on a tight loop. Polling at 1 Hz is 60 requests/minute — triple your 20/min budget, so most calls would just be rejected.
  • Space out bulk writes.If you need to create 200 jobs from a batch import, pace the calls (e.g. one every ~3 sec to match your per-key refill) rather than firing them all at once. Bursts beyond your 20-token allowance just queue against the 2-second wait window and then 429.
  • 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 ~20/minute (one every ~3 seconds) to stay within your per-key bucket. The whole run finishes in about 25 minutes with no 429s.