Guide

SMS-Activate API alternative: a migration tutorial

SMS-Activate stopped operating on December 22, 2025, according to the closure notice on its own site, and a lot of code still calls its handler_api.php. TempSMSVerify serves the same protocol, so most clients need two changes: the base URL and the api_key. This tutorial covers the swap, the action and status mapping, the differences that can trip you up, and working examples in Bash, Python and Node.js.

By TempSMSVerifyPublished

Quick answer

What is the best SMS-Activate API alternative?

Pick a provider that speaks the same handler_api protocol so your code keeps working. TempSMSVerify serves it at https://tempsmsverify.com/api/stubs/handler_api.php with getBalance, getNumber, getStatus, setStatus, getPrices, getNumbersStatus, getServicesList and getCountries. Change the base URL and api_key, check country ids, and you pay only when the SMS arrives.

Step by step

  1. 1

    Create an account and top up

    Sign up and add at least $5. API access unlocks after your first confirmed top-up.

  2. 2

    Generate an API key

    Open the API section of your dashboard and generate a key. It is shown once; store it as a secret. A new key revokes the old one.

  3. 3

    Swap the base URL

    Replace the old SMS-Activate host with https://tempsmsverify.com/api/stubs/handler_api.php and set api_key to your new key.

  4. 4

    Fix country ids

    Call getCountries and update your mapping. Send an explicit country: omitting it or sending 0 means USA on TempSMSVerify, not Russia.

  5. 5

    Confirm service codes

    Call getServicesList. Standard codes such as wa or tg work, and so do names such as whatsapp.

  6. 6

    Run one test activation

    Call getNumber, poll getStatus every 3 to 5 seconds, then finish with setStatus 6 or cancel with setStatus 8 if no code arrives.

What changes when you move off SMS-Activate?

The protocol stays the same: GET or POST requests with api_key and action, plain-text or JSON responses, and HTTP 200 on every reply, so your client keeps parsing the body. What changes is the host, the key and a few details of numbering. The id returned by getNumber is the order id on TempSMSVerify, and prices and balance are in USD.

SMS-Activate to TempSMSVerify: what to change
ItemSMS-Activate (before)TempSMSVerify (after)
EndpointSMS-Activate host + /stubs/handler_api.phphttps://tempsmsverify.com/api/stubs/handler_api.php
Authenticationapi_key parameterapi_key parameter (also X-Api-Key or Authorization: Bearer)
country=0 or missingRussiaUSA (id 187), the platform default
Country idsSMS-Activate numberingSame numbering where HeroSMS sells the country (187 USA, 16 UK, 36 Canada); other countries get ids of 1000 and up; names and ISO2 codes also accepted
serviceProtocol codes (wa, tg, go...)Same codes, or the service name (whatsapp)
CurrencyAccount currencyUSD
Unsupported actionsRentals and other extensionsReturn BAD_ACTION; only the eight actions below exist

Which actions are supported?

These eight actions cover the full activation flow, plus catalog lookups. Anything else returns BAD_ACTION, so search your code for other action names before you switch.

Supported actions on https://tempsmsverify.com/api/stubs/handler_api.php
actionParametersSuccess response
getBalancenoneACCESS_BALANCE:12.50
getNumberservice, country (optional), server (optional, 1 or 2)ACCESS_NUMBER:{id}:{phone}
getStatusidSTATUS_WAIT_CODE, STATUS_OK:{code}, STATUS_WAIT_RETRY:{last code}, STATUS_CANCEL
setStatusid, status (1, 3, 6 or 8)ACCESS_READY, ACCESS_RETRY_GET, ACCESS_ACTIVATION, ACCESS_CANCEL
getPricescountry, service (both optional)JSON: {"187": {"wa": {"cost": price, "count": stock}}}
getNumbersStatuscountry (optional)JSON: {"wa_0": stock, "tg_0": stock}
getServicesListnoneJSON: {"status": "success", "services": [{"code": "wa", "name": "whatsapp"}]}
getCountriesnoneJSON: {"187": {"id": 187, "eng": "usa", "rus": "usa", "visible": 1}}

How do setStatus values and error codes map?

setStatus keeps the original meaning of the four values used in an activation. Status 3 asks for another SMS on the same number, only after the first code arrived and while the number is active, at no extra cost. Status 6 finishes the activation; if no SMS ever arrived, the frozen amount is released. Status 8 cancels and refunds, but only before any SMS arrives and only after about 2 minutes; earlier returns EARLY_CANCEL_DENIED, so retry a moment later. Any other value returns BAD_STATUS.

