Разработчикам
Справочник API
Читайте баланс и действующий тариф по любому сервису в любой стране прямо из своего кода. Один ключ в заголовке — и JSON в ответ.
Первый запрос #
Везде JSON поверх HTTPS, с авторизацией одним ключом, который вы выпускаете сами. Ничего не нужно устанавливать, ничего подписывать, песочницу выпрашивать не у кого — хватит ключа и curl.
- 1 Создать ключ Меню аккаунта, затем «Ключ API», затем создать. Он появляется один раз, и хранится только его отпечаток, поэтому посмотреть позже негде.
- 2 Передавайте его как bearer-токен Каждый вызов несёт заголовок Authorization. Второго входа нет: ни параметра в строке запроса, ни куки, ни сессии.
- 3 Прочитать ответ Каждый ответ — объект JSON с полем ok. Там, где ok равно false, поле error называет причину устойчивой строкой, предназначенной вашему коду.
Как предъявляется ключ #
Один ключ на аккаунт. Любой путь, кроме заголовка ниже, не поддерживается намеренно: ключ в строке запроса оседает в журналах доступа, в истории браузера и в заголовках referer, поэтому его отклоняют сразу, а не принимают, тихо утекая.
Authorization: Bearer sk_your_key_here
Этот ключ тратит деньги Сегодня он читает баланс; тот же ключ будет размещать заказы, когда появится оформление. Обращайтесь с ним как с паролем: не в системе контроля версий, не на скриншоте, заменять, а не передавать из рук в руки. Выпуск замены выводит прежний ключ из строя тут же.
Формы и соглашения #
| Базовый адрес | https://smsactivate.io/api/v1 |
|---|---|
| Транспорт | Только HTTPS: на вызов по обычному HTTP приходит редирект, а редирект теряет ваш заголовок Authorization. |
| Формат | JSON в обе стороны. В ответе всегда объект — голый массив корректным ответом не бывает. |
| Успех | HTTP 200, и в "ok" стоит true. |
| Неудача | Статус 4xx или 5xx, в "ok" стоит false, а в "error" — строка, написание которой никогда не меняется. |
| Деньги | Суммы — числа в долларах США: не строки и не центы. 0.84 — это восемьдесят четыре цента. |
| Неизвестные поля | Со временем в любом ответе могут появиться новые поля. Незнакомые пропускайте, а не считайте ошибкой. |
Когда что-то не выходит #
Ветвитесь по строке ошибки и ни по чему другому. Предложение рядом с ней написано для вас, а не для вашего кода, и может быть переформулировано в любой момент; строка — нет.
| Статус | ошиб. | Причина |
|---|---|---|
| 401 | missing_key |
Заголовок Authorization не пришёл, либо пришло не bearer-токен. |
| 401 | bad_key |
Такой ключ нам неизвестен, либо он с тех пор выведен из строя или заменён. |
| 400 | unknown_service |
На этот код не отвечает ни один сервис. Коды берутся из каталога, а никогда не из имени, показанного на экране. |
| 404 | no_stock |
Для этой связки «сервис — страна» в данный момент ничего не простаивает. |
| 404 | unknown_country |
На этот код не отвечает ни одна страна для данного продукта. Каталоги покрывают разную территорию. |
| 400 | unknown_tech |
Это поколение сети в данной стране не предлагается. По городам продаются только США. |
| 429 | rate_limited |
Слишком много вызовов. Заголовок Retry-After называет ожидание в секундах. |
Насколько сильно можно давить #
600 вызовов в минуту, засчитываемых на аккаунт, а не на адрес. Ключ рассчитан жить на одном сервере — на одном адресе, — поэтому счёт по адресам штрафовал бы обычную работу, оставляя украденному ключу свободу работать откуда угодно ещё. За потолком вы получаете 429 и Retry-After в секундах.
Маршруты #
Чтение аккаунта и каталога. Каждый маршрут называет каждый принимаемый параметр, каждое возвращаемое поле и каждый способ отказать.
Что лежит на аккаунте #
GET
/api/v1/balance
Кредит на текущий момент и то, на сколько активаций его хватит по самому низкому тарифу, который сейчас есть в каталоге.
Что возвращается
| Ключ | Вид | Значение |
|---|---|---|
ok |
boolean | Присутствует и равно true всякий раз, когда статус 200. |
balance |
number | Доллары на балансе, с точностью до цента. |
currency |
string | Всегда "USD"; поле отдаётся, чтобы ничего не приходилось предполагать. |
codes_at_cheapest |
integer | Сколько кодов покроет баланс по самой низкой цене каталога. Оценка, чтобы понимать масштаб, а не котировка; null, если каталог пуст. |
cheapest_code |
number | Сама эта минимальная цена — на случай, если вы захотите пересчитать оценку от другой суммы. |
Вызов
curl -s "https://smsactivate.io/api/v1/balance" \
-H "Authorization: Bearer $SMSACTIVATE_KEY"
Ответ
{
"ok": true,
"balance": 42.5,
"currency": "USD",
"codes_at_cheapest": 425,
"cheapest_code": 0.1
}
Как отказывает missing_key bad_key rate_limited
Один сервис в одной стране #
GET
/api/v1/pricing?service={service}&country={country}
Актуальный тариф и актуальное число свободных линий для одной связки. Дрейфует и то и другое: тариф идёт за самым дешёвым оператором, который держит там этот сервис, а счётчик — это то, что оператор сообщает в данное мгновение.
Что принимает
| Имя | Значение | |
|---|---|---|
service |
нужен | Код сервиса, например telegram — строчными буквами, как он записан в каталоге. |
country |
можно опустить | Код страны, например england. Уберите его — и одним вызовом вернутся все страны, см. ниже. |
Что возвращается
| Ключ | Вид | Значение |
|---|---|---|
ok |
boolean | Присутствует и равно true всякий раз, когда статус 200. |
service |
string | Код сервиса, возвращённый без изменений. |
country |
string | Код страны, возвращённый без изменений. |
price |
number | Цена одного кода в долларах у самого дешёвого оператора, где он есть. |
stock |
integer | Сколько номеров операторы сообщают свободными прямо сейчас — живое показание, меняется от минуты к минуте. |
Вызов
curl -s "https://smsactivate.io/api/v1/pricing?service=telegram&country=england" \
-H "Authorization: Bearer $SMSACTIVATE_KEY"
Ответ
{
"ok": true,
"service": "telegram",
"country": "england",
"price": 0.84,
"stock": 61213
}
Как отказывает missing_key bad_key unknown_service no_stock rate_limited
Один сервис по всем странам #
GET
/api/v1/pricing?service={service}
Опустите страну — и вернутся все страны, несущие сервис. Порядок здесь собственный, сайтовый: сначала то, что реально доставляет, и уже внутри этого — самое дешёвое. Не голый тариф: он поднял бы наверх двухцентовую линию, на которую никто ничего никогда не получает.
Что принимает
| Имя | Значение | |
|---|---|---|
service |
нужен | Код сервиса, например telegram. |
Что возвращается
| Ключ | Вид | Значение |
|---|---|---|
ok |
boolean | Присутствует и равно true всякий раз, когда статус 200. |
service |
string | Код сервиса, который вы прислали. |
name |
string | Название, как оно показано на сайте, например «Telegram». |
countries |
array | По объекту на страну, в порядке, описанном выше. |
countries[].country |
string | Код страны, готовый для передачи в парный вызов. |
countries[].name |
string | Его английское название. |
countries[].price |
number | Цена одного кода в долларах. |
countries[].stock |
integer | Номеров свободно в эту минуту. |
Вызов
curl -s "https://smsactivate.io/api/v1/pricing?service=telegram" \
-H "Authorization: Bearer $SMSACTIVATE_KEY"
Ответ
{
"ok": true,
"service": "telegram",
"name": "Telegram",
"countries": [
{ "country": "england", "name": "United Kingdom", "price": 0.84, "stock": 61213 },
{ "country": "poland", "name": "Poland", "price": 0.91, "stock": 22140 }
]
}
Как отказывает missing_key bad_key unknown_service rate_limited
Тарифы мобильных сессий #
GET
/api/v1/proxy-pricing?country={country}&tech={tech}
Если страна не названа, вы получаете четыре, где у нас стоят модемы, — с числом операторов и начальным тарифом у каждой. Назовите страну — получите каждого оператора там и стоимость каждого отрезка. Отрезки указываются в днях: 1, 7, 30, 365.
Что принимает
| Имя | Значение | |
|---|---|---|
country |
можно опустить | Код страны, например us. Уберите его — и вместо этого вернётся список стран. |
tech |
можно опустить | Поколение сети, 4g или 5g. Оба продаются только в США; в остальных странах поле не учитывается и возвращается единственный доступный диапазон. |
Что возвращается
| Ключ | Вид | Значение |
|---|---|---|
ok |
boolean | Присутствует и равно true всякий раз, когда статус 200. |
country |
string | Код страны, который вы прислали. |
tech |
string | Поколение, к которому относится цена, уже после выбора значения по умолчанию. |
techs |
array | Каждое поколение, продающееся в этой стране. |
terms |
array | Сроки в продаже, в днях. |
carriers |
array | По объекту на оператора. |
carriers[].carrier |
string | Код оператора — передайте его при заказе. |
carriers[].name |
string | Название оператора для показа, например «T-Mobile». |
carriers[].prices |
object | Цена каждого срока в долларах, с ключом — длительностью в днях. |
Вызов
curl -s "https://smsactivate.io/api/v1/proxy-pricing?country=us" \
-H "Authorization: Bearer $SMSACTIVATE_KEY"
Ответ
{
"ok": true,
"country": "us",
"tech": "4g",
"techs": ["4g", "5g"],
"terms": [1, 7, 30, 365],
"carriers": [
{ "carrier": "us-t-mobile", "name": "T-Mobile", "prices": { "1": 8.67, "7": 52, "30": 130, "365": 1300 } },
{ "carrier": "us-verizon", "name": "Verizon", "prices": { "1": 8.67, "7": 52, "30": 130, "365": 1300 } }
]
}
Как отказывает missing_key bad_key unknown_country unknown_tech rate_limited
Тарифы на срок #
GET
/api/v1/rental-pricing?country={country}&days={days}
Если страна не названа, вы получаете каждую страну, где линию можно держать, с её начальным тарифом. Назовите страну — получите каждого оператора там и то, во что обходится у него запрошенный вами отрезок.
Что принимает
| Имя | Значение | |
|---|---|---|
country |
можно опустить | Код страны, например fr. Уберите его — и вместо этого вернётся список стран. |
days |
можно опустить | Срок в днях — 7, 14 или 30. Любое другое значение считается самым коротким сроком. |
Что возвращается
| Ключ | Вид | Значение |
|---|---|---|
ok |
boolean | Присутствует и равно true всякий раз, когда статус 200. |
country |
string | Код страны, который вы прислали. |
days |
integer | Срок, к которому относится цена. |
terms |
array | Каждый срок в продаже, в днях. |
operators |
array | По объекту на оператора, сначала самая низкая цена. |
operators[].operator |
string | Код оператора — передайте его при заказе. |
operators[].name |
string | Его название. |
operators[].type |
string | Одно из значений: physical, virtual или premium. |
operators[].price |
number | Цена всего срока в долларах. |
Вызов
curl -s "https://smsactivate.io/api/v1/rental-pricing?country=fr&days=30" \
-H "Authorization: Bearer $SMSACTIVATE_KEY"
Ответ
{
"ok": true,
"country": "fr",
"days": 30,
"terms": [7, 14, 30],
"operators": [
{ "operator": "fr-lycamobile", "name": "Lycamobile", "type": "virtual", "price": 14.32 },
{ "operator": "fr-orange", "name": "Orange", "type": "physical", "price": 20.76 }
]
}
Как отказывает missing_key bad_key unknown_country rate_limited
Разобранные примеры #
Программы, которые работают как есть, а не однострочные обрывки, — включая те два места, на которых спотыкаются.
Shell #
Найти самую дешёвую страну для сервиса, затем убедиться, что баланса на неё хватает.
#!/usr/bin/env bash
set -euo pipefail
: "${SMSACTIVATE_KEY:?export your key first}"
API="https://smsactivate.io/api/v1"
auth=(-H "Authorization: Bearer $SMSACTIVATE_KEY")
# The catalogue is already ordered: the first country is the one to take.
best=$(curl -sf "${API}/pricing?service=telegram" "${auth[@]}" \
| jq -r ".countries[0] | \"\(.country) \(.price)\"")
country=${best% *}
price=${best#* }
balance=$(curl -sf "${API}/balance" "${auth[@]}" | jq -r .balance)
# ⚠️ Compare as numbers, not as strings: "9.5" > "10" is true in a string sort.
if awk "BEGIN{exit !($balance >= $price)}"; then
echo "ok: $country at \$$price, balance \$$balance"
else
echo "top up first: need \$$price, have \$$balance" >&2
exit 1
fi
JavaScript #
То же самое на Node, с корректной обработкой формы ошибки.
const API = "https://smsactivate.io/api/v1";
const key = process.env.SMSACTIVATE_KEY;
async function call(path) {
const r = await fetch(API + path, {
headers: { Authorization: `Bearer ${key}` },
});
const body = await r.json();
// A non-2xx always carries { ok:false, error }. Branch on `error`, never on
// the sentence — the string is stable, the wording is not.
if (!r.ok || !body.ok) {
if (body.error === "rate_limited") {
const wait = Number(r.headers.get("Retry-After") || 60);
await new Promise((s) => setTimeout(s, wait * 1000));
return call(path);
}
throw new Error(body.error ?? `http_${r.status}`);
}
return body;
}
const { countries } = await call("/pricing?service=telegram");
const { balance } = await call("/balance");
const best = countries[0];
console.log(`${best.name}: $${best.price} (${best.stock} in stock)`);
console.log(balance >= best.price ? "balance covers it" : "top up first");
Python #
И на Python, с такой же повторной попыткой по 429.
import os, time, requests
API = "https://smsactivate.io/api/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['SMSACTIVATE_KEY']}"
def call(path):
r = S.get(API + path, timeout=20)
body = r.json()
if not r.ok or not body.get("ok"):
if body.get("error") == "rate_limited":
time.sleep(int(r.headers.get("Retry-After", 60)))
return call(path)
raise RuntimeError(body.get("error", f"http_{r.status_code}"))
return body
countries = call("/pricing?service=telegram")["countries"]
balance = call("/balance")["balance"]
best = countries[0]
print(f"{best['name']}: ${best['price']} ({best['stock']} in stock)")
print("balance covers it" if balance >= best["price"] else "top up first")
Чего здесь пока нет #
Сегодня ключ читает аккаунт и каталог. Размещение заказов — следующее, и отвечать оно будет по тому же базовому адресу тому же ключу, поэтому ничто, написанное под эти маршруты, распускать не придётся.
| Заказ номера | Пока нет Пока нет. Сейчас заказ проходит через сайт. |
|---|---|
| Опрос кода | Пока нет Планируется, вместе с заказом. |
| Возврат номера | Пока нет Планируется, вместе с заказом. |
| Аренда через API | Пока нет В планах. |
| Вебхуки | Пока нет Пока нет — появится вместе с событиями заказа, когда будет что доставлять. |
Страница растёт вместе с ними: то, что здесь есть, остаётся на месте, а новое появляется в разделе точек входа.
Что менялось #
- Первый публичный выпуск: ключи API, GET /balance, GET /pricing.