Error shapes
There are three shapes. Read the status first, then the body. 1.detail is a string (most errors).
detail is an object with a stable code (match on code, not on the message).
code and message at the top level, with no detail. These come from the rate limiter, the unknown-route handler and the catch-all for unexpected errors.
detail, one entry per bad field:
loc says where: ["body", "reference"] for a JSON body, ["query", "environment"] or ["path", "key"]. For a multipart form field the first item is body as well. Handle all three shapes: a client that assumes detail is always an object will fail on the first 422.
Request id
Every response carriesX-Request-ID. If you send your own (1 to 64 characters of A-Z a-z 0-9 . _ : -) Sahl returns it; otherwise Sahl creates one. Each key call is listed in Developers, Call log in the console under that id. Quote it when you write to Sahl.
Catalogue
400 Bad request
401 Unauthorized
403 Forbidden
404 Not found
413 Payload too large
422 Unprocessable
A bad
kind is not an error: unknown values count as individual.
429 Too many requests
Every successful response also carries
X-RateLimit-Limit and X-RateLimit-Remaining. The limit is per client IP, not per key, so several servers behind one address share it. 100 a minute is the default in the code and can change.
500 and 502
A reader that cannot be reached is not an error:
/extract answers 200 with reader_unavailable: true. See Read documents.
Retries
The API has no idempotency key. Think about each call before you retry it.
Rules of thumb:
- Never retry a 4xx, except a 429 for
rate_limit_exceeded, which hasRetry-After. A 4xx will fail the same way. - Retry a 5xx and a network timeout with exponential backoff and a cap, for example 2 s, 4 s, 8 s, then stop.
- Set a client timeout well above the usual answer time. The examples use 120 seconds for
/extract. - After a timeout on
/extractyou do not know whether the read ran. Check Documents for thereferencebefore you resend, or accept the duplicate.