A API da SmsEmpire
Compre verificações por SMS e email a partir do seu próprio código, debitadas do seu saldo, com desconto sobre o preço do site.
- →Preço com desconto em cada compra feita pela API
- →Webhooks assinados assim que o código chega — sem ciclo de polling
- →Compras idempotentes: repetir após um timeout é seguro
Início rápido
Tudo fica sob https://smsempire.com/api/v1. Autentique-se enviando a sua chave como bearer token. Apenas do lado do servidor: esta API não envia cabeçalhos CORS, e uma chave usada num navegador é uma chave exposta.
# 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"Convenções
- O dinheiro é uma string decimal ("0.28000"), nunca um número JSON. Analise-o com um tipo decimal, não com um float.
- As datas são RFC3339 em UTC.
- Os erros são sempre {"error": {"code", "message", "request_id"}}. Decida pelo code; message pode mudar.
- Cite o request_id em qualquer ticket de suporte: aponta diretamente para esse pedido nos nossos registos.
- Os limites voltam nos cabeçalhos X-RateLimit-*, e um 429 traz Retry-After.
Idempotência (obrigatória nas compras)
Cada compra precisa de um cabeçalho Idempotency-Key — um UUID é ideal. Reutilizá-lo devolve a resposta original com Idempotent-Replay: true em vez de comprar um segundo número.
Existe por uma única situação: o seu POST expira e não sabe se chegou a passar. Sem a chave, repetir compra duas vezes e não repetir perde o número já pago. Com ela, basta repetir.
Reutilizar uma chave com um corpo diferente dá 422, não uma repetição silenciosa: essa combinação é um bug do seu lado, e escondê-lo dar-lhe-ia o resultado errado.
max_price
Opcional mas muito recomendado. Se omitir o país tentamos vários, por ordem de qualidade e a preços diferentes; max_price é verificado contra cada candidato, por isso nunca lhe podem cobrar mais do que aceitou. Se nenhum servir, recebe 409 PRICE_ABOVE_MAX com o preço atual e nada é cobrado.
Webhooks
Registe um endpoint HTTPS no seu painel. Enviamos um POST assim que um código chega. A entrega é pelo menos uma vez, com repetições escalonadas ao longo de cerca de duas horas e meia.
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"
}
}Requisitos do seu endpoint: HTTPS na porta 443, resolvendo para um endereço público. Não seguimos redirecionamentos e recusamos destinos privados, de loopback e de metadados de nuvem.
Verificar um webhook
A assinatura é HMAC-SHA256(segredo, "<timestamp>.<corpo bruto>"), em hexadecimal. Quatro erros frequentes, por ordem:
- Assinar o JSON re-serializado em vez dos bytes brutos. Re-serializar muda os bytes e a assinatura nunca vai bater certo.
- Ignorar o timestamp. Sem a verificação de ±300 s, uma entrega capturada pode ser-lhe reenviada para sempre.
- Comparar com == em vez de uma comparação em tempo constante.
- Não deduplicar por X-SmsEmpire-Delivery. Garantimos pelo menos uma vez, não exatamente uma.
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))),
);
}Ao rodar o segredo, o anterior continua válido 24 horas e ambos assinam cada entrega nessa janela — pode fazer deploy sem interrupções.
Erros
| code | O que fazer |
|---|---|
| INVALID_API_KEY | Chave ausente, malformada ou desconhecida. Não repita. |
| KEY_REVOKED / KEY_EXPIRED | Crie uma chave nova. Não repita. |
| INSUFFICIENT_SCOPE | Esta chave foi criada sem acesso de escrita. |
| API_ACCESS_DENIED | A API está desativada para esta conta, ou ainda não carregou saldo. |
| API_DISABLED | A API está temporariamente desativada para todos. Tente mais tarde. |
| IDEMPOTENCY_KEY_REQUIRED | Envie um cabeçalho Idempotency-Key em cada compra. |
| IDEMPOTENCY_KEY_REUSED | Mesma chave, corpo diferente. Corrija o seu cliente. |
| IDEMPOTENCY_IN_PROGRESS | A primeira tentativa ainda está a decorrer. Repita dentro de um segundo. |
| PRICE_ABOVE_MAX | O preço subiu acima do seu max_price. Volte a consultar e decida. |
| INSUFFICIENT_BALANCE | Carregue saldo. Não repita antes disso. |
| COUNTRY_OUT_OF_STOCK | Sem números neste momento. Repita ou escolha outro país. |
| PROVIDER_CAPACITY | Não conseguimos fornecer esse país temporariamente. Tente mais tarde. |
| CONCURRENCY_LIMIT | Demasiadas ativações pendentes. Termine ou cancele algumas. |
| SPEND_LIMIT | Limite de gasto por hora atingido. Tente após a janela. |
| RATE_LIMITED | Abrande. Respeite o Retry-After. |
| VALIDATION_ERROR | O corpo do seu pedido está errado. Não repita sem o alterar. |
Ciclo de vida de uma ativação
Uma ativação está pending até chegar um código (received), a marcar como usada (finished) ou ser cancelada. Cancelar devolve exatamente o que foi cobrado.
Uma ativação pendente expira 20 minutos após a compra. Cancelamo-la na rede e devolvemos o valor integral assim que esse cancelamento é confirmado — nunca fica a pagar por um número que não recebeu nada.
Há um período de graça de cerca de 120 segundos antes de poder cancelar um número; cancelar antes devolve EARLY_CANCEL_DENIED com os segundos em falta.
Limites
Pedidos de leitura, de escrita e o total por utilizador têm cada um um teto por minuto, além de tetos opcionais para ativações pendentes, gasto por hora e ativações por hora. Os seus valores atuais estão em GET /api/v1/me, onde null significa sem limite. Se precisar de os aumentar, abra um ticket: são definições por conta, não valores fixos no código.
Referência completa dos endpoints
Documento OpenAPI 3.1 interativo, sempre gerado a partir do código em execução.
Abrir a referência