Errors
Every error has the same shape:
{ "error": { "code": "not_found", "message": "UPS returned no track details for that number" } }Branch on code; message is for people and can change.
| HTTP | code | When | Retry? |
|---|---|---|---|
| 400 | bad_request | No numbers, more than 100, or more than 35 for one carrier | No |
| 400 | unknown_carrier | No carrier's format matches the number (or its check digit is wrong), or an unsupported carrier | Fix the number or pass carrier |
| 401 | unauthorized | Missing, wrong or revoked key, or a disabled account | No |
| 402 | payment_required | No key, and no free lookup left (or a request for several numbers) | Pay with MPP; see Pay per request |
| 402 | credits_used | A credit key doesn't have enough credits left for the request | Pay again without the key |
| 404 | not_found | The carrier has no record of the number, or no such key or route | Later; new labels can take a while to appear |
| 429 | free_lookup_used | No key, and this IP's free lookup for today is used (when pay per request is off) | Sign up for a key, or after 00:00 UTC |
| 429 | rate_limited | Daily limit reached, or the carrier is throttling (Spee-Dee) | After 00:00 UTC, or in a minute for Spee-Dee |
| 500 | internal_error | Unexpected failure | Yes |
| 500 | not_configured | The service is misconfigured | Yes |
| 502 | carrier_error | The carrier failed or blocked the lookup | Yes |
On GET /v1/track?numbers=, per-number errors (not_found, unknown_carrier, carrier_error, rate_limited) are returned inside results with a 200; only a malformed request, a bad key or the daily limit fails the whole call.
Over MCP, a failed tool call returns this same object as a result with isError: true.