Text error codes on the compatible endpoint (always HTTP 200)
CodeWhen you see itWhat to do
NO_KEY / BAD_KEYMissing, invalid or revoked api_keyGenerate a key in the dashboard and update your config
BAD_ACTIONAction not implementedUse one of the eight supported actions
BAD_SERVICEUnknown service code or nameCheck getServicesList
WRONG_COUNTRYUnknown or unavailable countryCheck getCountries
NO_BALANCEBalance too low for the frozen priceTop up, then retry
NO_NUMBERSNo stock, or over 500 purchases per minuteTry another country or slow down
NO_ACTIVATIONUnknown order idUse the id from ACCESS_NUMBER
BAD_STATUSStatus not allowed now, such as cancel after an SMSFinish with status 6 instead
EARLY_CANCEL_DENIEDCancel before about 2 minutesWait and retry setStatus 8
SERVER_ERRORUpstream or internal failureRetry with backoff; contact support if it persists

How should you poll, and when are you charged?

Poll getStatus every 3 to 5 seconds. Each call syncs the order with the line provider, so that is where the code appears. Stop when you get STATUS_OK or STATUS_CANCEL. A number stays reserved for up to 20 minutes.

Billing differs from what many SMS-Activate users were used to. getNumber only freezes the price. The charge happens when the SMS arrives. If the number expires without an SMS, or you cancel with status 8, the frozen amount returns to your balance automatically, so your code does not need any refund logic. Each account can buy up to 500 numbers per minute across both APIs.

Should you use the compatible endpoint or the REST API?

Keep the compatible endpoint for existing bots and tools; it is the fastest path. For new code, the REST API at https://tempsmsverify.com/api/v1 returns JSON with normal HTTP status codes: POST /orders to buy, GET /orders/{id} to poll, and POST /orders/{id}/finish, /cancel or /resend to close or extend. Both share one balance and the same billing rule. The full reference, including the order object and REST errors, is on /api-docs.

What are the known differences to test?

Run one end-to-end activation with a small balance before moving production traffic, and check these points:

  • country=0 means USA here, not Russia. Send explicit ids.
  • Countries not sold by HeroSMS have ids of 1000 and up; do not hard-code a list, read getCountries.
  • server=1 or server=2 is our own optional extension; omit it for automatic selection.
  • There are no rentals, voice activations or multi-service numbers.
  • Amounts are in USD with two decimals in getBalance.
  • Responses are always HTTP 200; parse the body.

What are the API usage rules?

The API spends your balance with the same rules as the website. Use it to verify accounts you own or to test your own products, follow each app's terms, and do not use it for fraud, spam, impersonation or getting around bans; see /acceptable-use. TempSMSVerify is not SMS-Activate and is not affiliated with it. Integration questions can go to [email protected].

Code examples

Bash: swap the base URL and check your balance bash
BASE="https://tempsmsverify.com/api/stubs/handler_api.php"
API_KEY="your_key_here"

curl "$BASE?api_key=$API_KEY&action=getBalance"
# ACCESS_BALANCE:12.50

curl "$BASE?api_key=$API_KEY&action=getNumber&service=tg&country=187"
# ACCESS_NUMBER:123456:12025550123

curl "$BASE?api_key=$API_KEY&action=getStatus&id=123456"
# STATUS_WAIT_CODE  ->  STATUS_OK:482913

curl "$BASE?api_key=$API_KEY&action=setStatus&id=123456&status=6"
# ACCESS_ACTIVATION
Python: SMS-Activate-compatible flow python
import os
import time
import requests

BASE = "https://tempsmsverify.com/api/stubs/handler_api.php"
KEY = os.environ["TEMPSMSVERIFY_API_KEY"]

def call(**params):
    r = requests.get(BASE, params={"api_key": KEY, **params}, timeout=30)
    return r.text.strip()

resp = call(action="getNumber", service="tg", country="187")
if not resp.startswith("ACCESS_NUMBER:"):
    raise SystemExit(resp)  # NO_BALANCE, NO_NUMBERS, BAD_SERVICE...
_, order_id, phone = resp.split(":")
print("Number:", phone)

