Errors

One envelope for every failure, so you can branch on a code instead of parsing prose.

The envelope

Any non-2xx response
{
  "error": {
    "type": "invalid_cursor",
    "message": "`cursor` is not a cursor this endpoint issued. Omit it to start from the first page.",
    "docs": "https://developer.edmdb.net/docs"
  }
}

Branch on `type`, never on `message`

type is a stable contract and changes only with a version bump. message is written for a human reading a log and will be reworded whenever a clearer wording exists.

Codes

StatustypeWhat happened
400invalid_parameterA query or path parameter failed validation. The message names the parameter.
400invalid_cursorThe cursor is malformed, or came from a different endpoint. Drop it to start over.
401missing_api_keyNo key was sent.
401invalid_api_keyThe key is unknown, revoked or expired.
404not_foundNo such record, or no such endpoint.
429rate_limit_exceededToo many requests for this key. See rate limits.
429quota_exceededYour account's monthly allowance is spent. Upgrade or wait for the reset.
500internal_errorOur fault. Safe to retry with backoff; if it persists, tell us.

Handling them

Treat 4xx other than 429 as terminal — retrying an invalid_api_key will not fix it. Retry 429 and 500 with exponential backoff and jitter. See rate limits for the throttling specifics.

Empty is not an error

A list with no matches returns 200 with an empty data array, not a 404. The same goes for sub-resources: an event with no lineup on record returns an empty array, because "we have no lineup for this" and "this event does not exist" are different answers and you may want to act on them differently.