Every error carries a machine code, a human sentence and, where there is one, the field that caused it. The sentence is for your logs; the code is what you branch on.
{ "error": { "code": "landmark_required", "message": "Luanda addresses need a landmark the courier can find.", "field": "place.landmark", "doc": "https://api.palopgo.com/docs/orders#moradas" }}Idempotency
Every write takes an Idempotency-Key. Send the same key with the same body and you get the same answer; send it with a different body and you get 409 idempotency_conflict rather than a second order. Keys are kept for 24 hours.
- Generate the key from something stable in your own system — a purchase order number, a basket id — not from a random value you forget.
- A network timeout is not a failure. Retry with the same key; if the order was created you get it back.
- Reads need no key and are always safe to retry.
Rate limits
Limits are counted per application, not per organisation, so a noisy batch job cannot take your ordering path down with it. The answer always tells you when the window resets.
HTTP/1.1 429 Too Many RequestsRetry-After: 18X-RateLimit-Limit: 120X-RateLimit-Remaining: 0X-RateLimit-Reset: 1791371028Status codes we use
| Status | What it means for you |
|---|---|
| 200 | A read succeeded. |
| 201 | A write created something. The body is the new resource. |
| 202 | Accepted for processing — catalogue pushes answer this. |
| 401 | Bad or revoked key. Not retryable. |
| 403 | The key's scopes or markets do not cover this call. |
| 409 | Conflict: idempotency, or a state the machine does not allow. |
| 422 | The body is wrong. The field is in the error. |
| 429 | Rate limited. Back off using Retry-After. |
| 503 | A cell is degraded. Retry; the status page says more. |