Errors
Every non-2xx response uses one envelope: a machine-readable code, a human message, the request id, and a link back to the docs.
Envelope
json
{
"error": {
"code": "not_found",
"message": "No such endpoint: GET /v1/nope",
"request_id": "8eb9cdc8-65ff-4cdb-af14-ca6221fba79e",
"docs": "https://delilah-api.jsv21b.workers.dev/docs"
}
}Always log request_id — quoting it in a support request lets us find the exact call in our access log. The same id is returned on successful responses in the X-Request-Id header. Rate-limit errors add a few extra fields inside error (limit, window, or used and resets_at).
Error codes
| Code | HTTP | Meaning |
|---|---|---|
bad_request | 400 | A parameter failed validation — a non-integer id, a malformed ISO timestamp, a bad topic slug, or an invalid JSON body |
unauthorized | 401 | Missing X-API-Key header |
invalid_api_key | 401 | Key does not match an issued key |
revoked_api_key | 401 | Key has been revoked |
not_found | 404 | No such record — or no such endpoint |
subscription_inactive | 409 | The webhook subscription you addressed is not active |
rate_limit_exceeded | 429 | Burst limit or monthly cap exceeded — honor Retry-After |
subscription_limit | 429 | The 25 active-webhook ceiling for this key is reached |
internal_error | 500 | Unhandled server error — quote the request_id |
status_unavailable | 503 | /v1/status snapshot temporarily unavailable |
coverage_unavailable | 503 | /v1/coverage snapshot temporarily unavailable |
Notes
- On
429, wait the number of seconds inRetry-Afterbefore retrying. A burst rejection sendsRetry-After: 1; a monthly-cap rejection sends the seconds remaining until the period resets. See Rate limits. - A
404on a sub-resource path means the parent record does not exist. An existing record with no rows for that sub-resource returns200with an emptydataarray. 503applies only to the two snapshot endpoints, which are served from a cached materialised view. Retry in a minute.

