SMS API for developers & AI agents

Everything on NanSMS can be done over a small REST API. There is no API key or account: an order is accessed with the secret token returned when it is created.

Base URL: https://api.nansms.com · OpenAPI spec · llms.txt

1. Choose a service and a country

curl "https://api.nansms.com/catalog"            # services[].slug, e.g. telegram, whatsapp, google, openai
curl "https://api.nansms.com/services/telegram"  # countries[]: country, priceUsd, count, rate (success % last week)

2. Order a number

curl -X POST "https://api.nansms.com/buy"   -H "Content-Type: application/json"   -d '{"service":"telegram","country":"usa","payinCurrency":"XNO"}'

The response contains token (shown only once, store it), balanceToken (a new balance where refunds land; pass it in later orders), priceUsd and payment with payinAddress, payinAmount, payinCurrency and paymentLink.

3. Pay

Send exactly payinAmount of payinCurrency to payinAddress before expireAt, or open paymentLink. Fastest: XNO or NANUSD (seconds), or "payinCurrency":"BALANCE" with a funded balanceToken (instant, no invoice).

4. Get the number, then the code

curl "https://api.nansms.com/order" -H "Authorization: Bearer ns_..."

Poll every few seconds (or read GET /order/stream, Server-Sent Events). When status is active, use phone on the target service and request the code. When it is received, sms[].code holds it. The number receives SMS until expiresAt (about 15-20 minutes).

5. Finish or cancel

curl -X POST "https://api.nansms.com/order/finish" -H "Authorization: Bearer ns_..."   # got the code
curl -X POST "https://api.nansms.com/order/cancel" -H "Authorization: Bearer ns_..."   # no SMS yet: refund

No SMS before expiresAt, a cancelled number, or no number available after payment: status becomes refunded and priceUsd is credited to the balance. Check it with GET /balance and Authorization: Bearer nb_.... Some numbers can only be cancelled after a short wait: cancelableAt (null when there is none).

Errors & limits

Errors return {"error": "message"} with status 400 (invalid input), 401 (bad token), 402 (balance too low), 409 (no number in this country / action not possible), 429 (too many orders) or 503.