Errors
Failed requests return a JSON body with one error object:
{ "error": { "code": "spend_limit", "message": "This purchase is above the per purchase limit of this API key.", "details": { "limitCents": 2000 }, "request_id": "c3a7e1f0-52d8-4b69-8e14-7f0a9d6b2c35" }}codeis stable. Branch on it in code.messageis for people. Do not parse it, and expect the wording to change.detailsis optional and depends on the code. It can hold the field that failed, a limit, or a retry time.request_idmatches theX-Request-Idheader on the same response. Log it, and quote it when you contact support.
| Code | HTTP status | Meaning | What to do |
|---|---|---|---|
validation |
422 | The request is malformed or breaks a rule. details.issues lists each field path and message. Also used when a shipping rule blocks a shipment (details.blockedByRule). |
Fix the request. Do not retry unchanged. |
unauthenticated |
401 | The key is missing, malformed, unknown, revoked or expired. | Check the key. |
forbidden |
403 | The key lacks the permission, or the request came from an address outside its allowlist. | Use a key with the right permission or from an allowed address. |
not_found |
404 | No such endpoint or object in your account. | Check the id. |
conflict |
409 | The request clashes with the current state: an already purchased shipment, expired rates (details.expired), a refund already requested, or an idempotency key still in use. |
Read the current state, then decide. |
insufficient_funds |
402 | The wallet cannot cover the purchase. | Add funds, then try again. |
spend_limit |
402 | The purchase would break a per label or per day limit on the key or the account. details holds the limit in cents. |
Raise the limit in the app, or wait for the day to reset. |
org_suspended |
403 | The account is not active. | Contact support. |
rate_limited |
429 | Too many requests. The Retry-After header says how many seconds to wait. |
Wait, then retry. See Rate limits. |
maintenance |
503 | The platform is in maintenance and refuses changes. Reads still work. | Retry later. |
not_configured |
501 | A needed service is not set up, for example no carrier is available. | Retry later or contact support. |
upstream |
502 | A carrier failed or returned something unusable. | Retry with backoff. |
internal |
500 | Something went wrong on our side. The message is generic on purpose. | Retry with backoff. Quote the request_id if it persists. |
An unsupported method on a real path returns 405 with an Allow header.
Retrying
Section titled “Retrying”- Retry
429,502,503and500with exponential backoff and some jitter. For429, wait at leastRetry-Afterseconds. - Do not retry
422,401,403,404unchanged. They will fail the same way. - For a purchase, always retry with the same
Idempotency-Key. See Buying labels safely. - A
5xxresponse to a request with an idempotency key does not lock the key. You can send the same request again.
© 2026 Ship Jay. All rights reserved. Created & Developed by WCWS Internet
