Шаг 1 из 5
Создайте аккаунт и ключ API
Зарегистрируйтесь по адресу электронной почты и подтвердите его. В панели откройте Разработчикам, затем Создать ключ. Выберите области send и read. Ключ показывается один раз: скопируйте его в переменную окружения и никогда не добавляйте в систему контроля версий.
- Ключ скопирован
- Экспортирован как
SMSMETEOR_KEY
# macOS, Linux
export SMSMETEOR_KEY="sk_test_4b8e...c19d"
# Windows PowerShell
$env:SMSMETEOR_KEY = "sk_test_4b8e...c19d"Шаг 2 из 5
Пополните баланс
При использовании тестового ключа пропустите этот шаг. Для реальной отправки откройте Баланс, выберите монету и переведите указанную сумму на адрес пополнения. После обнаружения в сети платёж получает статус ожидания, а после подтверждения зачисляется в USD: за несколько секунд в Tron или Solana и за 10–60 минут в Bitcoin.
Проверяйте баланс из кода и задайте в панели нижний порог, чтобы получить событие balance.low до остановки кампании.
curl https://api.smsmeteor.com/v1/balance \
-H "Authorization: Bearer $SMSMETEOR_KEY"{ "balance": "250.00", "currency": "USD",
"pending": "0.00", "low_threshold": "25.00" }Шаг 3 из 5
Отправьте сообщение
Один POST: направление в формате E.164, отправитель и текст. Всегда добавляйте Idempotency-Key; при сетевом сбое и повторе API вернёт исходное сообщение вместо отправки второго.
В ответе сразу указаны количество сегментов и цена. Для направления без покрытия возвращается 422 unsupported_destination, средства не списываются.
- Получен
id, начинающийся сmsg_ statusравенqueuedлибоtestдля тестового ключа
curl https://api.smsmeteor.com/v1/messages \
-H "Authorization: Bearer $SMSMETEOR_KEY" \
-H "Idempotency-Key: first-message-001" \
-d to="+14155550142" \
-d from="METEOR" \
-d text="Привет от SMSMeteor!"import os, requests
r = requests.post(
"https://api.smsmeteor.com/v1/messages",
headers={"Authorization": f"Bearer {os.environ['SMSMETEOR_KEY']}",
"Idempotency-Key": "first-message-001"},
json={"to": "+14155550142", "from": "METEOR",
"text": "Привет от SMSMeteor!"},
)
msg = r.json()
print(msg["id"], msg["status"], msg["price"])const r = await fetch("https://api.smsmeteor.com/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMSMETEOR_KEY}`,
"Idempotency-Key": "first-message-001",
"Content-Type": "application/json",
},
body: JSON.stringify({ to: "+14155550142", from: "METEOR",
text: "Привет от SMSMeteor!" }),
});
const msg = await r.json();
console.log(msg.id, msg.status, msg.price);$ch = curl_init('https://api.smsmeteor.com/v1/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SMSMETEOR_KEY'),
'Idempotency-Key: first-message-001', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['to' => '+14155550142',
'from' => 'METEOR', 'text' => 'Привет от SMSMeteor!']),
]);
$msg = json_decode(curl_exec($ch), true);
echo $msg['id'], ' ', $msg['status'], ' ', $msg['price'];{
"id": "msg_9Kd2fQ",
"status": "queued",
"to": "+14155550142",
"from": "METEOR",
"segments": 1,
"encoding": "gsm7",
"price": "0.0084",
"currency": "USD",
"created_at": "2026-09-21T11:42:07Z"
}Шаг 4 из 5
Получите статус
Получите сообщение по идентификатору. Статусы изменяются только вперёд: В очереди → Отправлено, затем Доставлено, Ошибка или Срок истёк. Большинство доставок подтверждается за несколько секунд; некоторые операторы связи отправляют отчёты пакетами.
Для одного сообщения подойдёт опрос. Для большего объёма используйте вебхук из следующего шага.
curl https://api.smsmeteor.com/v1/messages/msg_9Kd2fQ \
-H "Authorization: Bearer $SMSMETEOR_KEY"r = requests.get(
"https://api.smsmeteor.com/v1/messages/msg_9Kd2fQ",
headers={"Authorization": f"Bearer {os.environ['SMSMETEOR_KEY']}"},
)
print(r.json()["status"])const r = await fetch("https://api.smsmeteor.com/v1/messages/msg_9Kd2fQ", {
headers: { Authorization: `Bearer ${process.env.SMSMETEOR_KEY}` },
});
console.log((await r.json()).status);$ch = curl_init('https://api.smsmeteor.com/v1/messages/msg_9Kd2fQ');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SMSMETEOR_KEY')]]);
echo json_decode(curl_exec($ch), true)['status'];{ "id": "msg_9Kd2fQ", "status": "delivered",
"sent_at": "2026-09-21T11:42:08Z",
"delivered_at": "2026-09-21T11:42:10Z",
"carrier_code": null, "price": "0.0084" }Шаг 5 из 5
Получите вебхук
В панели откройте Разработчикам → Вебхуки → Добавить конечную точку. Укажите HTTPS URL, подпишитесь на message.delivered и message.failed и скопируйте секрет конечной точки. Проверяйте подпись до обработки данных, затем возвращайте любой 2xx в течение 5 секунд.
- Конечная точка отвечает 200
- Подпись проверена по исходному телу
# Имитация подписанного события для локальной конечной точки
TS=$(date +%s)
BODY='{"id":"evt_test","type":"message.delivered","data":{"message_id":"msg_9Kd2fQ"}}'
SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$HOOK_SECRET" | awk '{print $2}')
curl -X POST http://localhost:3000/hooks/sms \
-H "X-Meteor-Timestamp: $TS" \
-H "X-Meteor-Signature: t=$TS,v1=$SIG" \
-H "Content-Type: application/json" -d "$BODY"import hmac, hashlib, time
from flask import Flask, request, abort
app = Flask(__name__)
@app.post("/hooks/sms")
def hook():
ts = request.headers["X-Meteor-Timestamp"]
sig = request.headers["X-Meteor-Signature"].split("v1=")[1]
mac = hmac.new(SECRET.encode(), f"{ts}.".encode() + request.data,
hashlib.sha256).hexdigest()
if abs(time.time() - int(ts)) > 300 or not hmac.compare_digest(mac, sig):
abort(400)
event = request.get_json()
print(event["type"], event["data"]["message_id"])
return "", 200import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
app.post("/hooks/sms", express.raw({ type: "application/json" }), (req, res) => {
const ts = req.get("X-Meteor-Timestamp");
const sig = req.get("X-Meteor-Signature").split("v1=")[1];
const mac = createHmac("sha256", process.env.HOOK_SECRET)
.update(`${ts}.${req.body}`).digest("hex");
if (!timingSafeEqual(Buffer.from(mac), Buffer.from(sig))) return res.sendStatus(400);
const event = JSON.parse(req.body);
console.log(event.type, event.data.message_id);
res.sendStatus(200);
});$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_METEOR_TIMESTAMP'];
$sig = explode('v1=', $_SERVER['HTTP_X_METEOR_SIGNATURE'])[1];
$mac = hash_hmac('sha256', $ts . '.' . $raw, getenv('HOOK_SECRET'));
if (abs(time() - (int) $ts) > 300 || !hash_equals($mac, $sig)) { http_response_code(400); exit; }
$event = json_decode($raw, true);
error_log($event['type'] . ' ' . $event['data']['message_id']);
http_response_code(200);{ "id": "evt_4Qm8Xz", "type": "message.delivered",
"created_at": "2026-09-21T11:42:10Z",
"data": { "message_id": "msg_9Kd2fQ", "status": "delivered",
"to": "+14155550142", "price": "0.0084" } }Устранение неполадок
401 unauthorized
Заголовок должен иметь вид Authorization: Bearer sk_… без кавычек вокруг ключа. Убедитесь, что ключ не отозван, имеет область send, а ваш IP входит в список разрешённых, если он задан.
402 insufficient_balance
Доступный баланс меньше цены сообщения. Во время интеграции используйте тестовый ключ или пополните баланс; пополнение со статусом pending пока нельзя потратить.
400 invalid_number
Номер должен соответствовать E.164: плюс, код страны и национальный номер без начального нуля и пробелов. Например, +447700900123, а не 07700 900123.
Сообщение остаётся в статусе sent
Некоторые операторы связи возвращают отчёты пакетами или не возвращают вовсе; это указано на странице страны. Через 48 часов без отчёта сообщение получает статус expired.
Получатель видит номер вместо моего имени
В этом направлении буквенно-цифровые отправители не поддерживаются или требуют регистрации. Поле from показывает фактически использованного отправителя. См. имена отправителей и маршруты.
Вебхук не срабатывает
Конечная точка должна использовать HTTPS с действительным сертификатом и отвечать в течение 5 секунд. В панели перечислены все попытки и полученные ответы; после исправления повторите событие оттуда.