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.

Embed
Preview of the card
<a href="https://numestri.com/docs/errors"><img src="https://numestri.com/docs/_api/public/orgs/25229885-9620-4c08-bd2e-a0d0d74ac8b6/spaces/ef21e0c3-966d-4b03-b6d2-e66d449835d4/pages/99832685-e8d6-4faa-9d21-c5de19cee838/social-card.svg?v=3e7ae33eec8b" alt="Errors and limits" width="600" height="315"></a>

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

message

yes

What went wrong, in plain English.

param

no

The parameter at fault (key for API key problems).

description

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 message

304

A catalogue level you sent If-None-Match for has not changed. No body — reuse what you have. See caching.

—

400

A required parameter is missing.

You are missing a required parameter for this request.

400

A keyed endpoint was called without a key (param: "key").

You are missing a required parameter for this request.

400

An id is not exact — a platform name instead of its id, us instead of US.

No platform has the id "Telegram". See /api/v1/catalog/platforms.

400

A parameter is malformed.

max_price must be a positive amount in dollars, e.g. 0.75.

403

The API key does not exist or was revoked — on any endpoint, public ones included (param: "key").

Invalid API key

404

A catalogue level has nothing in stock for the platform, country or provider in the path. The message points one level up.

Nothing is in stock for the platform "telegarm". See /api/v1/catalog/platforms.

404

/rental/retrieve_pricing has no rentals for that country (or platform there).

No rentals are available in "XX". Check /api/v1/rental/retrieve_all for the current list.

409

A purchase with the same Idempotency-Key is still running.

A purchase with this Idempotency-Key is still in progress. Retry in a few seconds.

422

An Idempotency-Key was reused for a different kind of request.

This Idempotency-Key was already used for a different request. Use a new key.

429

Too many requests without a key from your IP address.

Rate limit exceeded. Slow down and try again.

429

Too many requests with your key.

Rate limit exceeded. Slow down and try again.

429

Too many requests with an invalid key from your IP address (param: "key").

Too many requests with an invalid API key. Wait before trying again.

500

Something went wrong on our side.

Could not load the catalogue.

503

The catalogue is still loading prices, usually just after a restart. Retry in a few seconds.

Prices are being refreshed. Try again 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"
}

code

type

HTTP

What to do

OUT_OF_STOCK

OUT_OF_STOCK

409

Try another country or option, or later.

OPTION_GONE

OUT_OF_STOCK

409

The option you sent no longer exists. Re-read the options and pick again.

PROVIDER_UNAVAILABLE

OUT_OF_STOCK

409

The provider you sent has nothing in stock here. Pick another or drop provider.

INSUFFICIENT_BALANCE

BALANCE_ERROR

402

Top up. The message says what the order costs and what your balance is.

PRICE_ABOVE_MAX

PRICE_NOT_FOUND

400

The price is above your max_price. The message says what the price is.

INVALID_DAYS

PRICE_NOT_FOUND

400

That rental term isn't offered. Use one from retrieve_pricing.

NOT_SOLD

PRICE_NOT_FOUND

409

That platform isn't sold in that country right now.

UNAVAILABLE

SERVICE_UNAVAILABLE

503

Supply is briefly unreachable. Retry after a short wait.

UNSUPPORTED

UNSUPPORTED

400 / 409

Not available for this product, number or action.

RATE_LIMITED

RATE_LIMITED

429

Too many purchases on your account (60 a minute by default), or supply is briefly rate-limiting. Wait Retry-After seconds when it is sent.

NOT_FOUND

NOT_FOUND

404

No such order, rental or account on your account.

CANCEL_TOO_EARLY

CANCEL_TOO_EARLY

409

/sms/cancel only. Wait retry_after seconds, then cancel again.

INVALID_QUANTITY

INVALID_QUANTITY

400

Email accounts: choose 1 to 500.

UNKNOWN_PRODUCT

UNKNOWN_PRODUCT

400

Email accounts: no such product.

UNKNOWN

UNKNOWN

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 429 and 503 after a short, growing wait, honouring Retry-After when it is there. Don't retry other 4xx unchanged.

  • An order request that times out on your side may still have succeeded. Send an Idempotency-Key header with every purchase and retry with the same key: a repeat after success returns the original order (marked Idempotent-Replayed: true) and charges nothing. Without one, check /request/active before ordering again, or you may buy two numbers.

Updated