SmsEmpire

SmsEmpire API

在你自己的代码中购买短信和邮箱验证,从账户余额扣费,并享受低于网站价格的折扣。

  • 通过 API 的每一笔购买都享受折扣价
  • 验证码到达即发送带签名的 Webhook —— 无需轮询
  • 幂等下单:网络超时后重试是安全的

快速开始

所有接口都在 https://smsempire.com/api/v1 之下。把密钥作为 bearer token 发送即可完成认证。仅限服务端使用:本 API 不发送任何 CORS 响应头,在浏览器中使用的密钥就是已经泄露的密钥。

# 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"

约定

  • 金额是十进制字符串("0.28000"),绝不是 JSON 数字。请用 decimal 类型解析,不要用 float。
  • 时间戳采用 RFC3339 UTC 格式。
  • 错误格式始终是 {"error": {"code", "message", "request_id"}}。请依据 code 分支处理;message 可能变化。
  • 在任何工单中附上 request_id:它能直接定位到我们日志中的这次请求。
  • 限流信息通过 X-RateLimit-* 响应头返回,429 会带上 Retry-After。

幂等性(下单必需)

每次购买都必须带 Idempotency-Key 请求头,建议用 UUID。重复使用同一个键会返回原始响应并带上 Idempotent-Replay: true,而不是再买一个号码。

它只为一种情况而存在:你的 POST 超时了,而你不知道它是否已经生效。没有这个键,重试会买两次,不重试则会丢掉已经付过钱的号码。有了它,直接重试即可。

同一个键配上不同的请求体会返回 422,而不是静默重放——这种组合是你这边的 bug,掩盖它只会给你错误的结果。

max_price

可选,但强烈建议使用。如果你不指定国家,我们会按质量顺序尝试多个、价格各不相同的国家;max_price 会对每一个候选逐一校验,因此绝不会向你收取超过你认可的金额。如果没有符合的,会返回 409 PRICE_ABOVE_MAX 并附上当前价格,且不产生任何扣费。

Webhook

在控制台注册一个 HTTPS 端点。验证码一到达,我们就会向它发送 POST。投递保证至少一次,并在约两个半小时内按退避策略重试。

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"
  }
}

对你的端点的要求:HTTPS、443 端口,且解析到公网地址。我们不跟随重定向,并拒绝私有地址、回环地址和云元数据地址。

校验 Webhook

签名为 HMAC-SHA256(secret, "<timestamp>.<原始报文>") 的十六进制编码。按常见程度排列的四个易犯错误:

  1. 对重新序列化后的 JSON 签名,而不是原始字节。重新序列化会改变字节,签名永远对不上。
  2. 忽略时间戳。没有 ±300 秒的校验,被截获的一次投递可以被永久重放给你。
  3. 用 == 比较,而不是恒定时间比较。
  4. 没有按 X-SmsEmpire-Delivery 去重。我们保证至少一次,而不是恰好一次。
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))),
    );
}

轮换密钥后,旧密钥仍有效 24 小时,且该窗口内每次投递都用两个密钥同时签名——因此你可以无中断地完成部署。

错误

code应对方式
INVALID_API_KEY密钥缺失、格式错误或不存在。不要重试。
KEY_REVOKED / KEY_EXPIRED请创建新密钥。不要重试。
INSUFFICIENT_SCOPE该密钥创建时没有写权限。
API_ACCESS_DENIED本账户的 API 已关闭,或你尚未充值。
API_DISABLED全平台暂时关闭了 API。请稍后重试。
IDEMPOTENCY_KEY_REQUIRED每次购买都要发送 Idempotency-Key 请求头。
IDEMPOTENCY_KEY_REUSED同一个键配了不同的请求体。请修正你的客户端。
IDEMPOTENCY_IN_PROGRESS第一次尝试仍在处理中。请一秒后重试。
PRICE_ABOVE_MAX价格已超过你的 max_price。请重新询价再决定。
INSUFFICIENT_BALANCE请充值。充值前不要重试。
COUNTRY_OUT_OF_STOCK当前没有号码。请重试或换一个国家。
PROVIDER_CAPACITY该国家暂时无法供货。请稍后重试。
CONCURRENCY_LIMIT待处理的激活过多。请先完成或取消一些。
SPEND_LIMIT已达每小时消费上限。请等本窗口结束后重试。
RATE_LIMITED请放慢速度,并遵守 Retry-After。
VALIDATION_ERROR请求体有误。不要原样重试。

激活的生命周期

激活在收到验证码前处于 pending;收到后变为 received,你标记为已用则是 finished,也可以被取消。取消会原额退回你被扣除的金额。

待处理的激活在购买 20 分钟后过期。我们会向网络发起取消,并在该取消被确认后向你全额退款——你绝不会为一个什么都没收到的号码买单。

号码在购买后约 120 秒内不能取消;过早取消会返回 EARLY_CANCEL_DENIED,并附上还需等待的秒数。

限制

读请求、写请求和每用户总请求各有每分钟上限,另有可选的待处理激活数、每小时消费额和每小时激活数上限。你当前的数值可在 GET /api/v1/me 查看,其中 null 表示不限。需要调高请开工单:这些是按账户配置的,并非写死在代码里。

完整接口参考

交互式 OpenAPI 3.1 文档,始终由运行中的代码生成。

打开参考文档