Errors & rate limits
How the API reports failures and throttling, and what to do about each.
Audience: developers handling error responses. What you will accomplish: parse error bodies and implement correct backoff.
The error shape (problem+json)
All errors share one shape — application/problem+json (RFC 9457):
{
"type": "https://errors.chat-bot/validation",
"title": "Validation failed",
"status": 422,
"detail": "Question cannot be empty",
"correlation_id": "…",
"errors": [{ "field": "q", "message": "..." }]
}errors[] is present on validation failures and names the offending field. 5xx bodies
never include internal details — quote the correlation_id instead.
Status codes
| Status | Meaning | What to do |
|---|---|---|
| 400 | Bad request (e.g. invalid X-User-Id, blocked input) | Fix the input. |
| 401 | Missing/invalid X-API-Key (ingest) | Provide a valid key. |
| 404 | Unknown doc_id | Check the id. |
| 422 | Validation error | See errors[] for the offending field. |
| 429 | Rate limited | Back off (see below). |
| 500 | Server error | Retry later; quote correlation_id. 5xx bodies never include internal details. |
Rate limits
The default limit is 60 requests/minute per client IP. Every response includes:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Total requests allowed in the window. |
X-RateLimit-Remaining | Requests left in the current window. |
X-RateLimit-Reset | Window reset time (epoch seconds). |
On a 429, you also get a Retry-After header (seconds).
Backoff pseudocode
on response:
if status == 429:
wait(seconds = Retry-After)
retry
else if X-RateLimit-Remaining is low:
slow down upcoming requestsVerify your result
- Verify: Your client reads
statusand, on422, theerrors[]array. - Verify: On
429you wait exactlyRetry-Afterseconds before retrying. - Verify: On
5xxyou log and quote thecorrelation_idrather than expecting detail in the body.
Common mistakes and fixes
- Retrying a
429immediately → respectRetry-After; a tight retry loop stays throttled. - Expecting detail in a
5xxbody → there is none by design; use thecorrelation_id. - Behind a proxy and getting throttled globally → the operator must configure
TRUSTED_PROXIESso your real client IP is used, not the proxy’s.
Related next steps
- See which headers to log in API summary.
- Map specific symptoms to fixes in Troubleshooting.