API & webhooks
API errors and rate limits
What each status code means, what the 402 payloads carry, and how the per-key rate limit works.
Every response from the public API is either your data or a status code that tells you what to do next. This is the whole set.
Status codes
| Code | Meaning | What to do |
|---|---|---|
200 | Success. | — |
400 | Malformed request body or an invalid reference. | Fix the payload. Retrying unchanged will fail again. |
401 | Missing or unrecognised API key. | Check the Authorization header. Keys can be revoked. |
403 | Key is valid but not permitted for this call. | Check the key is active and the account is enabled. |
402 | Payment required — two distinct causes, see below. | Branch on data.error, never on the message text. |
429 | Rate limit exceeded for this key. | Wait Retry-After seconds, then retry. |
5xx | Our side, or the carrier upstream. | Retry with backoff. If it persists, send us the reference. |
The two 402s
They mean different things and need different handling, so data.error distinguishes them rather than making you scrape a string.
data.error | Cause | Payload carries |
|---|---|---|
shipment_limit_reached | All shipment slots for the billing period are used. | limit, used, plan, maxedOut, manageUrl, usageUrl, and contactUrl when you are already on the largest standard plan. |
payment_overdue | A payment failed and the grace period has ended. | overdueDays, graceDays, plan, manageUrl, usageUrl. |
maxedOut is the field worth branching on: it is the difference between "upgrade" and "you have already upgraded as far as self-serve goes, talk to us". Telling a customer on the top plan to upgrade is how an integration gets abandoned.
A 402 for a slot limit does not affect references you already track — those keep updating. It blocks adding new ones. See how shipment slots are counted.
Rate limits
| Property | Value |
|---|---|
| Scope | Per API key, not per account or per IP. |
| Default | 120 requests per minute. |
| Window | Fixed, 60 seconds. |
| Headers | X-RateLimit-* on every response, not only on a 429. |
| Over limit | 429 with Retry-After in seconds. |
Because the headers are on every response, you can pace yourself without ever tripping the limit — read the remaining count rather than waiting to be refused. Higher limits can be set per plan; ask if 120/min is genuinely not enough for your integration.
Authentication
Send your key as a bearer token:
Authorization: Bearer <your-api-key>
Keys are stored hashed, so we cannot show you an existing key again — if you lose it, issue a new one and revoke the old. See getting an API key.
Checking usage without tripping a limit
The Developer page shows live usage — used, limit, remaining — for the account, and the same figures are available to your integration. Poll that rather than discovering your position from a 402.
Was this helpful?