API SmsEmpire
Покупайте SMS- и email-верификации из своего кода, списывая с баланса, со скидкой от цены сайта.
- →Скидка на каждую покупку через API
- →Подписанные вебхуки сразу при получении кода — без опроса
- →Идемпотентные покупки: повтор после таймаута безопасен
Быстрый старт
Всё находится по адресу https://smsempire.com/api/v1. Аутентификация — ключ в виде bearer-токена. Только со стороны сервера: API не отправляет CORS-заголовки, а ключ, использованный в браузере, — это утёкший ключ.
# 1. Create a key in your dashboard, then:
export SE_KEY="se_live_XXXXXXXXXXXX_..."
# 2. What will this cost?
curl -s https://smsempire.com/api/v1/prices?service=tg \
-H "Authorization: Bearer $SE_KEY"
# 3. Buy a number. The Idempotency-Key is required.
curl -s https://smsempire.com/api/v1/activations \
-H "Authorization: Bearer $SE_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"service":"tg","max_price":"0.35"}'
# 4. Collect the code (or let the webhook push it to you).
curl -s https://smsempire.com/api/v1/activations/90210 \
-H "Authorization: Bearer $SE_KEY"Соглашения
- Деньги — десятичная строка ("0.28000"), никогда не число JSON. Разбирайте её типом decimal, а не float.
- Время — RFC3339 в UTC.
- Ошибки всегда имеют вид {"error": {"code", "message", "request_id"}}. Ветвитесь по code; message может измениться.
- Указывайте request_id в обращении в поддержку: он ведёт прямо к этому запросу в наших логах.
- Лимиты возвращаются в заголовках X-RateLimit-*, а 429 несёт Retry-After.
Идемпотентность (обязательна при покупке)
Каждая покупка требует заголовок Idempotency-Key — лучше всего UUID. Повторное использование вернёт исходный ответ с Idempotent-Replay: true, а не купит второй номер.
Это нужно ровно для одной ситуации: ваш POST завершился таймаутом, и вы не знаете, прошёл ли он. Без ключа повтор купит дважды, а отказ от повтора потеряет уже оплаченный номер. С ключом просто повторяйте.
Тот же ключ с другим телом даёт 422, а не тихий повтор: такое сочетание — ошибка на вашей стороне, и скрыть её значило бы выдать вам неверный результат.
max_price
Необязательный, но настоятельно рекомендуемый параметр. Если страна не указана, мы пробуем несколько — по порядку качества и с разными ценами; max_price проверяется для каждого кандидата, поэтому с вас никогда не спишут больше согласованного. Если не подходит ничего, вы получите 409 PRICE_ABOVE_MAX с текущей ценой, и списания не будет.
Вебхуки
Зарегистрируйте один HTTPS-эндпоинт в панели. Мы отправляем POST сразу при получении кода. Доставка «хотя бы один раз», с повторами по нарастающей примерно два с половиной часа.
POST /your/webhook
X-SmsEmpire-Event: activation.code_received
X-SmsEmpire-Delivery: evt_01j8z... <- deduplicate on this
X-SmsEmpire-Timestamp: 1765432991
X-SmsEmpire-Signature: v1=3a7f...
{
"id": "evt_01j8z...",
"type": "activation.code_received",
"created_at": "2026-08-14T12:03:11Z",
"data": {
"activation_id": 90210,
"service": "tg",
"country": "2",
"number": "+79991234567",
"code": "483920",
"status": "received",
"price": "0.28000"
}
}Требования к эндпоинту: HTTPS на порту 443, резолвящийся в публичный адрес. Мы не следуем редиректам и отклоняем приватные, loopback- и облачные metadata-адреса.
Проверка вебхука
Подпись — HMAC-SHA256(секрет, "<timestamp>.<сырое тело>") в hex. Четыре типичные ошибки, по частоте:
- Подписывать пересериализованный JSON вместо сырых байтов. Пересериализация меняет байты, и подпись никогда не сойдётся.
- Игнорировать метку времени. Без проверки ±300 с перехваченную доставку можно повторять вам бесконечно.
- Сравнивать через == вместо сравнения за постоянное время.
- Не дедуплицировать по X-SmsEmpire-Delivery. Мы гарантируем «хотя бы один раз», а не «ровно один раз».
import hmac, hashlib, time
from flask import request, abort
SECRET = "whsec_..." # from your dashboard
TOLERANCE = 300 # seconds
def verify(request):
raw = request.get_data() # RAW bytes, before json parsing
ts = request.headers.get("X-SmsEmpire-Timestamp", "")
sig_header = request.headers.get("X-SmsEmpire-Signature", "")
if abs(time.time() - int(ts)) > TOLERANCE: # blocks replays
abort(400)
expected = hmac.new(SECRET.encode(),
f"{ts}.".encode() + raw,
hashlib.sha256).hexdigest()
# More than one v1= during a secret rotation; any match is valid.
sigs = [p.split("=", 1)[1] for p in sig_header.split(",") if p.startswith("v1=")]
if not any(hmac.compare_digest(expected, s) for s in sigs):
abort(400)const crypto = require("crypto");
// express.raw({type: "application/json"}) — you need the RAW body, not the parsed one.
function verify(req, secret) {
const ts = req.get("X-SmsEmpire-Timestamp") || "";
const header = req.get("X-SmsEmpire-Signature") || "";
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(Buffer.concat([Buffer.from(ts + "."), req.body]))
.digest("hex");
return header
.split(",")
.filter((p) => p.startsWith("v1="))
.some((p) =>
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(p.slice(3))),
);
}При ротации прежний секрет остаётся действительным 24 часа, и в этом окне каждую доставку подписывают оба — так что выкатывать можно без простоя.
Ошибки
| code | Что делать |
|---|---|
| INVALID_API_KEY | Ключ отсутствует, повреждён или неизвестен. Не повторяйте. |
| KEY_REVOKED / KEY_EXPIRED | Создайте новый ключ. Не повторяйте. |
| INSUFFICIENT_SCOPE | Этот ключ создан без права записи. |
| API_ACCESS_DENIED | API отключён для этого аккаунта, либо вы ещё не пополняли баланс. |
| API_DISABLED | API временно отключён для всех. Повторите позже. |
| IDEMPOTENCY_KEY_REQUIRED | Отправляйте заголовок Idempotency-Key при каждой покупке. |
| IDEMPOTENCY_KEY_REUSED | Тот же ключ, другое тело. Исправьте клиент. |
| IDEMPOTENCY_IN_PROGRESS | Первая попытка ещё выполняется. Повторите через секунду. |
| PRICE_ABOVE_MAX | Цена превысила ваш max_price. Запросите цену заново и решите. |
| INSUFFICIENT_BALANCE | Пополните баланс. До этого не повторяйте. |
| COUNTRY_OUT_OF_STOCK | Сейчас номеров нет. Повторите или выберите другую страну. |
| PROVIDER_CAPACITY | Временно не можем выдать эту страну. Повторите позже. |
| CONCURRENCY_LIMIT | Слишком много активаций в ожидании. Завершите или отмените часть. |
| SPEND_LIMIT | Достигнут часовой лимит трат. Повторите после окончания окна. |
| RATE_LIMITED | Снизьте темп. Соблюдайте Retry-After. |
| VALIDATION_ERROR | Тело запроса неверно. Не повторяйте без изменений. |
Жизненный цикл активации
Активация находится в pending, пока не придёт код (received), пока вы не отметите её использованной (finished) или пока её не отменят. Отмена возвращает ровно ту сумму, что была списана.
Ожидающая активация истекает через 20 минут после покупки. Мы отменяем её в сети и возвращаем вам полную сумму, как только отмена подтверждена, — вы никогда не платите за номер, на который ничего не пришло.
Есть льготный период около 120 секунд, прежде чем номер можно отменить; более ранняя отмена вернёт EARLY_CANCEL_DENIED с числом секунд ожидания.
Лимиты
У запросов на чтение, на запись и у общего числа запросов пользователя есть свой потолок в минуту, плюс необязательные лимиты на число активаций в ожидании, часовые траты и число активаций в час. Ваши текущие значения — в GET /api/v1/me, где null означает «без лимита». Нужно поднять — напишите в поддержку: это настройки аккаунта, а не константы в коде.
Полный справочник эндпоинтов
Интерактивный документ OpenAPI 3.1, всегда генерируемый из работающего кода.
Открыть справочник