API SMSCode для разработчиков: полное руководство по интеграции

API SMSCode для разработчиков: полное руководство по интеграции

Если вам нужно автоматизировать SMS-верификацию — будь то тестирование собственного продукта, массовая регистрация аккаунтов или интеграция верификации в бизнес-процесс — ручной интерфейс быстро становится узким местом. Именно для таких задач существует API SMSCode.

Это руководство охватывает все ключевые аспекты работы с API: от получения ключа до написания полноценного клиента с обработкой ошибок и реальными примерами кода на Python и TypeScript.

TL;DR: API SMSCode — REST API с Bearer-аутентификацией. Основной цикл: получить список номеров → заказать номер → опросить входящие SMS → отменить заказ при необходимости. Полная документация в /docs.

Базовые концепции API

Аутентификация

API использует Bearer-токен аутентификацию. Токен генерируется в настройках аккаунта SMSCode — нужна только регистрация по email.

Authorization: Bearer ваш_api_токен

Храните токен в переменных окружения — никогда не коммитьте его в репозиторий и не вставляйте в исходный код напрямую. Используйте .env файлы или менеджеры секретов (AWS Secrets Manager, HashiCorp Vault, GitHub Secrets).

Базовый URL

Все внешние запросы идут на:

https://smscode.gg/v1/

Формат ответов

API возвращает JSON. Успешный ответ всегда содержит success: true и объект data:

{
  "success": true,
  "data": { ... }
}

При ошибке:

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Недостаточно средств на балансе"
  }
}

Жизненный цикл заказа

Понимание состояний заказа критически важно для корректной интеграции:

СтатусЗначениеДействие
waitingОжидаем SMSПродолжаем polling
receivedSMS полученоИзвлечь код из sms.text
cancelledЗаказ отменёнБаланс возвращён
expiredСессия истеклаБаланс возвращён автоматически

Основные эндпоинты

Каталог номеров

Получить доступные страны и сервисы с ценами и наличием:

GET /v1/catalog

Фильтрация по конкретному сервису:

GET /v1/catalog?service=telegram

Ответ содержит список доступных комбинаций страна/сервис с ценой (в USD-центах), количеством доступных номеров и временем последнего обновления. Используйте этот эндпоинт для динамического выбора страны с наилучшим соотношением цены и доступности.

Заказ номера

POST /v1/orders
Content-Type: application/json

{
  "service": "telegram",
  "country": "india"
}

Ответ:

{
  "success": true,
  "data": {
    "order_id": "ord_abc123",
    "phone": "+919876543210",
    "expires_at": "2026-03-16T12:15:00Z",
    "status": "waiting"
  }
}

Поле phone — номер в международном формате, который нужно ввести на целевом сервисе. Поле expires_at — крайнее время ожидания SMS. После него баланс вернётся автоматически.

Получение статуса и SMS

После того как ввели номер на целевом сервисе — опрашивайте статус заказа:

GET /v1/orders/{order_id}

Пока SMS не пришло:

{
  "success": true,
  "data": {
    "order_id": "ord_abc123",
    "status": "waiting",
    "sms": null
  }
}

Когда SMS получено:

{
  "success": true,
  "data": {
    "order_id": "ord_abc123",
    "status": "received",
    "sms": {
      "text": "Your Telegram code: 12345",
      "received_at": "2026-03-16T12:11:23Z"
    }
  }
}

Отмена заказа

Если SMS не нужно или верификация не удалась — отменяйте заказ явно:

DELETE /v1/orders/{order_id}

При отмене до получения SMS баланс возвращается автоматически. Всегда вызывайте этот эндпоинт при неудаче — это хорошая практика, даже если сессия в любом случае истечёт.

Баланс аккаунта

GET /v1/balance
{
  "success": true,
  "data": {
    "balance": 1250,
    "currency": "USD"
  }
}

Полезно для мониторинга: добавьте алёрт, если баланс упадёт ниже определённого порога, чтобы не прерывать автоматические процессы.

Практическая реализация

Python-клиент (с httpx)

Полноценный клиент с ожиданием SMS и обработкой таймаута:

import httpx
import time
import os

API_TOKEN = os.environ["SMSCODE_API_TOKEN"]
BASE_URL = "https://smscode.gg/v1"

headers = {"Authorization": f"Bearer {API_TOKEN}"}