deadline = time.time() + 20 * 60
while time.time() < deadline:
    status = call(action="getStatus", id=order_id)
    if status.startswith("STATUS_OK:"):
        print("Code:", status.split(":", 1)[1])
        call(action="setStatus", id=order_id, status="6")
        break
    if status == "STATUS_CANCEL":
        print("Closed without a code; the frozen amount was refunded.")
        break
    time.sleep(5)
Node.js: cancel with retry when no code arrives javascript
const BASE = "https://tempsmsverify.com/api/stubs/handler_api.php";
const KEY = process.env.TEMPSMSVERIFY_API_KEY;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function call(params) {
  const qs = new URLSearchParams({ api_key: KEY, ...params });
  const res = await fetch(BASE + "?" + qs);
  return (await res.text()).trim();
}

async function cancel(id) {
  for (let i = 0; i < 30; i++) {
    const r = await call({ action: "setStatus", id, status: "8" });
    if (r !== "EARLY_CANCEL_DENIED") return r; // ACCESS_CANCEL or BAD_STATUS
    await sleep(10000); // cancel unlocks after a short delay
  }
}

const number = await call({ action: "getNumber", service: "wa", country: "187" });
if (!number.startsWith("ACCESS_NUMBER:")) throw new Error(number);
const [, id] = number.split(":");
console.log(await cancel(id));
REST v1: the same activation for new code bash
curl -X POST "https://tempsmsverify.com/api/v1/orders" \
  -H "X-Api-Key: $API_KEY" -H "Content-Type: application/json" \
  -d '{"product": "telegram", "country": "usa"}'

curl "https://tempsmsverify.com/api/v1/orders/123456" -H "X-Api-Key: $API_KEY"
curl -X POST "https://tempsmsverify.com/api/v1/orders/123456/finish" -H "X-Api-Key: $API_KEY"

FAQ

FAQ

Is the SMS-Activate API still working?

No. The closure notice on sms-activate.org says the service ceased operations on December 22, 2025, and withdrawals closed on February 5, 2026. Requests to the old API will not return numbers. To keep your code, point it at a provider that serves the same handler_api protocol, such as TempSMSVerify.

Do I need to rewrite my SMS-Activate bot?

Usually not. For the core flow, change the base URL to https://tempsmsverify.com/api/stubs/handler_api.php and set the new api_key. Then check country ids with getCountries, because country=0 means USA on TempSMSVerify, and confirm that your bot uses only the eight supported actions. Anything else returns BAD_ACTION.

Why does country=0 return a US number?

In the original SMS-Activate protocol, 0 meant Russia. On TempSMSVerify, 0 or a missing country maps to the platform default, USA (id 187). If your code relied on 0 for Russia, send the explicit id from getCountries instead. Countries can also be passed by name or two-letter ISO code.

Are prices returned in rubles or dollars?

In US dollars. getBalance returns ACCESS_BALANCE with the USD amount, and getPrices returns cost in USD per country and service, with count for current stock. If your old code converted rubles, remove that step. Prices change with supply, so read them from getPrices rather than hard-coding them.

How do refunds work through the compatible API?

You never request them. getNumber only freezes the price, and the charge happens when the SMS arrives. If nothing arrives within up to 20 minutes, the order times out and the amount returns automatically. To get it back sooner, send setStatus 8 after about 2 minutes; earlier returns EARLY_CANCEL_DENIED.

Can I request a second SMS on the same number?

Yes, with setStatus 3 after the first code has arrived and while the number is active. There is no extra cost. Until the new message arrives, getStatus returns STATUS_WAIT_RETRY with the last code, then STATUS_OK with the new one. Before the first code, status 3 returns BAD_STATUS.

Are rentals supported?

No. TempSMSVerify supports one-time activations only, so rental actions and other extensions return BAD_ACTION. If your workflow depends on long-term numbers, you will need a provider that offers rentals for that part. For activations, the compatible endpoint and the REST API cover the full flow.

What rate limits apply?

Each account can buy up to 500 numbers per minute across both APIs. On the compatible endpoint, going over the limit returns NO_NUMBERS. On REST, POST /orders is also limited to 500 requests per minute per IP and resend to 30 per minute, returning HTTP 429. Poll status every 3 to 5 seconds.

Last updated:

Ready when you are

Get your verification code now

Create an account, top up and pick a number. Pay only if the code arrives.