Common Noir SMS API errors and how to handle them
Quick answer
API errors use the HTTP status plus a JSON body like {"error": {"code": "...", "message": "..."}}. Switch on the code, not the message. Retry rate_limited (429), provider_error and restocking (502/503) with backoff. Treat no_numbers as "try another country". Fix invalid_api_key (401) and insufficient_balance (402) before retrying. You're never charged for a purchase that returns an error.
A small error-handling layer keeps a panel or bot from showing raw errors to users.
Error codes
| HTTP | Code | What to do |
|---|---|---|
| 401 | invalid_api_key | Check or regenerate the key |
| 402 | insufficient_balance | Top up; needed shows the price |
| 409 | price_above_max | Raise max_price or choose another country |
| 400 | no_numbers | No stock now; try another country |
| 400 | too_early | Cancel 2 minutes after buying; see waitSeconds |
| 400 | already_closed | The number was already canceled or expired |
| 400 | invalid_request / invalid_country | Fix the service or country code |
| 403 | account_suspended | Contact support |
| 429 | rate_limited | Wait and retry; limit is 120/min |
| 502/503 | provider_error / restocking | Retry shortly; not charged |
A retry pattern
import time, requests
RETRY = {"rate_limited", "provider_error", "restocking"}
def buy_with_retry(service, country, max_price, tries=4):
for i in range(tries):
r = requests.post(f"{API}/numbers", headers=H, timeout=20,
json={"service": service, "country": country, "max_price": max_price})
if r.status_code == 201:
return r.json()
err = r.json()["error"]
if err["code"] in RETRY:
time.sleep(2 ** i) # 1, 2, 4, 8 seconds
continue
if err["code"] == "no_numbers":
return None # try another country
raise RuntimeError(f"{err['code']}: {err['message']}")
return Noneresend_refused
When you ask for another SMS with /resend, some carriers refuse. You get resend_refused; the first code is still valid while the number is open.
Use it responsibly
Use the API only for your own legitimate accounts or apps you're authorized to test, and make sure your customers follow the same Acceptable Use Policy.
Frequently asked questions
Am I charged when I get no_numbers?
No. Failed purchases are never charged.
Where do I see my key?
On the API page of your dashboard, where you can also regenerate it.
Services in this guide
Keep reading
Receive SMS verification codes with Python
Buy a temporary number and read the SMS verification code from Python with the Noir SMS REST API. Full example with requests, polling and errors.
Read the guide →DevelopersReceive SMS verification codes with Node.js
Buy a temporary number and read SMS verification codes from Node.js with fetch and the Noir SMS REST API. Complete example with error handling.
Read the guide →DevelopersAutomate receiving SMS codes: polling or webhooks
Two ways to receive SMS verification codes automatically with the Noir SMS API: polling GET /numbers/{id} or a signed sms.received webhook. Code included.
Read the guide →DevelopersUsing virtual numbers to test SMS flows
How QA teams use real virtual numbers to test SMS sign-up and OTP flows end to end, when to use them vs test numbers, and how to automate with an API.
Read the guide →DevelopersReceive SMS verification codes with PHP
Use PHP and cURL to buy a temporary number and read the SMS verification code with the Noir SMS API. Copy-paste example for panels and scripts.
Read the guide →DevelopersTesting OTP and SMS sign-up flows in staging
Run end-to-end tests of your app's SMS sign-up in staging with real temporary numbers: Playwright example, what to check and how to keep costs low.
Read the guide →