SmsEmpire

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:

  1. Assinar o JSON re-serializado em vez dos bytes brutos. Re-serializar muda os bytes e a assinatura nunca vai bater certo.
  2. Ignorar o timestamp. Sem a verificação de ±300 s, uma entrega capturada pode ser-lhe reenviada para sempre.
  3. Comparar com == em vez de uma comparação em tempo constante.
  4. 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

codeO que fazer
INVALID_API_KEYChave ausente, malformada ou desconhecida. Não repita.
KEY_REVOKED / KEY_EXPIREDCrie uma chave nova. Não repita.
INSUFFICIENT_SCOPEEsta chave foi criada sem acesso de escrita.
API_ACCESS_DENIEDA API está desativada para esta conta, ou ainda não carregou saldo.
API_DISABLEDA API está temporariamente desativada para todos. Tente mais tarde.
IDEMPOTENCY_KEY_REQUIREDEnvie um cabeçalho Idempotency-Key em cada compra.
IDEMPOTENCY_KEY_REUSEDMesma chave, corpo diferente. Corrija o seu cliente.
IDEMPOTENCY_IN_PROGRESSA primeira tentativa ainda está a decorrer. Repita dentro de um segundo.
PRICE_ABOVE_MAXO preço subiu acima do seu max_price. Volte a consultar e decida.
INSUFFICIENT_BALANCECarregue saldo. Não repita antes disso.
COUNTRY_OUT_OF_STOCKSem números neste momento. Repita ou escolha outro país.
PROVIDER_CAPACITYNão conseguimos fornecer esse país temporariamente. Tente mais tarde.
CONCURRENCY_LIMITDemasiadas ativações pendentes. Termine ou cancele algumas.
SPEND_LIMITLimite de gasto por hora atingido. Tente após a janela.
RATE_LIMITEDAbrande. Respeite o Retry-After.
VALIDATION_ERRORO 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