def order_number(service: str, country: str) -> dict:
    resp = httpx.post(
        f"{BASE_URL}/orders",
        json={"service": service, "country": country},
        headers=headers,
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()["data"]

def wait_for_sms(order_id: str, timeout: int = 120) -> str | None:
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = httpx.get(
            f"{BASE_URL}/orders/{order_id}",
            headers=headers,
            timeout=10,
        )
        data = resp.json()["data"]
        if data["status"] == "received":
            return data["sms"]["text"]
        if data["status"] in ("cancelled", "expired"):
            return None
        time.sleep(3)  # Не опрашивайте чаще 3 секунд
    return None

def cancel_order(order_id: str):
    httpx.delete(
        f"{BASE_URL}/orders/{order_id}",
        headers=headers,
        timeout=10,
    )

def get_balance() -> float:
    resp = httpx.get(f"{BASE_URL}/balance", headers=headers, timeout=10)
    return resp.json()["data"]["balance"]

# Использование
def verify_telegram(country: str = "india") -> tuple[str, str] | None:
    """Возвращает (phone, sms_text) или None при неудаче."""
    order = order_number("telegram", country)
    phone = order["phone"]
    order_id = order["order_id"]

    print(f"Используйте номер: {phone}")
    # Здесь ваша логика: ввести номер на целевом сервисе

    sms_text = wait_for_sms(order_id)
    if sms_text:
        print(f"SMS получено: {sms_text}")
        return phone, sms_text
    else:
        cancel_order(order_id)
        print("SMS не получено, заказ отменён")
        return None

Node.js / TypeScript

import fetch from "node-fetch"; // или встроенный fetch в Node 18+

const API_TOKEN = process.env.SMSCODE_API_TOKEN!;
const BASE_URL = "https://smscode.gg/v1";

const headers = {
  Authorization: `Bearer ${API_TOKEN}`,
  "Content-Type": "application/json",
};

interface Order {
  order_id: string;
  phone: string;
  expires_at: string;
  status: "waiting" | "received" | "cancelled" | "expired";
  sms: { text: string; received_at: string } | null;
}

async function orderNumber(service: string, country: string): Promise<Order> {
  const res = await fetch(`${BASE_URL}/orders`, {
    method: "POST",
    headers,
    body: JSON.stringify({ service, country }),
  });
  const json = await res.json() as any;
  if (!json.success) throw new Error(json.error.message);
  return json.data;
}

async function waitForSms(
  orderId: string,
  timeoutMs = 120_000
): Promise<string | null> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await fetch(`${BASE_URL}/orders/${orderId}`, { headers });
    const json = await res.json() as any;
    const order: Order = json.data;
    if (order.status === "received") return order.sms!.text;
    if (order.status === "cancelled" || order.status === "expired") return null;
    await new Promise((r) => setTimeout(r, 3000));
  }
  return null;
}

async function cancelOrder(orderId: string): Promise<void> {
  await fetch(`${BASE_URL}/orders/${orderId}`, {
    method: "DELETE",
    headers,
  });
}

Rust-клиент

use reqwest::Client;
use serde::Deserialize;
use std::time::{Duration, Instant};
use tokio::time::sleep;

#[derive(Deserialize)]
struct OrderData {
    order_id: String,
    phone: String,
    status: String,
    sms: Option<SmsData>,
}

#[derive(Deserialize)]
struct SmsData {
    text: String,
}

struct SmsCodeClient {
    client: Client,
    token: String,
}

impl SmsCodeClient {
    fn new(token: String) -> Self {
        Self { client: Client::new(), token }
    }

    async fn order_number(&self, service: &str, country: &str) -> anyhow::Result<OrderData> {
        let resp = self.client
            .post("https://smscode.gg/v1/orders")
            .bearer_auth(&self.token)
            .json(&serde_json::json!({"service": service, "country": country}))
            .send()
            .await?;
        let data: serde_json::Value = resp.json().await?;
        Ok(serde_json::from_value(data["data"].clone())?)
    }

    async fn wait_for_sms(&self, order_id: &str, timeout: Duration) -> anyhow::Result<Option<String>> {
        let deadline = Instant::now() + timeout;
        while Instant::now() < deadline {
            let resp = self.client
                .get(format!("https://smscode.gg/v1/orders/{order_id}"))
                .bearer_auth(&self.token)
                .send()
                .await?;
            let data: serde_json::Value = resp.json().await?;
            let order: OrderData = serde_json::from_value(data["data"].clone())?;
            match order.status.as_str() {
                "received" => return Ok(Some(order.sms.unwrap().text)),
                "cancelled" | "expired" => return Ok(None),
                _ => sleep(Duration::from_secs(3)).await,
            }
        }
        Ok(None)
    }
}

