Errors
Errors use HTTP status codes and a consistent JSON body, so a client can branch on the code and show the message.
Error body
422 Unprocessable Entity
json
{
"error": {
"code": "validation_failed",
"message": "Product is invalid",
"details": {
"fieldErrors": {
"sku": "SKU may only contain letters, digits, '.', '_' and '-' (no spaces or slashes) and must start with a letter or digit",
"attributes.volume_ml": "Must be a whole number",
"translations.ar.name": "Name must be at most 500 characters"
}
},
"requestId": "req_7Qm2cX5fGa1b9KpZ"
}
}codeis stable and machine-readable;messageis human-readable and may change.details.fieldErrorsmaps dotted field paths (images.0.url,prices.SAR,attributes.color) to messages. Root-level problems use_root.requestId(also theX-Request-Idheader) identifies the request in our logs — include it when you contact support.
Status codes
| Status | Code | When |
|---|---|---|
400 | bad_request | The request could not be understood. |
401 | unauthorized | No API key, or the key is invalid, expired or revoked. |
403 | forbidden | The key lacks a scope the endpoint needs (the message names it). |
404 | not_found | The record does not exist — or belongs to another workspace. |
409 | conflict | Duplicate SKU or code, a stale expectedVersion, or a paused channel. |
409 | idempotency_in_progress | A request with the same Idempotency-Key is still running. |
412 | precondition_failed | If-Match did not match the product's current version. |
413 | payload_too_large | The body is larger than the endpoint accepts. |
422 | validation_failed | The input is invalid; see details.fieldErrors. |
422 | idempotency_key_reused | The Idempotency-Key was already used for a different request. |
429 | rate_limited | Too many requests; wait Retry-After seconds. |
500 | internal_error | Something failed on our side. Retry later; quote the requestId. |
Partial success
POST /products/bulk never fails as a whole because of bad items: it answers 200 with one result per item, and failed items carry their own error object with the same code, message and fieldErrors. Background jobs (imports, exports, publishing) report row errors on the job (errors, errorCount) instead of in the HTTP response.
Retrying
- Retry 429 after the Retry-After delay, and 500/502/503/504 with exponential backoff.
- Do not retry 4xx errors other than 409 idempotency_in_progress and 429 — fix the request instead.
- Make retried POSTs safe with an
Idempotency-Keyheader, or use naturally idempotent calls (PUT by SKU, bulk upserts).