Public API v2: Errors & retries
Branch on errors.code; message is written for people and may change.
| HTTP | Code | Cause | Retry? |
|---|---|---|---|
| 400 | INVALID_BODY |
The body is not valid JSON. | No |
| 400 | DOMAIN_MISSING |
No X-Shop-Domain. |
No |
| 401 | ACCESS_KEY_MISSING |
No X-API-Key. |
No |
| 401 | ACCESS_KEY_INVALID |
Unknown key, a key of another store, a v1 key, or an unknown domain. | No |
| 401 | ACCESS_KEY_REVOKED |
The key was revoked, or the store moved to a plan without Public API. | No |
| 403 | AUTHORIZATION_DENIED |
The key lacks a scope (details names it), the request comes from an IP outside the key’s allowed IPs (details names the IP), or the plan does not include the module. |
No |
| 404 | RESOURCE_NOT_FOUND |
No such path, or the id does not exist, was deleted or belongs to another store. | No |
| 409 | RESOURCE_CONFLICT |
Another bulk request or upload of the store is running; a request with the same Idempotency-Key is still running; a price list is being updated by the app. |
Yes, after a few seconds |
| 413 | PAYLOAD_TOO_LARGE |
Body over 10 MB. | No — split it |
| 422 | VALIDATION_ERROR |
A field is wrong (details lists each one), the request costs more than 600 points, or the store would pass 10,000 rules of the module. |
No — fix the request |
| 429 | RATE_LIMIT_EXCEEDED |
Out of points, or more than 10 requests of the store at once. | Yes, after Retry-After |
| 500 | INTERNAL_ERROR |
Unexpected error on our side. | Yes, with backoff |
| 502 | UPSTREAM_UNAVAILABLE |
Shopify did not answer, or did not save during an upload. | Yes, with backoff |
Validation details
Section titled “Validation details”A 422 lists every wrong field in details: field is the path in the request (rules.0.discount_value, form.7.options), message says what is wrong, and code tells the kind of problem:
| details[].code | Meaning |
|---|---|
invalid_type |
Wrong type, or a required field is missing. |
invalid_value |
Not one of the allowed values of an enum. |
invalid_format |
Wrong format: a date without Z, an id that is not numeric, a decimal with 3 places. |
too_small / too_big |
Under or over a limit: length, number, number of items. |
unrecognized_keys |
A field the endpoint does not take; field is root or the object that holds it. |
custom |
A business rule, explained in message — e.g. a percentage over 100, overlapping ranges, a duplicate code. |
A cursor that is not valid 422
{ "success": false, "status": "error", "errors": { "code": "VALIDATION_ERROR", "message": "Validation failed", "details": [ { "field": "cursor", "message": "Invalid cursor; pass next_cursor or prev_cursor as received" } ] }, "meta": { "timestamp": "2026-10-21T09:34:53.200Z", "version": "v2", "request_id": "4b9ec3f9-eb99-4044-80c1-8ae53d918b15", "rate_limit": { "cost": 10, "limit": 600, "remaining": 590, "restore_rate": 10 } }}Retrying
Section titled “Retrying”- Retry only the codes marked yes. Fix the request for the others.
- On
429, wait the seconds inRetry-After, then send the same request again. - On
500and502, retry with exponential backoff (1 s, 2 s, 4 s, 8 s …) and give up after about 5 tries. - Add an
Idempotency-KeytoPOSTrequests you may retry, so a retry never creates a rule twice. See Bulk & idempotency. PUTandDELETEare safe to retry as they are.- In a bulk response, retry only the elements whose
resultiserrorwith a retryable code.
- Public API v2: Authentication & keysCreate Public API v2 keys with per-module permissions, send them with every request, and keep them safe.
- Public API v2: Auto TagRules that tag customers and orders, add notes or mark a customer tax exempt when an event happens.
- Public API v2: Buy X Get YBuy X Get Y offers: what the customer buys, what they get, and the discount on it.
- Public API v2: Bulk & idempotencyCreate, replace or delete up to 250 rules in one Public API v2 request, and retry POST requests safely.