Обработка ошибок

Хорошая интеграция предполагает обработку всех возможных ситуаций:

Код ошибкиСитуацияРекомендуемое действие
INSUFFICIENT_BALANCEНет средствПополнить баланс, отправить алёрт
NO_NUMBERS_AVAILABLEНет номеров для страны/сервисаПопробовать другую страну
ORDER_NOT_FOUNDЗаказ не существуетПроверить order_id
ORDER_EXPIREDСессия истеклаСоздать новый заказ
RATE_LIMIT_EXCEEDEDСлишком много запросовДобавить задержку, exponential backoff
INVALID_SERVICEНеверное имя сервисаПроверить каталог /v1/catalog

Для продакшен-интеграции реализуйте retry с экспоненциальной задержкой для временных ошибок сети и алертинг для критических ошибок (нулевой баланс, отсутствие номеров).

Лучшие практики

Интервал polling. Не опрашивайте API чаще раза в 3 секунды. SMS не приходит мгновенно, более частые запросы только расходуют rate limit и нагружают сеть.

Всегда устанавливайте timeout. Не ждите SMS бесконечно. 2–3 минуты — достаточный timeout для большинства сервисов. После этого отменяйте заказ.

Логируйте order_id. Если что-то пошло не так, order_id поможет службе поддержки найти проблему. Сохраняйте его в структурированных логах вместе с timestamp и именем сервиса.

Всегда отменяйте при неудаче. Вызывайте DELETE /v1/orders/{order_id} при любой ошибке или таймауте — это гарантирует немедленный возврат баланса.

Выбирайте страну динамически. Перед заказом проверяйте наличие номеров через /v1/catalog. Выбирайте страну с ненулевым количеством номеров и приемлемой ценой.

Мониторьте баланс. Добавьте алёрт при падении баланса ниже порогового значения — автоматические процессы не должны останавливаться из-за нулевого баланса.

Типичные сценарии автоматизации

Автотест регистрации

В CI/CD пайплайне можно автоматически создавать тестовых пользователей для проверки флоу регистрации вашего продукта. Каждый запуск — новый виртуальный номер, новый аккаунт, чистые данные.

Пример для pytest:

@pytest.fixture
def fresh_account():
    client = SmsCodeClient(os.environ["SMSCODE_API_TOKEN"])
    order = client.order_number("your_service", "india")
    yield order["phone"]
    # teardown: отмена, если SMS не было использовано

Мониторинг доставки SMS

Используйте API для проверки, что ваша система SMS-уведомлений работает корректно. Заказывайте номер, инициируйте отправку SMS из вашей системы, проверяйте получение. Это замечательно вписывается в synthetic monitoring через Grafana или Datadog.

Параллельная обработка

Для создания нескольких аккаунтов одновременно — заказывайте несколько номеров параллельно. В Python используйте asyncio.gather(), в Node.js — Promise.all():

import asyncio
import httpx

async def order_multiple(service: str, countries: list[str]) -> list[dict]:
    async with httpx.AsyncClient(headers=headers) as client:
        tasks = [
            client.post(f"{BASE_URL}/orders", json={"service": service, "country": c})
            for c in countries
        ]
        responses = await asyncio.gather(*tasks)
        return [r.json()["data"] for r in responses]

Интеграция в очереди задач

Для масштабируемых пайплайнов используйте очереди задач (Celery, Sidekiq, BullMQ). Задача получает номер, создаёт заказ в SMSCode, ждёт SMS и обновляет статус в базе данных.

Безопасность при интеграции

Ротация токенов. Периодически обновляйте API-токен — особенно после смены сотрудников с доступом к секретам. Новый токен генерируется в настройках аккаунта.

Минимум привилегий. Если вы используете отдельные аккаунты для разных проектов — это правильная практика изоляции. Один проект — один токен — один баланс.

Не логируйте токен. Убедитесь, что библиотеки HTTP не логируют заголовки Authorization по умолчанию. Настройте whitelist логируемых заголовков.

Проверяйте ответы. Всегда проверяйте поле success в ответе перед обращением к data. Не полагайтесь только на HTTP status code.

