Errors and limits
The error shapes, every status code with what causes it, the business failure codes, and how to handle rate limits and retries.
A failed request always has success: 0. There are two main shapes, depending on what went wrong, and you can tell them apart by whether the body has an errors array.
The request itself was wrong
Problems with the request — a missing or invalid parameter, the API key, the rate limit, a catalogue level with nothing under it — return an errors array:
{
"success": 0,
"errors": [{
"message": "You are missing a required parameter for this request.",
"param": "country",
"description": "Country code from /api/v1/catalog/platforms/{platform}/countries, e.g. US"
}]
}Field | Always there | Meaning |
|---|---|---|
| yes | What went wrong, in plain English. |
| no | The parameter at fault ( |
| no | What that parameter should contain, or how to get past the error. |
Fix the request; retrying it unchanged will fail the same way (except 429 and 503, below).
Status codes
HTTP | When | Example |
|---|---|---|
| A catalogue level you sent | — |
| A required parameter is missing. |
|
| A keyed endpoint was called without a key ( |
|
| An id is not exact — a platform name instead of its id, |
|
| A parameter is malformed. |
|
| The API key does not exist or was revoked — on any endpoint, public ones included ( |
|
| A catalogue level has nothing in stock for the platform, country or provider in the path. The message points one level up. |
|
|
|
|
| A purchase with the same |
|
| An |
|
| Too many requests without a key from your IP address. |
|
| Too many requests with your key. |
|
| Too many requests with an invalid key from your IP address ( |
|
| Something went wrong on our side. |
|
| The catalogue is still loading prices, usually just after a restart. Retry in a few seconds. |
|
Note the difference between 400 and 404 for ids: an endpoint that takes service and country as parameters (/request/price, /purchase/sms) answers an unknown id with 400; a catalogue path (/catalog/platforms/{platform}/…) answers with 404, because the tree lists only what is in stock and that level is empty.
The request was fine, but it can't be done
Ordering, renting and buying email accounts fail the same handful of ways, and always carry two fields to branch on:
type— the broad category most verification clients already handle.code— the exact reason.
{
"success": 0,
"message": "That option is no longer available. Pick another.",
"type": "OUT_OF_STOCK",
"code": "OPTION_GONE"
}
|
| HTTP | What to do |
|---|---|---|---|
|
| 409 | Try another country or option, or later. |
|
| 409 | The |
|
| 409 | The |
|
| 402 | Top up. The message says what the order costs and what your balance is. |
|
| 400 | The price is above your |
|
| 400 | That rental term isn't offered. Use one from |
|
| 409 | That platform isn't sold in that country right now. |
|
| 503 | Supply is briefly unreachable. Retry after a short wait. |
|
| 400 / 409 | Not available for this product, number or action. |
|
| 429 | Too many purchases on your account (60 a minute by default), or supply is briefly rate-limiting. Wait |
|
| 404 | No such order, rental or account on your account. |
|
| 409 |
|
|
| 400 | Email accounts: choose 1 to 500. |
|
| 400 | Email accounts: no such product. |
|
| 500 | Something went wrong on our side. Retry once; if it persists, check the status page. |
A quote with nothing in stock has the same idea without a code: /request/price answers 409 with type: "OUT_OF_STOCK" and the (sold-out) options.
A few order-management answers carry just a message, for example /sms/check on an order that is not yours:
{ "success": 0, "message": "We could not find this order!" }A refused order costs nothing
Any failure before a number or account is issued leaves your balance untouched. If an order cannot be filled after your balance was reserved, the money goes straight back before the response returns.
Rate limits
Caller | Limit (default) | Counted per |
|---|---|---|
No key, public endpoints | 10 requests per second | IP address |
With a key | 32 requests per second | Account |
Purchases of any kind | 60 per minute | Account |
Rate-limit refusals carry a Retry-After header — wait that many seconds before retrying. A request-rate refusal looks like this:
{ "success": 0, "errors": [{ "message": "Rate limit exceeded. Slow down and try again." }] }Without a key, the error also has a description suggesting you send your key, which moves you onto your account's higher limit. The purchase limit answers in the business shape instead, with code: "RATE_LIMITED". More in Authentication.
Retrying safely
Retry
429and503after a short, growing wait, honouringRetry-Afterwhen it is there. Don't retry other4xxunchanged.An order request that times out on your side may still have succeeded. Send an
Idempotency-Keyheader with every purchase and retry with the same key: a repeat after success returns the original order (markedIdempotent-Replayed: true) and charges nothing. Without one, check/request/activebefore ordering again, or you may buy two numbers.