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
740

Pay per verification. Cancel an order that received nothing and the charge comes back.

The flow, as calls

  1. GET /api/v2/tn101/get-services — the catalogue, with the identifier you will order by and its current price.
  2. POST /api/v2/tn101/request-number — order one. Takes service_key, plus optional state and area codes. Answers with the order id, the number and when it expires.
  3. GET /api/v2/tn101/get-number-details — poll it with order_id. pin is 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.
  4. 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

Ten in total. All of them take the same bearer token and answer with the same envelope.

  • GET/api/v2/profile
    Account profile
  • GET/api/v2/account-details
    Wallet balance
  • GET/api/v2/tn101/get-services
    S1 service list
  • GET/api/v2/tn101/us-states
    US states
  • POST/api/v2/tn101/request-number
    Order a number
  • POST/api/v2/tn101/reuse-number
    Reuse a number
  • GET/api/v2/tn101/get-number-details
    Order detail
  • GET/api/v2/tn101/get-reserved-list
    Reserved numbers
  • GET/api/v2/tn101/get-order-histories
    Order history
  • POST/api/v2/tn101/reject-number
    Cancel and refund

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