L'API di SmsEmpire
Acquista verifiche SMS ed email dal tuo codice, addebitate sul tuo saldo, con uno sconto sul prezzo del sito.
- →Prezzo scontato su ogni acquisto effettuato via API
- →Webhook firmati appena arriva il codice — nessun ciclo di polling
- →Acquisti idempotenti: riprovare dopo un timeout è sicuro
Avvio rapido
Tutto sta sotto https://smsempire.com/api/v1. Autenticati inviando la tua chiave come bearer token. Solo lato server: questa API non invia header CORS, e una chiave usata da un browser è una chiave trapelata.
# 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"Convenzioni
- Il denaro è una stringa decimale ("0.28000"), mai un numero JSON. Analizzalo con un tipo decimal, non con un float.
- Le date sono in RFC3339 UTC.
- Gli errori sono sempre {"error": {"code", "message", "request_id"}}. Ramifica su code; message può cambiare.
- Cita il request_id in qualsiasi ticket di supporto: punta direttamente a quella richiesta nei nostri log.
- I limiti tornano negli header X-RateLimit-*, e un 429 porta Retry-After.
Idempotenza (obbligatoria negli acquisti)
Ogni acquisto richiede un header Idempotency-Key — un UUID è ideale. Riutilizzarlo restituisce la risposta originale con Idempotent-Replay: true invece di comprare un secondo numero.
Esiste per una sola situazione: il tuo POST va in timeout e non sai se è andato a buon fine. Senza la chiave, riprovare compra due volte e non riprovare perde il numero già pagato. Con essa, riprova e basta.
Riusare una chiave con un corpo diverso dà 422, non una ripetizione silenziosa: quella combinazione è un bug lato tuo, e nasconderlo ti darebbe il risultato sbagliato.
max_price
Opzionale ma fortemente consigliato. Se ometti il paese ne proviamo diversi, in ordine di qualità e a prezzi diversi; max_price viene verificato su ogni candidato, quindi non ti si può mai addebitare più di quanto hai accettato. Se nessuno rientra, ricevi 409 PRICE_ABOVE_MAX con il prezzo attuale e non viene addebitato nulla.
Webhook
Registra un endpoint HTTPS nella tua dashboard. Inviamo un POST appena arriva un codice. La consegna è almeno una volta, con ritentativi scaglionati per circa due ore e mezza.
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"
}
}Requisiti del tuo endpoint: HTTPS sulla porta 443, che risolva a un indirizzo pubblico. Non seguiamo i redirect e rifiutiamo destinazioni private, di loopback e di metadati cloud.
Verificare un webhook
La firma è HMAC-SHA256(segreto, "<timestamp>.<corpo grezzo>"), codificata in esadecimale. Quattro errori comuni, in ordine di frequenza:
- Firmare il JSON riserializzato invece dei byte grezzi. Riserializzare cambia i byte e la firma non combacerà mai.
- Ignorare il timestamp. Senza il controllo ±300 s, una consegna intercettata può esserti riproposta all'infinito.
- Confrontare con == invece che con un confronto a tempo costante.
- Non deduplicare su X-SmsEmpire-Delivery. Garantiamo almeno una volta, non esattamente una.
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))),
);
}Ruotando il segreto, il precedente resta valido per 24 ore ed entrambi firmano ogni consegna in quella finestra — così puoi rilasciare senza interruzioni.
Errori
| code | Cosa fare |
|---|---|
| INVALID_API_KEY | Chiave mancante, malformata o sconosciuta. Non riprovare. |
| KEY_REVOKED / KEY_EXPIRED | Crea una nuova chiave. Non riprovare. |
| INSUFFICIENT_SCOPE | Questa chiave è stata creata senza accesso in scrittura. |
| API_ACCESS_DENIED | L'API è disattivata per questo account, o non hai ancora ricaricato. |
| API_DISABLED | L'API è temporaneamente disattivata per tutti. Riprova più tardi. |
| IDEMPOTENCY_KEY_REQUIRED | Invia un header Idempotency-Key a ogni acquisto. |
| IDEMPOTENCY_KEY_REUSED | Stessa chiave, corpo diverso. Correggi il tuo client. |
| IDEMPOTENCY_IN_PROGRESS | Il primo tentativo è ancora in corso. Riprova tra un secondo. |
| PRICE_ABOVE_MAX | Il prezzo ha superato il tuo max_price. Richiedi un nuovo preventivo e decidi. |
| INSUFFICIENT_BALANCE | Ricarica il saldo. Non riprovare prima di averlo fatto. |
| COUNTRY_OUT_OF_STOCK | Al momento non ci sono numeri. Riprova o scegli un altro paese. |
| PROVIDER_CAPACITY | Non possiamo fornire quel paese al momento. Riprova più tardi. |
| CONCURRENCY_LIMIT | Troppe attivazioni in sospeso. Concludine o annullane alcune. |
| SPEND_LIMIT | Raggiunto il tetto di spesa orario. Riprova dopo la finestra. |
| RATE_LIMITED | Rallenta. Rispetta Retry-After. |
| VALIDATION_ERROR | Il corpo della richiesta è errato. Non riprovare senza modificarlo. |
Ciclo di vita di un'attivazione
Un'attivazione è pending finché non arriva un codice (received), la marchi come usata (finished) o viene annullata. Annullare rimborsa esattamente quanto ti è stato addebitato.
Un'attivazione in sospeso scade 20 minuti dopo l'acquisto. La annulliamo sulla rete e ti rimborsiamo per intero non appena l'annullamento è confermato: non resti mai a pagare un numero che non ha ricevuto nulla.
C'è un periodo di grazia di circa 120 secondi prima di poter annullare un numero; annullare prima restituisce EARLY_CANCEL_DENIED con i secondi da attendere.
Limiti
Richieste in lettura, in scrittura e totale per utente hanno ciascuna un tetto al minuto, più tetti opzionali su attivazioni in sospeso, spesa oraria e attivazioni orarie. I tuoi valori attuali sono in GET /api/v1/me, dove null significa nessun limite. Se ti servono più alti apri un ticket: sono impostazioni per account, non valori fissi nel codice.
Riferimento completo degli endpoint
Documento OpenAPI 3.1 interattivo, sempre generato dal codice in esecuzione.
Apri il riferimento