The error envelope
typenames the kind of error (see the table below). Branch on it and oncode;messageis meant for people.detailsis there when the error is about something specific: each has a machine-readablecode, usually thefieldor parameter it is about, and amessage. Some codes add more:usageandlimit,current_revision, or therecord_idthat already has a value.request_idis also in theX-Request-Idheader of every response, errors or not. Log it, and include it when you ask for support.
Statuses and types
- 400: the request is malformed, such as JSON that doesn’t parse, a sort or cursor the API doesn’t know, or a body key the operation doesn’t take (
unknown_parameter, naming the key). Fix the request: retrying it unchanged fails the same way. - 401: the API key is missing, invalid, revoked or expired.
- 402: the change would go over a hard limit of your organization’s plan, such as its number of projects, collections, records, members or webhooks, or, on Free, its storage. The detail names the limit in
field, with the currentusageand thelimit. A file larger than the plan allows (10 MB on Free, 100 MB on paid plans) is refused when its upload starts: thefieldisbyte_size,usageis the file’s size andlimitthe largest upload. - 403: the key may see the resource but not do this, for example when it lacks a scope (the message names it; there is no detail code) or is published-only. An
expandthat embeds what the key can’t read isnot_permitted. - 404: the resource doesn’t exist, or the key can’t see it, such as another project’s. The two look the same on purpose.
- 409: the request conflicts with the resource’s state. A deleted resource must be restored first (
deleted); a record has one draft at most (draft_exists), and a live record with a pending draft can’t be published until the draft is merged (draft_pending); a collection is being migrated (migration_in_progress). Retrying helps only once the state changes;in_progressis the exception (see below). - 422: the values don’t pass validation. For a record, each detail’s
fieldis the field’s key indata; unknown keys indataareunknown_field, and a value another record already has in a unique field istaken, with that record’srecord_id. - 500 is an unexpected failure in the API, and 503 means a service such as search is briefly unavailable. Retry both with backoff.
Retry safely with Idempotency-Key
A request can fail after the API has done the work, when the response is lost on the way back. Send anIdempotency-Key header on any POST, PUT, PATCH or DELETE, and you can retry it without doing it twice:
- The key is 1 to 255 printable ASCII characters; a UUID is a good choice. Keys are scoped to the API key that sends them.
- The first response below
500is stored for 24 hours, validation errors included. A retry with the same key and the same request (method, path, query string and body bytes) gets that response again, with its originalETagandLocationheaders andIdempotent-Replayed: true. Nothing runs a second time. - The same key with a different request is refused:
400, detail codereused. Send the same bytes when you retry, and use a new key for a new request. - A retry while the first request is still running gets
409, detail codein_progress. Wait and retry. A request that hasn’t finished after 5 minutes is treated as abandoned, and the key can be used again. - A
5xxresponse doesn’t keep the key, so retrying runs the request again.
Rate limits
Every authenticated response carries the current window:
An API key’s limit is per minute and depends on its organization’s plan (the pricing page lists it). A published-only key can ship inside an app people run, so it is counted per client IP address: one user can’t use up everyone else’s. Requests with an invalid key are limited per IP address too.
Over the limit, the API answers
429 rate_limited with Retry-After, in seconds. The request was neither performed nor counted, so wait that long and send it again (with the same Idempotency-Key, for a write).
A
429 with a plan_limit detail is different: on Free, the organization has used the month’s API requests, and Retry-After runs to the start of the next month. See API requests.What the SDKs retry
The SDKs retry connection errors,429, 500, 502, 503, 504 and in_progress for you (Retries and idempotency has the rules). What reaches your code is raised as its type’s class. Change the number of retries when you create the client:
Concurrent edits
Without a check, the last write wins: your update can overwrite a change someone made after you read the record. Both writes are kept in its history, but the record shows the last. To prevent that, use the record’s revision. Record and draft responses carry the revision as anETag, such as "7". Send it back as If-Match when you change the record:
412:
If-Match works on record writes (a create with upsert_on, which may update, and update, delete, restore, publish, unpublish, revert) and on a draft’s update, discard and merge, where the revision is the draft’s. In the SDKs, pass the record you read as ifMatch (if_match): see Concurrent edits.