Developers · REST API v1

Noir SMS API

Buy temporary numbers and read SMS verification codes from your own system, app or reseller panel. Same prices, balance and automatic refunds as the dashboard.

Português →

Quick answer

Create a Noir SMS account, add funds and generate an API key on the API page of your dashboard. Then call POST /api/v1/numbers with a service and a country to get a number, and GET /api/v1/numbers/{id} to read the SMS code. Numbers without an SMS are refunded automatically after 20 minutes.

Quickstart

  1. Get a keyLog in, open Dashboard → API and click Generate API key. Copy it: it is shown only once.
  2. Add fundsThe API spends your normal balance. Top up by card or USDC / SOL in the dashboard.
  3. Buy, read, doneBuy a number, put it in the app you are verifying, poll for the code (or use a webhook), then finish or cancel.
curl
curl -X POST https://noirsms.com/api/v1/numbers \
  -H "Authorization: Bearer nsk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service": "wa", "country": "us", "max_price": 1.50}'

Authentication

Every request needs your API key, either as a bearer token or in the X-API-Key header. Keys start with nsk_live_. One key per account; regenerating it in the dashboard disables the old one at once. Never put the key in a browser or mobile app: call the API from your server.

http
Authorization: Bearer nsk_live_YOUR_KEY
# or
X-API-Key: nsk_live_YOUR_KEY

Base URL: https://noirsms.com/api/v1. All bodies are JSON, all prices are in US dollars, all times are ISO 8601 in UTC.

Machine-readable spec: /api/openapi.json (OpenAPI 3.1). Import it into Postman or Insomnia, or generate a client in your language.

How a purchase works

  • waiting: the number is yours for 20 minutes (expires_at). Enter it in the app and wait for the SMS.
  • received: at least one SMS arrived; the code is in sms[].code and the full text in sms[].text. You can ask for another SMS on the same number with /resend while it is open.
  • canceled: you canceled it (allowed from cancelable_at, 2 minutes after buying) and the price went back to your balance.
  • expired: no SMS in 20 minutes; the price went back to your balance automatically.

The price is calculated on our side at the moment of purchase; send max_price to refuse the purchase if the current price is higher than you expect.

Endpoints

GET/balance

Your current balance.

request
curl https://noirsms.com/api/v1/balance -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "balance": 24.37, "currency": "USD", "status": "active" }

GET/countries

Countries with numbers in stock right now and how many services each has.

request
curl https://noirsms.com/api/v1/countries -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "server": 1, "countries": [ { "country": "gb", "services": 382 }, { "country": "us", "services": 264 } ] }

GET/services?country=us

Services in stock for one country, with the price you pay per number and the stock.

Use the service code (for example wa = WhatsApp, tg = Telegram, go = Google) when you buy.

ParameterTypeDescription
countrystring2-letter ISO code, default us.
request
curl "https://noirsms.com/api/v1/services?country=us" -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{
  "server": 1, "country": "us", "currency": "USD",
  "services": [
    { "service": "wa", "name": "Whatsapp", "price": 0.73, "stock": 91562 },
    { "service": "tg", "name": "Telegram", "price": 0.65, "stock": 40211 }
  ]
}

POST/numbers

Buy a number.

The price is debited from your balance and refunded automatically if no SMS arrives. Responds 201 with the number.

ParameterTypeDescription
servicestringService code from /services. Required.
countrystring2-letter ISO code. Required.
max_pricenumberOptional. Refuse (HTTP 409 price_above_max) if the current price is higher.
request
curl -X POST https://noirsms.com/api/v1/numbers \
  -H "Authorization: Bearer nsk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service": "wa", "country": "us", "max_price": 1.50}'
response
{
  "id": "6026e15f-088d-45b5-bf22-70ed0b417afa",
  "phone": "12025550147",
  "service": "wa",
  "country": "us",
  "server": 1,
  "price": 0.73,
  "currency": "USD",
  "status": "waiting",
  "created_at": "2026-10-14T07:05:10Z",
  "expires_at": "2026-10-14T07:25:10Z",
  "cancelable_at": "2026-10-14T07:07:10Z",
  "sms": []
}

GET/numbers/{id}

Status of one number and every SMS it received.

Checks the carrier in real time while the number is open. Poll every 3 to 5 seconds until status is received.

request
curl https://noirsms.com/api/v1/numbers/6026e15f-088d-45b5-bf22-70ed0b417afa \
  -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{
  "id": "6026e15f-088d-45b5-bf22-70ed0b417afa",
  "phone": "12025550147", "service": "wa", "country": "us",
  "status": "received", "price": 0.73, "currency": "USD",
  "expires_at": "2026-10-14T07:25:10Z",
  "sms": [ { "code": "482915", "text": "Your WhatsApp code: 482-915", "received_at": "2026-10-14T07:06:02Z" } ]
}

GET/numbers

Numbers you bought through the API, newest first.

ParameterTypeDescription
statusstringactive (default: open numbers), waiting, received, canceled, expired or all.
limitinteger1 to 100, default 50.
beforetimestampPagination: numbers created before this time (use the last created_at).
request
curl "https://noirsms.com/api/v1/numbers?status=active&limit=20" -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "data": [ { "id": "…", "status": "waiting", "sms": [] } ], "has_more": false }

