Skip to main content

Error response shape

All non-2xx responses share this envelope:
Branch on error.code only. error.message is human-readable English and may evolve. details is structured supplementary info — fields vary per code. request_id is also returned as the X-Request-Id response header. Include it in any support request.

HTTP → code matrix

Per-code troubleshooting

Telling the two 429s apart

429 rate_limited covers two conditions with opposite remedies. Neither sends a Retry-After header, so you must discriminate on the body:
Do not write a blanket “429 → exponential backoff” handler. It is correct for one of these and an infinite loop for the other.

Test webhook throttle

POST /v1/eagle-eye/watches/{watch_id}/test-webhook is limited to 5 fires per minute per watch, counted over a rolling 60-second window. Exceeding it returns:
No details object, and no Retry-After. The window is rolling, so capacity returns gradually as older fires age out — waiting a full minute after the last accepted fire is always sufficient. This is the only throttle enforced on /v1; the fair-use targets in Authentication are not enforced server-side.

Trial budget exhausted

Sandbox keys minted from a trial (self-serve) account carry a lifetime call budget — 1,000 calls by default. When it runs out, every subsequent request returns 429:
This is not a throttle, and backing off will not clear it. The budget is a lifetime total, not a per-minute or per-month window — it does not refill on its own. A client that treats 429 as “retry with exponential backoff” will retry forever. error.details.limit being present is what distinguishes this from the test-webhook throttle, which does clear.
The API sends no Retry-After header on this response, because there is no time after which the request would succeed.

How to resolve it

Rotating an exhausted key does not reset its budget. Rotation deliberately carries over both the budget and the calls already used, so the new key is exhausted the moment it’s created — otherwise rotation would be an unlimited free-budget loop. To get a fresh budget you must create a new key, which is a different button.

Staying ahead of it

Budgeted keys carry the remaining count on every response, including successful ones:
X-Quota-Remaining is computed after the current request is counted. Log it, or alert when it drops below a threshold, rather than waiting for the first 429. Keys with no budget — every production key, plus any sandbox key whose budget has been lifted — send no quota headers at all. Absence means unlimited, not zero; a client that parses a missing header as 0 will lock itself out of a perfectly healthy production key.