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:

Client API bucket
40 requests / minute, burst up to 40.
Tokens refill at ~1.5/sec. This bucket is currently shared across all client traffic, so plan for the ceiling rather than treating it as exclusively yours.
Upstream ceiling
60 requests / minute, burst up to 60.
A global cap protecting the FieldInsight API itself. In practice the 40/min bucket is the binding constraint for client calls.

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 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.