Если вам нужно автоматизировать 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 |
received | SMS получено | Извлечь код из 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 как основной вариант, а голосовой звонок — как резервный.