Хаб миграции SMS-Activate 2026: Контрольный список для разработчиков, сопоставление API и сравнение возврата средств
If you are still carrying handler_api.php calls in your repo pointed at sms-activate.org, this is the hub you open at 9pm on a Friday and close with a working integration before midnight. Real endpoint mapping, real code diffs, refund policy comparison, and the gotchas that will bite you between the line you change and the alert that wakes you up on Monday morning.
Почему существует этот playbook
Большинство постов «SMS-Activate умер, вот список альтернатив» пропускают часть, которая действительно сжигает часы разработчиков: код. Регистрация нового провайдера занимает пять минут. Переписывание интеграции, которая тихо работает в продакшене уже годы, с необработанными пограничными случаями и отслеживанием затрат, прикрепленным к ней, занимает больше времени, чем вы думаете.
После того, как мы выпустили наш слой совместимости в январе 2026 года, мы начали получать одни и те же три вопроса от каждой команды, которая его попробовала:
- Какие конечные точки отображаются чисто, а какие требуют ручных изменений?
- Как сохранить отслеживание затрат и поток возврата без переписывания?
- Что ломается молча и появляется через неделю в виде биллингового сюрприза?
Этот playbook отвечает на эти три вопроса по порядку и дает вам готовый к использованию код, который вы можете проверить перед запуском.
48-часовой обзор: что на самом деле произошло
SMS-Activate перестал работать 29 декабря 2025 года. Не было баннера планового обслуживания, инструмента миграции и публичного уведомления. Пользователи, пытающиеся войти в систему, столкнулись с одной страницей, сообщающей, что сервис закрыт навсегда. API начал возвращать сбросы соединений на всех конечных точках в течение нескольких часов.
Быстро произошло три вещи:
- Балансы на панели управления стали недоступны. Отчеты на субреддите
r/smsи официальных каналах Telegram описывали балансы от 20 до нескольких тысяч долларов, замороженные без пути восстановления. - Работающие интеграции сломались жестко. Любой сервис, опрашивающий
handler_api.php, начал получать ошибки HTTP-соединения, которые в большинстве трекеров ошибок означают «сработал предохранитель, оповестите команду». - Окно миграции закрылось в течение дней, а не недель. В течение 72 часов у каждого оставшегося провайдера появилась очередь. 5sim, SMSPVA и SMS-MAN все признали нагрузку на мощность. VerifySMS справился, потому что мы были самым маленьким в группе и имели запас мощности, но это было действительно близко.
Последствия все еще продолжаются. По состоянию на апрель 2026 года есть активные дела в малых претензиях в России и как минимум два координированных судебных иска сообщества, пытающихся вернуть замороженные балансы. Ничто из этого не помогает вашему коду, поэтому мы сосредоточимся на части, которую вы можете реально исправить.
Часть 1: Карта депекрации API
SMS-Activate поставлялся с одной публичной конечной точкой по адресу https://sms-activate.org/stubs/handler_api.php. Каждое действие было параметром строки запроса на этом URL. Таблица ниже сопоставляет каждое основное действие с его эквивалентом VerifySMS. Слой совместимости по адресу https://api.verifysms.app/compat/handler_api.php принимает точно такую же форму строки запроса.
| Действие SMS-Activate | Назначение | Уровень совместимости VerifySMS | Собственный API VerifySMS |
|---|---|---|---|
getBalance | Вернуть баланс в USD как текст | Работает без изменений. Возвращает ACCESS_BALANCE:X.YY | GET /v1/balance возвращает JSON |
getNumbersStatus | Доступность по странам | Работает. Возвращает устаревший формат карты | GET /v1/countries/availability |
getNumber | Аренда номера для сервиса | Работает. Возвращает ACCESS_NUMBER:id:+phone | POST /v1/rentals |
setStatus | Подтверждение или отмена аренды | Работает. Коды статуса 1/3/6/8 ведут себя идентично | POST /v1/rentals/{id}/status |
getStatus | Опрос за прибытием SMS | Работает. Возвращает STATUS_WAIT_CODE, STATUS_OK:CODE, STATUS_WAIT_RETRY | GET /v1/rentals/{id} |
getPrices | Получение таблицы цен | Работает. Возвращает цены VerifySMS в устаревшем формате JSON | GET /v1/prices |
getCountries | Карта кодов стран | Работает. Возвращает как устаревшие числовые идентификаторы, так и коды ISO-3166 | GET /v1/countries |
getTopCountriesByService | Лучшие страны на сервис | Возвращает данные VerifySMS в реальном времени вместо устаревших рейтингов SMS-Activate | GET /v1/services/{id}/top-countries |
Несколько менее используемых действий SMS-Activate не отображаются один в один. getRentServicesAndCountries и API долгосрочной аренды были специфичны для SMS-Activate и не имеют слоя совместимости. Если ваша интеграция использовала их, вам следует перейти на собственный конечный пункт VerifySMS для долгосрочной аренды по адресу POST /v1/rentals/long, который документирован отдельно.
Часть 2: Пошаговое руководство по миграции кода
Следующие фрагменты — это точная форма, протестированная против нашей собственной среды тестирования в январе. Я сохранил их намеренно скучными, чтобы вы могли прочитать их против вашего собственного кода без переключения контекста.
Python (requests)
Единственное необходимое изменение — базовый URL. Если вы уже обернули API в небольшой модуль клиента, разница заключается в одной строке.
import os
import requests
# ДО
# BASE_URL = "https://sms-activate.org/stubs/handler_api.php"
# ПОСЛЕ
BASE_URL = "https://api.verifysms.app/compat/handler_api.php"
API_KEY = os.environ["SMS_API_KEY"]
def get_number(service: str, country: int) -> tuple[str, str]:
resp = requests.get(BASE_URL, params={
"api_key": API_KEY,
"action": "getNumber",
"service": service,
"country": country,
}, timeout=30)
resp.raise_for_status()
# ACCESS_NUMBER:12345:+441234567890
status, rental_id, phone = resp.text.split(":", 2)
if status != "ACCESS_NUMBER":
raise RuntimeError(f"неожиданный ответ: {resp.text}")
return rental_id, phone
def wait_for_code(rental_id: str, deadline_seconds: int = 180) -> str:
import time
start = time.monotonic()
while time.monotonic() - start < deadline_seconds:
resp = requests.get(BASE_URL, params={
"api_key": API_KEY,
"action": "getStatus",
"id": rental_id,
}, timeout=15).text
if resp.startswith("STATUS_OK:"):
return resp.split(":", 1)[1]
time.sleep(4)
# Пометить как неиспользованный, чтобы получить возврат
requests.get(BASE_URL, params={
"api_key": API_KEY,
"action": "setStatus",
"status": 8,
"id": rental_id,
}, timeout=15)
raise TimeoutError(f"нет кода после {deadline_seconds}с")
Node.js (axios)
import axios from "axios";
// ДО
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// ПОСЛЕ
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";
const API_KEY = process.env.SMS_API_KEY;
export async function getNumber(service, country) {
const { data } = await axios.get(BASE_URL, {
params: { api_key: API_KEY, action: "getNumber", service, country },
timeout: 30_000,
});
const [status, rentalId, phone] = data.split(":");
if (status !== "ACCESS_NUMBER") {
throw new Error(`неожиданный ответ: ${data}`);
}
return { rentalId, phone };
}
export async function waitForCode(rentalId, deadlineMs = 180_000) {
const start = Date.now();
while (Date.now() - start < deadlineMs) {
const { data } = await axios.get(BASE_URL, {
params: { api_key: API_KEY, action: "getStatus", id: rentalId },
timeout: 15_000,
});
if (data.startsWith("STATUS_OK:")) return data.split(":")[1];
await new Promise((r) => setTimeout(r, 4000));
}
await axios.get(BASE_URL, {
params: { api_key: API_KEY, action: "setStatus", status: 8, id: rentalId },
timeout: 15_000,
});
throw new Error(`нет кода после {deadlineMs}мс`);
}
PHP (curl)
<?php
// ДО
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// ПОСЛЕ
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";
function sms_call(array $params): string {
$params["api_key"] = getenv("SMS_API_KEY");
$url = BASE_URL . "?" . http_build_query($params);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$body = curl_exec($ch);
curl_close($ch);
return $body;
}
function get_number(string $service, int $country): array {
$resp = sms_call(["action" => "getNumber", "service" => $service, "country" => $country]);
[$status, $id, $phone] = explode(":", $resp, 3);
if ($status !== "ACCESS_NUMBER") {
throw new RuntimeException("неожиданный: $resp");
}
return ["id" => $id, "phone" => $phone];
}
Go (net/http)
package sms
import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
)
// ДО
// const baseURL = "https://sms-activate.org/stubs/handler_api.php"
// ПОСЛЕ
const baseURL = "https://api.verifysms.app/compat/handler_api.php"
func call(params url.Values) (string, error) {
params.Set("api_key", os.Getenv("SMS_API_KEY"))
req, _ := http.NewRequest("GET", baseURL+"?"+params.Encode(), nil)
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return string(body), nil
}
func GetNumber(service string, country int) (id, phone string, err error) {
body, err := call(url.Values{
"action": {"getNumber"},
"service": {service},
"country": {fmt.Sprint(country)},
})
if err != nil {
return "", "", err
}
parts := strings.SplitN(body, ":", 3)
if len(parts) != 3 || parts[0] != "ACCESS_NUMBER" {
return "", "", errors.New("неожиданный: " + body)
}
return parts[1], parts[2], nil
}
Пример на Go намеренно написан без сторонних клиентов, чтобы вы могли вставить его в минимальный сервис без добавления зависимостей. Такая же схема действует на каждом другом языке: замените URL, сохраните остальное и позвольте вашей существующей обработке ошибок взять на себя.
Часть 3: Подводные камни, которые вас сожгут
Это места, где слой совместимости верно соответствует причудам SMS-Activate, но сами причуды вас удивят, если вы не трогали этот код уже давно.
Идентификаторы стран не являются кодами ISO
SMS-Activate пронумеровал страны в своем собственном порядке: Россия была 0, США была 187, Индонезия была 6 и т. д. Если в вашей интеграции есть эти магические числа, закодированные жестко, они все еще работают на слое совместимости. Если вы пишете новый код, предпочтительнее использовать форму ISO-3166 alpha-2 (RU, US, ID), которую слой совместимости также принимает. Не смешивайте оба стиля в одном месте вызова, потому что будущая отладка будет болезненной.
Код статуса 3 против 6
Действие setStatus код 3 означал «запросить еще один SMS» в мире SMS-Activate, а код 6 означал «принять код как действительный». Эти два кода легко поменять местами, и у них есть противоположные результаты биллинга: 3 оставляет вас с биллингом, 6 подтверждает успешную верификацию. Слой совместимости ведет себя одинаково. Ищите в вашем коде setStatus и убедитесь, что ветка, которая принимает код 6, выполняется только после того, как вы уверены, что верификация прошла успешно.
Таймауты и предохранители
SMS-Activate под нагрузкой иногда возвращал 200 с пустым телом вместо ошибки HTTP. Защитные клиенты оборачивают вызов в таймаут и обрабатывают пустое тело как сигнал повтора. VerifySMS никогда не возвращает пустое тело на слое совместимости. Если ваш клиент по-прежнему обрабатывает пустое тело как повтор, он сожжет бюджет на нестабильной сети, потому что повтор попадет в другой идентификатор аренды. Безопаснее проверять известные префиксы ответа (ACCESS_, STATUS_, BAD_) и обрабатывать все остальное как жесткий сбой, а не как транзитный.
Ограничения скорости перемещаются от ключа к IP
Ограничения скорости SMS-Activate были привязаны к токену API. Ограничения скорости VerifySMS привязаны к комбинации токена API и адреса исходного IP, потому что мы видим много злоупотреблений со стороны скриптов-скраперов, которые делятся одним ключом между ботнетом. Для нормального трафика продакшена с одного сервера или пула с балансировкой нагрузки это незаметно. Если вы запускаете распределенные задания CI, которые все используют один ключ этапа, вы можете увидеть 429 при первом запуске всего флота вместе. Исправление состоит в том, чтобы позволить канонике запуститься с одного узла в течение дня, прежде чем развернуть.
Тайминг возврата кажется мгновенным, потому что он действительно мгновенный
Это не ловушка, а приятный сюрприз. Там, где возвраты SMS-Activate занимали несколько часов, чтобы появиться в вашем балансе, возвраты VerifySMS появляются в течение 60 секунд. Если ваш трекер затрат читает баланс по расписанию, он зарегистрирует возврат как кредит, который старая система пропустила бы. Дашборды сверки иногда помечают это как аномалию в первый день.
Часть 4: Реальность проверки цен
До закрытия SMS-Activate был нижней границей рынка. Российские номера стоили от 0,03 до 0,05 долларов за верификацию, а покупатели больших объемов платили еще меньше. Эта граница исчезла. Вот где находятся оставшиеся поставщики на апрель 2026 года для наиболее распространенных услуг, взятые из публичной страницы ценообразования каждого поставщика на :
| Сервис | 5sim | TextVerified | SMSPVA | SMS-MAN | VerifySMS |
|---|---|---|---|---|---|
| WhatsApp / Россия | $0.014 | — | $0.05 | $0.035 | $0.10 |
| WhatsApp / США | $0.27 | $0.25 | $0.28 | $0.22 | $0.18 |
| Telegram / Россия | $0.016 | — | $0.05 | $0.04 | $0.10 |
| Telegram / США | $0.35 | $0.40 | $0.38 | $0.30 | $0.20 |
| Google / Индонезия | $0.07 | — | $0.08 | $0.06 | $0.10 |
Шаблон прост: 5sim и SMS-MAN выигрывают по самым низким ценам на российские номера, TextVerified является премиальным уровнем США, а VerifySMS находится в середине с базовой ценой 0,10 долларов за все, кроме самых дорогих номеров США без VoIP. Если ваш бюджет был настроен на нижние цены SMS-Activate, ожидайте платить в два-пять раз больше за верификацию, независимо от того, какой заменитель вы выберете.
Два замечания к этой таблице. Во-первых, каждый поставщик (включая VerifySMS) повышает и понижает индивидуальные цены на страны в ответ на затраты операторов, поэтому подтвердите текущую цену в вашем собственном аккаунте, прежде чем фиксировать бюджет. Во-вторых, эффективная цена за успешную верификацию зависит от коэффициента возврата. Поставщик с ценой 0,08 долларов и коэффициентом успеха 70% стоит вам больше за успех, чем поставщик с ценой 0,10 долларов и автоматическими возвратами и коэффициентом успеха 90%.
Часть 5: Контрольный список миграции из 10 шагов
Это реальная последовательность, которую мы прошли с нашими собственными пользователями в январе. Она предполагает одного разработчика с доступом к репозиторию, один сервис в производстве и среду тестирования. Масштабируйте проценты канарейки, если вы запускаете несколько сервисов или монолит.
- Инвентаризируйте все вызовы. Запустите
git grep -n 'sms-activate\.org\|handler_api\.php\|getNumber\|setStatus'и перечислите все файлы, которые попадают в старый API. Если вы найдете более дюжины, выберите модуль-обертку и централизуйте вызовы сначала, прежде чем мигрировать. - Получите ключ API VerifySMS. Зарегистрируйтесь, добавьте небольшую сумму баланса и сгенерируйте ограниченный ключ для тестирования. Храните производственный ключ вне репозитория.
- Измените базовый URL. Замените хост SMS-Activate на
api.verifysms.app/compat/handler_api.php. Не меняйте строку запроса. Зафиксируйте это отдельно, чтобы диф был чистым. - Запустите существующие тесты. Если тесты попадают в реальный API, укажите им на тестовую среду и наблюдайте за несоответствиями формы. Если они имитируют API, запустите их против живого тестового эндпоинта, а также чтобы поймать дрейф контракта.
- Подтвердите идентификаторы стран. Просмотрите свой код для констант стран. Если вы используете устаревшие числовые идентификаторы, они все еще работают. Если у вас есть возможность, замените их кодами ISO-3166, потому что следующий разработчик, который будет работать с этим файлом, будет вам благодарен.
- Настройте претензии на возврат. Подтвердите, что ваш путь таймаута вызывает
setStatusсоstatus=8. Без этого вы все равно получите возвраты (мы автоматически возвращаем истекшие аренды), но ваш отслеживатель затрат будет отставать от реальности. - Обновите отслеживатель затрат. Считывайте стоимость из заголовка ответа
X-VerifySMS-Costвместо того, чтобы парсить ее из таблицы цен. Это единственное изменение делает вашу финансовую панель точной до цента. - Мониторинг. Добавьте оповещения о коэффициенте успеха, задержке p95 и коэффициенте возврата против вашего существующего базового уровня. Выберите пороги, которые вы можете защитить, а не те, которые, по вашему мнению, будут "нормальными".
- Канарейка 5 процентов в течение 24 часов. Направьте небольшой кусок производственного трафика через новый эндпоинт. Наблюдайте за панелью управления, а не только за оповещениями.
- Переключите остальное. Как только окно канарейки будет чистым, переместите оставшиеся 95 процентов и оставьте старый клиентский код закомментированным (не удаленным) в течение одного цикла выпуска, чтобы у вас был быстрый откат.
Удалите старый код в следующем выпуске после этого. Не оставляйте мертвые вызовы вокруг более недели, потому что следующий человек, который будет работать с модулем, вставит их обратно в новую интеграцию по ошибке.
Часто задаваемые вопросы
Является ли слой совместимости SMS-Activate реальным API или просто заглушкой?
Это реальный эндпоинт на api.verifysms.app/compat/handler_api.php, который принимает каждое основное действие из публичной документации SMS-Activate: getBalance, getNumber, getStatus, setStatus, getPrices и getCountries. Запросы проксируются к нашему родному API под капотом, поэтому вы получаете цены VerifySMS, покрытие и поведение возврата без изменений кода на вашей стороне.
Будет ли мой старый ключ API работать?
Нет. Ключи API SMS-Activate перестали аутентифицироваться в день закрытия сервиса. Вам нужен новый ключ от VerifySMS. Зарегистрируйтесь, добавьте небольшую сумму баланса и сгенерируйте ключ из панели управления. Формат ключа идентичен по длине, поэтому вы можете вставить его в ту же переменную окружения.
Как работают возвраты по сравнению с SMS-Activate?
SMS-Activate требовал от вас вызвать setStatus с кодом статуса 8 в течение 20 минут, чтобы отметить номер как неиспользованный, и возвраты обрабатывались вручную в течение нескольких часов. VerifySMS принимает тот же вызов setStatus и возвращает полную сумму на ваш баланс в течение 60 секунд. Если вы забудете вызвать setStatus совсем, наша система все равно автоматически возвращает любой номер, который никогда не получал SMS после истечения срока аренды.
Какие страны поддерживаются?
VerifySMS охватывает более 200 стран. Каждая страна, которую предлагал SMS-Activate, доступна на VerifySMS, включая Россию, Индонезию, Вьетнам, Нигерию и США. Вы можете сохранить свою существующую карту идентификаторов стран или мигрировать на альфа-2 коды ISO-3166, когда захотите.
Является ли цена одинаковой?
Нет. Цены SMS-Activate на нижнем рынке в размере 0,03-0,05 долларов за верификацию для российских номеров исчезли с открытого рынка. Текущие рыночные цены варьируются от 0,10 долларов за общие услуги до 0,25 долларов за номера США без VoIP на более строгих платформах. VerifySMS взимает 0,10 долларов в качестве базовой цены и публикует цены для каждой страны на панели управления.
Нужно ли мне изменить логику опроса?
Нет. Вызов getStatus возвращает STATUS_WAIT_CODE и STATUS_OK в том же формате, что и SMS-Activate. Интервалы опроса в 3-5 секунд все еще работают. Единственное новое поведение заключается в том, что VerifySMS также exposes URL веб-хука в панели управления, поэтому вы можете停止 опрос совсем, если предпочитаете событийно-ориентированный поток.
Что произойдет, если слой совместимости когда-нибудь будет удален?
Слой совместимости считается постоянным публичным интерфейсом. Если мы когда-либо изменим его поведение, мы опубликуем минимальный шестимесячный период деprecation с полным примечанием миграции. Родной JSON API VerifySMS также документирован, поэтому вы можете мигрировать со слоя совместимости в любое время, когда это имеет смысл.
Как я могу протестировать без затрат?
Панель управления VerifySMS exposes тестовый режим, который возвращает смоделированные телефонные номера и тестовые SMS-коды без уменьшения вашего баланса. Переключите флаг тестового режима в панели управления или отправьте заголовок X-Sandbox-Mode с любым запросом, чтобы отработать пути вашего кода перед запуском.
Могу ли я мигрировать из других сервисов?
Да. Этот плейбук написан вокруг API SMS-Activate, потому что именно там живет большинство застрявшего кода, но тот же контрольный список применяется к миграциям из 5sim, SMS-MAN или любого другого сервиса, совместимого с handler_api. Слой совместимости распознает параметры handler_api.php, независимо от того, какой сервис вы ранее вызывали.
Сколько времени занимает реальная миграция?
Для интеграции с одним сервисом и несколькими десятками вызовов планируйте два-четыре часа сосредоточенной работы плюс 24-часовой окно канарейки, прежде чем вы переключите весь трафик. Более крупные миграции с пользовательским обработкой ошибок, аналитикой и повторными попытками могут занять больше времени, но обычно укладываются в один рабочий день.
Потеряю ли я свои исторические данные?
История верификаций SMS-Activate ушла в офлайн, когда сервис закрылся, и не подлежит восстановлению. VerifySMS поддерживает полный аудиторский журнал каждой попытки верификации на вашем аккаунте в течение 12 месяцев, доступный из панели управления и через расширение /compat/handler_api.php?action=getHistory.
Влияет ли это на мою позицию GDPR или соответствие?
VerifySMS зарегистрирован в Великобритании и следует за UK GDPR. Мы публикуем нашу политику хранения данных, суб-процессоров и DPA на странице конфиденциальности. Если ваша предыдущая настройка требовала DPA с SMS-Activate, свяжитесь с нами, и мы подпишем то же соглашение в течение одного рабочего дня.
Следующие шаги
Если вы прочитали это, у вас уже есть все необходимое. Начните с инвентаризации, получите базовый URL-замену перед коллегой для обзора и запустите канарейку на ночь. Плейбук небольшой по замыслу; сложная часть - дисциплина остановиться после окна канарейки, а не переключить все в одном коммите.
Связанное чтение на остальной части сайта:
- Состояние SMS-верификации 2026 — полный независимый бенчмарк из 8 сервисов с данными о ценах, трафике и политике возврата.
- Лучшие альтернативы SMS-Activate в 2026 — Полное руководство — руководство для не-разработчиков по вариантам замены.
- VerifySMS vs SMS-Activate — полное сравнение — сравнение бок о бок для команд, которые все еще оценивают.
- Руководство по интеграции API SMS-верификации — родной VerifySMS API-тур для тех пор, когда вы будете готовы перейти со слоя совместимости.
Готовы ли вы сократить миграцию до одного вечера?
Создайте ключ API VerifySMS →Тестовый режим включен · Гарантия авто-возврата · 200+ стран · Слой совместимости SMS-Activate на /compat/handler_api.php
Next steps
If you have read this far, you already have the pieces you need. Start with the inventory step, get the base URL swap in front of a teammate for review, and run the canary overnight. The playbook is small on purpose; the hard part is the discipline to stop after the canary window instead of cutting everything over in one commit.
Related reading on the rest of the site:
- State of SMS Verification 2026 — full independent benchmark of 8 services with pricing, traffic, and refund policy data.
- Лучшие альтернативы SMS-Activate в 2026 — Полное руководство — non-developer guide to the replacement options.
- VerifySMS vs SMS-Activate — полное сравнение — side-by-side comparison for teams still evaluating.
- SMS Verification API Integration Guide — native VerifySMS API walkthrough for when you are ready to move off the compat layer.
Ready to cut the migration to a single evening?
Create a VerifySMS API key →Sandbox mode included · Auto-refund guarantee · 200+ countries · SMS-Activate compat layer on /compat/handler_api.php