SMS verification API
Three calls cover the whole flow: order a number, poll until the code lands, release it if it does not. Bearer token, JSON in and out, and every action available in the panel is available over the same API.
- From
- $0.01per code
- Services
- 744
Pay per verification. Cancel an order that received nothing and the charge comes back.
The flow, as calls
GET /api/v2/tn101/get-services— the catalogue, with the identifier you will order by and its current price.POST /api/v2/tn101/request-number— order one. Takesservice_key, plus optionalstateand area codes. Answers with the order id, the number and when it expires.GET /api/v2/tn101/get-number-details— poll it withorder_id.pinis null until the code arrives and holds it afterwards. This is the cheapest read in the API and the one built to be called in a loop.POST /api/v2/tn101/reject-number— release a number that received nothing. This is also what returns the charge.
Nothing is pushed to you on this path, so step 3 is a poll. If you would rather be told than ask, the account also sends webhooks on order events, configured in the panel.
Authenticating
Every request carries Authorization: Bearer <token>. Tokens are generated in the panel under API access, and can be rotated there at any time — rotating invalidates the old token immediately, so swap it in your configuration first.
A request with no valid token gets 401. A suspended account, or one with API access switched off, gets 403. When the API is disabled site-wide, every endpoint answers 503 rather than pretending.
The response envelope
Every endpoint answers with the same three fields:
data carries the payload, success is the boolean to branch on, and summery is the human-readable message. That spelling is not a typo on this page — it is the field name the API has always shipped, it is in every integration written against it, and renaming it would break all of them to tidy up a keystroke. Read summery.
Validation failures answer 422 with data as an object of field names to error messages, so a caller can report which argument was wrong rather than just that something was.
Testing verification flows
The API is a practical way to exercise a signup or password-reset flow against a real carrier path instead of a mocked one — order a number, drive your own form with it, poll for the code, assert on what your application does next. It is a slower test than a stub and it catches the things a stub cannot: normalisation, line-type filtering, and the delivery delays your users will actually hit.
Limits
There is no per-second request limit on the API, but there are two quotas on ordering, and they are the same ones the panel enforces:
- Open orders. An account can hold only so many reserved numbers at once. Poll and release rather than fanning out.
- Cancelled and expired orders per day. Cross the daily allowance and ordering pauses for a while, extending on repeat. Every reservation costs real supply whether or not a code arrives, which is what the quota protects.
Both are described in more detail under verification numbers.
A worked example
Order, poll, release. Replace YOUR_API_TOKEN with a token from the panel and each of these runs as pasted.
# 1. Order a number.
curl -X POST https://smsportal.io/api/v2/tn101/request-number \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"service_key": "Google"}'
# 2. Poll until "pin" stops being null.
curl "https://smsportal.io/api/v2/tn101/get-number-details?order_id=12345" \
-H "Authorization: Bearer YOUR_API_TOKEN"
# 3. Nothing arrived? Release it and get the charge back.
curl -X POST https://smsportal.io/api/v2/tn101/reject-number \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"order_id": 12345}'What step 2 answers once the code lands
Before it arrives the same call returns the same shape with pin and message null.
{
"data": {
"order_id": 12345,
"number": "12085551234",
"service": "Google",
"status": "Reserved",
"state": "CA",
"pin": "418902",
"message": "418902 is your Google verification code.",
"price": 0.35,
"till_expiration": "2026-09-03T12:20:00.000Z"
},
"success": true,
"summery": "Success"
}Endpoints
Grouped by server. All of them take the same bearer token and answer with the same envelope.
Account
- GETAccount profile
/api/v2/profile - GETWallet balance
/api/v2/account-details
US Real SIM — /api/v2/real-sim
- GETProducts, validity windows, prices and stock
/api/v2/real-sim/get-services - POSTBuy numbers (queued, answers at once)
/api/v2/real-sim/request-number - GETPending, ready with the number, or failed and refunded
/api/v2/real-sim/get-order-status - GETYour numbers
/api/v2/real-sim/get-numbers - GETEvery SMS on a number
/api/v2/real-sim/get-messages - POSTRenew numbers for another period
/api/v2/real-sim/renew-number
Temp (s1) — /api/v2/tn101
- GETService list
/api/v2/tn101/get-services - GETUS states
/api/v2/tn101/us-states - POSTOrder a number
/api/v2/tn101/request-number - POSTReuse a number
/api/v2/tn101/reuse-number - GETOrder detail
/api/v2/tn101/get-number-details - GETReserved numbers
/api/v2/tn101/get-reserved-list - GETOrder history
/api/v2/tn101/get-order-histories - POSTCancel and refund
/api/v2/tn101/reject-number
Temp (s2) — /api/v2/tn102
- GETService list, carriers and surcharges
/api/v2/tn102/get-services - GETNumbers in stock per area code
/api/v2/tn102/get-area-stock - POSTOrder a number (carrier, area codes)
/api/v2/tn102/request-number - POSTOne number for several services
/api/v2/tn102/request-multiple - POSTAsk for a particular number
/api/v2/tn102/request-specific-number - POSTGet the same number again
/api/v2/tn102/reuse-number - GETOrder detail and code
/api/v2/tn102/get-number-details - GETOpen orders
/api/v2/tn102/get-reserved-list - GETOrder history
/api/v2/tn102/get-order-histories - POSTCancel and refund (after 5 minutes)
/api/v2/tn102/reject-number
Rental — /api/v2/rental
- GETRental services and period prices
/api/v2/rental/get-services - POSTRent a number
/api/v2/rental/request-number - GETYour rentals
/api/v2/rental/get-rentals - GETOne rental with its messages
/api/v2/rental/get-rental-details - GETMessages on a rental
/api/v2/rental/get-messages - POSTRenew for another period
/api/v2/rental/renew-rental - POSTSwitch auto-renew on or off
/api/v2/rental/set-auto-renew - POSTCancel and refund (no messages yet)
/api/v2/rental/cancel-rental
US Real SIM, Temp (s2) and Rental
US Real SIM sells real-SIM numbers kept for days. Buying is queued, so a slow carrier never times out your request:
get-services— every option; each validity window has its ownproduct_id,priceandstock.request-number—product_id, optionalquantity(1–100). Charges at once and answers withorder_idsandstatus: "pending". 402 when the balance is too low.get-order-status—order_ids(comma-separated, up to 100).pendingwhile we get the number, thenreadywithnumber,expires_atand ashare_link, orfailedwitherror— a failed number is refunded automatically. Polling every 2–3 seconds is fine; most numbers are ready within seconds.get-numbers(status=active|all) andget-messages(order_id) — every SMS, newest first, withcodepicked out.renew-number—order_ids; charges the renewal price now and extends each active number by the renewal period, usually within hours.get-numbersshowsrenewal: "pending"until it lands; one not applied within 3 days is refunded automatically.- A delivered number is not refundable. Buy one first when trying a service.
- Webhooks:
order.number_assignedwhen the number is ready,sms.receivedfor every SMS,order.failed+order.refundedwhen it could not be delivered; these carry"provider": "us-real-sim".
Temp (s2) works like Temp (s1), with two extra choices on request-number:
service_key— required; theservice_keyfromtn102/get-services.carrier— optional,at&tortmobile.keep_carrier: truefails the order instead of falling back to another carrier.area_codes— optional, comma-separated, e.g."212,305";tn102/get-area-stock?service_key=…lists what is in stock.lock_area_code: truefails the order instead of falling back.- Each choice adds the surcharge percentage returned by
get-services. A failed order is never charged. request-multiple—service_keys(2–7, comma-separated or an array) plus the samecarrier/area_codesoptions: one number for all of them, one order per service. All or nothing — if one service cannot go on the number, none is charged.request-specific-number—number(10-digit US) andservice_key: that exact number, for the service price plus a surcharge. Nothing is charged when it is not available.reject-numberworks from five minutes after purchase. Before that it answers 409 withdata.retry_after_seconds.- Poll
get-number-details;codeholds the verification code once it arrives. Unused numbers are refunded automatically at expiry.
Rental keeps one number for one service for a period:
request-number—service_keyandperiod(1d,3d,7d,14dor30d), both required; optionalarea_codes,lock_area_code,auto_renew. Answers withrental_id.get-messages/get-rental-details—rental_id; every message received, newest first, with the code picked out.renew-rental—rental_id; charges the current price for the same period and extends the end date. 402 when the balance is too low.set-auto-renew—rental_id,auto_renew(true/false); renews from the balance shortly before the end.cancel-rental—rental_id; refunds in full only while no message has arrived.
Order events for both reach your webhooks like any other order; rental events carry "provider": "s2-rental".
Create an account to generate a token — the full reference and your keys live in the panel under API access.
Questions
- How do I get a token?
- Create an account, then generate one in the panel under API access. It is shown in full there and can be rotated whenever you need.
- Does calling the API cost anything?
- Requests are free; numbers are not. You are charged for a number when `request-number` issues one, at the same per-verification price as an order placed in the panel.
- Is there a sandbox?
- No separate sandbox. There is one environment, and an ordered number is a real number with a real charge against it — which is also what makes the API worth testing against in the first place.
- How should I poll for the code?
- `get-number-details` is one indexed lookup with no upstream call, so a few seconds between polls is reasonable. Stop when `pin` is populated or when the order passes its expiry, and release anything that received nothing.
- Can I be notified instead of polling?
- Yes. Webhooks are configured per account in the panel and fire on order events, so a long-running integration does not have to sit in a loop.
Related
Ready to receive a code?
Registration takes a minute, and you only pay for numbers that deliver.
Create a free account