Every error has one shape, whatever went wrong. Branch on code, never on message: codes are added over time but never renamed or repurposed, and messages are written for people and may change.
details lists the offending fields when a request fails validation, each with its field, a message and a machine-readable type. It is null otherwise.request_id is also in the X-Request-Id header of every response. Quote it in any support request.{
"error": {
"code": "INVALID_REQUEST",
"message": "One or more parameters are invalid.",
"details": [
{ "field": "body.lines", "message": "List should have at most 200 items after validation, not 201", "type": "too_long" }
],
"request_id": "req_4f1c8a90b7e24d3f9c0a5b6e7d8f9012"
}
}INVALID_REQUEST400The request is malformed: a missing or invalid parameter, an unknown field in the body, an id in the body that is not in your account, a sort field the operation does not accept, a line list outside its limits, or search combined with a filter it would override.
Read details, fix the request and send it again.
UNAUTHENTICATED401No credentials: the Authorization header is missing or is not Bearer <key>.
Send the key as a bearer token.
INVALID_API_KEY401A key was sent and rejected: it is wrong, revoked, past its rotation grace period, or its nsk_live_ or nsk_test_ prefix does not match its account.
Check which key is deployed and its prefix. The message is deliberately the same for every cause.
NOT_ENTITLED403The account's plan does not include the API: it is not a paid Enterprise plan, or the plan has lapsed. Test keys follow the plan of the account that owns them.
The account owner needs an active paid Enterprise plan. On a plan without the API, keys can still be listed and revoked meanwhile.
INSUFFICIENT_SCOPE403The key does not carry the scope the operation needs. Each operation in the reference shows its scope.
Add the scope to the key in Settings, Developer, or use a key that has it.
READ_ONLY403The request tries to change a shared reference row, one marked is_global, which every account can read but none can change.
Create your own row and use it instead.
NOT_FOUND404The id in the path is not in your account. The API does not distinguish a record that does not exist from one that belongs to someone else.
Check the id, and that the key is for the account you expect.
CONFLICT409The request conflicts with the current state: a duplicate code, a status change the document's state does not allow, a record still in use elsewhere, or an Idempotency-Key reused with a different body.
Read the message, fetch the current state and decide what to do. Sending the same request again will fail the same way.
UNPROCESSABLE422The request was understood but cannot be applied. Rare in v1: most such refusals are INVALID_REQUEST or CONFLICT.
Read the message and change the request.
RATE_LIMITED429The account went over its 300 requests per minute, or one IP address sent too many requests that failed authentication.
Wait the number of seconds in Retry-After, then retry.
INTERNAL_ERROR500Something went wrong on our side, including a request whose database work ran past the time limit. The failure is recorded against its request_id.
Retry reads with backoff. Before retrying a write, check whether it landed. If it persists, contact support with the request_id.