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
Create an account and top up
Sign up and add at least $5. API access unlocks after your first confirmed top-up.
- 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
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
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
Confirm service codes
Call getServicesList. Standard codes such as wa or tg work, and so do names such as whatsapp.
- 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.
| Item | SMS-Activate (before) | TempSMSVerify (after) |
|---|---|---|
| Endpoint | SMS-Activate host + /stubs/handler_api.php | https://tempsmsverify.com/api/stubs/handler_api.php |
| Authentication | api_key parameter | api_key parameter (also X-Api-Key or Authorization: Bearer) |
| country=0 or missing | Russia | USA (id 187), the platform default |
| Country ids | SMS-Activate numbering | Same 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 |
| service | Protocol codes (wa, tg, go...) | Same codes, or the service name (whatsapp) |
| Currency | Account currency | USD |
| Unsupported actions | Rentals and other extensions | Return 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.
| action | Parameters | Success response |
|---|---|---|
| getBalance | none | ACCESS_BALANCE:12.50 |
| getNumber | service, country (optional), server (optional, 1 or 2) | ACCESS_NUMBER:{id}:{phone} |
| getStatus | id | STATUS_WAIT_CODE, STATUS_OK:{code}, STATUS_WAIT_RETRY:{last code}, STATUS_CANCEL |
| setStatus | id, status (1, 3, 6 or 8) | ACCESS_READY, ACCESS_RETRY_GET, ACCESS_ACTIVATION, ACCESS_CANCEL |
| getPrices | country, service (both optional) | JSON: {"187": {"wa": {"cost": price, "count": stock}}} |
| getNumbersStatus | country (optional) | JSON: {"wa_0": stock, "tg_0": stock} |
| getServicesList | none | JSON: {"status": "success", "services": [{"code": "wa", "name": "whatsapp"}]} |
| getCountries | none | JSON: {"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.
| Code | When you see it | What to do |
|---|---|---|
| NO_KEY / BAD_KEY | Missing, invalid or revoked api_key | Generate a key in the dashboard and update your config |
| BAD_ACTION | Action not implemented | Use one of the eight supported actions |
| BAD_SERVICE | Unknown service code or name | Check getServicesList |
| WRONG_COUNTRY | Unknown or unavailable country | Check getCountries |
| NO_BALANCE | Balance too low for the frozen price | Top up, then retry |
| NO_NUMBERS | No stock, or over 500 purchases per minute | Try another country or slow down |
| NO_ACTIVATION | Unknown order id | Use the id from ACCESS_NUMBER |
| BAD_STATUS | Status not allowed now, such as cancel after an SMS | Finish with status 6 instead |
| EARLY_CANCEL_DENIED | Cancel before about 2 minutes | Wait and retry setStatus 8 |
| SERVER_ERROR | Upstream or internal failure | Retry 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
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_ACTIVATIONimport 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)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));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.