SmsEmpire

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. Четыре типичные ошибки, по частоте:

  1. Подписывать пересериализованный JSON вместо сырых байтов. Пересериализация меняет байты, и подпись никогда не сойдётся.
  2. Игнорировать метку времени. Без проверки ±300 с перехваченную доставку можно повторять вам бесконечно.
  3. Сравнивать через == вместо сравнения за постоянное время.
  4. Не дедуплицировать по 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_DENIEDAPI отключён для этого аккаунта, либо вы ещё не пополняли баланс.
API_DISABLEDAPI временно отключён для всех. Повторите позже.
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, всегда генерируемый из работающего кода.

Открыть справочник