Authentication & errors

Send your key on every request: Authorization: Bearer ak_YOUR_KEY. Every successful metered response carries X-Credits-Remaining — your live balance after that request.

Status codes

CodeMeaningWhat to do
400Invalid input — e.g. a non-public target URL (private ranges, localhost, cloud metadata are always refused) or a force_tier/max_tier your key cannot use. No credit is charged for a 400.Fix the request.
401invalid or missing API key. Check the header; rotate your key from the dashboard if leaked.
422Request body failed validation (missing/mistyped field).Fix the field named in detail.
429Three distinct app-level cases, told apart by detail: rate limit exceeded (per-key rps cap; includes Retry-After: 1), insufficient credits (balance too low; includes X-Credits-Remaining), or search credit cap reached (your plan's monthly search sub-cap; general credits may still remain). Back off, or upgrade / wait for the monthly reset.
429 (no JSON)A fourth case, from the edge: the nginx per-IP burst limiter can also return 429 ahead of the app, with an HTML body — no JSON detail, no Retry-After. Any 429 response that fails to parse as JSON is this case, not one of the three above. Treat any 429 without a JSON body as "back off briefly" and retry.
503A dependency (key store, search provider) is unavailable. Nothing was charged unless a body was served. Retry with backoff.

No 402: this API does not use HTTP 402 — running out of credits is a 429 with detail: "insufficient credits", so one retry-after-backoff code path covers both throttling and balance exhaustion.

Response envelopes, not exceptions

/v1/fetch and /v1/extract return HTTP 200 with a structured envelope even when the target could not be retrieved — check blocked and error in the body. You decide raise-vs-retry; we report honestly what happened per attempt.

Honest capacity notes