POST/numbers/{id}/cancel

Cancel a number that has not received an SMS and get the price back.

Allowed from cancelable_at (2 minutes after buying). If the SMS arrived in the meantime, nothing is refunded and the code is returned.

request
curl -X POST https://noirsms.com/api/v1/numbers/{id}/cancel -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "id": "…", "status": "canceled", "refunded": true, "sms": [] }

POST/numbers/{id}/resend

Ask for another SMS on the same number, free, while it is open.

Only after the first SMS arrived. Some carriers refuse it; you then get resend_refused.

request
curl -X POST https://noirsms.com/api/v1/numbers/{id}/resend -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "id": "…", "status": "received", "sms": [ … ] }

POST/numbers/{id}/finish

Tell us you are done with a number that received its code.

Optional, but it releases the number sooner.

request
curl -X POST https://noirsms.com/api/v1/numbers/{id}/finish -H "Authorization: Bearer nsk_live_YOUR_KEY"
response
{ "id": "…", "status": "received", "sms": [ … ] }

Webhooks

Set an HTTPS URL on the API page of your dashboard and we POST sms.received as soon as we detect an SMS on a number you bought through the API (we retry once after 3 seconds if your server fails). Respond with any 2xx status.

json
{
  "type": "sms.received",
  "created_at": "2026-10-14T07:06:03Z",
  "data": {
    "number_id": "6026e15f-088d-45b5-bf22-70ed0b417afa",
    "phone": "12025550147", "service": "wa", "country": "us",
    "status": "received", "expires_at": "2026-10-14T07:25:10Z",
    "sms": { "code": "482915", "text": "Your WhatsApp code: 482-915", "received_at": "2026-10-14T07:06:02Z" }
  }
}

Every webhook carries X-NoirSMS-Signature: t=<unix time>,v1=<signature>, where the signature is the hex HMAC-SHA256 of "<t>.<raw body>" with your webhook secret (shown in the dashboard). Check it, and reject timestamps older than 5 minutes.

node.js
import crypto from 'node:crypto';

function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
python
import hmac, hashlib, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Errors

Errors use the HTTP status code and a JSON body with a stable code you can switch on:

json
{ "error": { "code": "insufficient_balance", "message": "Insufficient balance. You need $0.73.", "needed": 0.73 } }
HTTPcodeMeaning
401invalid_api_keyMissing, wrong or revoked key.
402insufficient_balanceNot enough balance; needed has the price.
409price_above_maxCurrent price is above your max_price.
400no_numbersNo stock for this service and country right now. You were not charged.
400too_earlyCancel is allowed 2 minutes after buying; see waitSeconds.
400already_closedThe number was already canceled or expired.
400invalid_request / invalid_countryCheck the service and country codes.
403account_suspendedThe account is suspended. Contact support.
429rate_limitedMore than 120 requests in a minute.
502 / 503provider_error / restockingTemporary carrier problem. You were not charged; retry shortly.

Limits & rules

  • 120 requests per minute per key. Poll a number every 3 to 5 seconds, not faster.
  • Each number is for one verification and stays reserved for 20 minutes. SMS only: no calls, no outgoing messages.
  • Prices follow supply and demand and can change during the day; always read them from /services or use max_price.
  • Use the API only for your own legitimate accounts or, as a reseller, for customers who follow the same Acceptable Use Policy. Bulk or automated account creation, spam, ban evasion and bypassing bank, fintech or identity (KYC) checks are prohibited and lead to account closure.

High volume

Buying numbers every day for your platform, panel or bot? Open a ticket in the Support area of your dashboard with your approximate monthly volume, the services and countries you use and what your platform does. We review each request and can offer special conditions for medium and high volume.

Ask for volume conditions

API FAQ

How much does the API cost?

Nothing extra. Each number costs the same as in the dashboard and is paid from the same balance. If no SMS arrives within 20 minutes, the charge goes back to your balance automatically.

Can I resell numbers with the API?

Yes. You can offer SMS verification inside your own platform or panel and set your own prices. Your customers must follow the same Acceptable Use Policy, and you are responsible for abuse on your platform.

Is there a sandbox?

There is no separate sandbox. Use GET endpoints (balance, services, countries) freely; a purchase uses real balance but is refunded automatically if no SMS arrives, and you can cancel it 2 minutes after buying.

How fast do I get the code?

Usually within seconds of the app sending it. Poll GET /numbers/{id} every 3 to 5 seconds, or set a webhook in the dashboard to receive sms.received as soon as we detect the SMS.

What is the rate limit?

120 requests per minute per API key. Over the limit you get HTTP 429 with the code rate_limited; wait a few seconds and retry.

Are there special conditions for high volume?

Yes, on request. If you buy medium or high volume, open a ticket in the Support area of your dashboard with your monthly volume, services and countries, and we will review special conditions for your account.

Is there an OpenAPI spec?

Yes, at https://noirsms.com/api/openapi.json (OpenAPI 3.1). Import it into Postman, Insomnia or a code generator to get a ready client.

Start building with Noir SMS

Create a free account, add funds and generate your API key in a minute.