Больше деталей и примеров — в полной документации API. Для бизнес-сценариев читайте статью о виртуальных номерах для бизнеса.

Интеграция в CI/CD пайплайн

Одно из ключевых применений API SMSCode — автоматическое тестирование флоу верификации в рамках непрерывной интеграции.

GitHub Actions пример:

- name: Run verification flow tests
  env:
    SMSCODE_API_TOKEN: ${{ secrets.SMSCODE_API_TOKEN }}
  run: pytest tests/test_verification.py -v

Тест создаёт виртуальный номер, запускает верификацию через ваш сервис и проверяет корректность доставки OTP. При каждом деплое вы можете быть уверены, что SMS-верификация работает.

Стоимость тестов. Каждый прогон CI — это несколько центов за виртуальный номер. При ежедневных деплоях это $0.50–$2 в месяц. Ничтожно мало в сравнении с ценой пропущенной поломки верификации в продакшене.

Мониторинг и алертинг

Для продакшен-систем добавьте проактивный мониторинг:

Алёрт на низкий баланс:

balance = get_balance()
if balance < 500:  # меньше $5
    send_alert("SMSCode balance low", balance)

Синтетический тест верификации. Раз в час запускайте тест: заказ номера → отправка тестового SMS → проверка получения. Если цепочка ломается — алёрт немедленно.

Логирование метрик. Отслеживайте процент успешных верификаций по странам. Если индийские номера вдруг начали чаще давать ошибку NO_NUMBERS_AVAILABLE — пора добавить Индонезию как запасной вариант.

SDK и библиотеки сообщества

На текущий момент официального SDK нет — API достаточно простой, чтобы реализовать клиент самостоятельно за 30 минут по описанным выше примерам. Примеры кода в этой статье покрывают все основные сценарии.

Для быстрого старта рекомендуем:

  • Python: скопируйте пример выше, добавьте обработку INSUFFICIENT_BALANCE и NO_NUMBERS_AVAILABLE
  • TypeScript/Node.js: типизированный интерфейс из примера легко расширить под нужды проекта
  • Rust: пример с reqwest и tokio готов к продакшену

Если вы написали клиент на другом языке — делитесь в нашем чате поддержки. Полная документация API доступна на сайте.


FAQ

Как получить API-ключ SMSCode?

Зарегистрируйтесь на SMSCode, перейдите в настройки аккаунта и сгенерируйте API-токен. Токен отображается только один раз при создании — сохраните его в безопасном месте немедленно. Если потеряли — сгенерируйте новый в тех же настройках.

Есть ли лимиты на количество запросов?

Да, действует rate limiting. Для большинства эндпоинтов ограничение достаточно мягкое для стандартных задач. При превышении лимита вы получите ответ 429 с заголовком Retry-After. Реализуйте exponential backoff в своём коде.

Можно ли использовать API для тестирования на локальном окружении?

Да. API работает через HTTPS из любого места с доступом в интернет. Для изоляции тестовой среды создайте отдельный SMSCode-аккаунт с небольшим балансом специально для тестов.

Как получать уведомления о входящих SMS без polling?

На текущий момент API работает в режиме polling. Webhook-поддержка находится в роадмапе. Следите за обновлениями в документации. Для минимизации задержки при polling рекомендуем интервал 3–5 секунд.

Что делать при ошибке NO_NUMBERS_AVAILABLE?

Это значит, что для выбранной комбинации сервис+страна в данный момент нет доступных номеров. Решение: запросить каталог GET /v1/catalog?service=your_service, найти страны с count > 0 и попробовать другую. Номера появляются постоянно — можно повторить через 1–2 минуты.

Как обрабатывать несколько SMS на один номер?

Если целевой сервис отправляет несколько SMS (например, сначала приветственное, потом код) — API возвращает первое полученное SMS. Для получения последующих SMS используйте повторный запрос статуса заказа. В объекте sms будет самое последнее сообщение.

Поддерживается ли получение голосовых звонков с кодом?

Нет, SMSCode API работает только с текстовыми SMS. Если целевой сервис предлагает только голосовую верификацию — это не поддерживается. Большинство сервисов предлагают SMS как основной вариант, а голосовой звонок — как резервный.

Хотите попробовать SMSCode?

Создайте аккаунт и получите первый виртуальный номер менее чем за две минуты.

Начать →