1 选择平台 1,304 个服务可用

选择服务

2 选择国家 145 个国家

选择国家

3 运营商与价格

选择平台和国家,顺序不限,运营商和价格会显示在这里。

面向开发者

API 参考文档

直接在你的代码中读取任意服务、任意国家的余额和现行价格。一个密钥放在请求头中,返回 JSON 数据。

基础地址

https://smsactivate.io/api/v1
获取密钥

第一个请求 #

全程都是走 HTTPS 的 JSON,用一把你自己签发的密钥认证。没有东西要安装,没有协议要签,也不用申请沙箱 —— 一把密钥加上 curl 就够了。

  1. 1 创建密钥 打开账户菜单,进入 API 密钥,创建一把。它只显示一次,之后我们只保留它的指纹,所以事后无处可查。
  2. 2 以 bearer 令牌的形式提交 每个调用都带一个 Authorization 请求头。除此之外没有第二条入口:不接受查询参数,不接受 cookie,也没有会话。
  3. 3 读取响应 每个响应都是一个带 ok 字段的 JSON 对象。ok 为 false 时,error 字段会用一个稳定的字符串说明原因,这个字符串是给你的代码看的。

如何出示密钥 #

一个账户一把密钥。除下面这个请求头以外的任何方式都不受支持,这是有意为之 —— 放在查询串里的密钥会进入访问日志、浏览器历史和 referrer 请求头,所以我们直接拒绝,而不是接受之后再悄悄泄露出去。

request
Authorization: Bearer sk_your_key_here

这把密钥会花钱 今天它只读取余额;等下单接口上线后,同一把密钥就能下单。请像对待密码一样对待它:不要提交到版本库,不要出现在截图里,需要时更换而不是传阅。签发一把新的,上一把当场作废。

格式与约定 #

格式与约定
基础地址 https://smsactivate.io/api/v1
传输 只走 HTTPS:用普通 HTTP 调用会收到重定向,而重定向会丢掉你的 Authorization 头。
格式 双向都是 JSON。返回的永远是一个对象——裸数组从来不是合法的响应。
成功 HTTP 200,且 "ok" 为 true。
失败 4xx 或 5xx 状态码,"ok" 为 false,外加一个拼写永不改变的 "error" 字符串。
金额 金额是美元数字——不是字符串,也不是美分:0.84 就是八十四美分。
未知字段 任何响应日后都可能增加字段。遇到不认识的字段请跳过,不要当成错误。

出错的时候 #

只根据 error 字符串分支判断,别的都不要用。旁边那句话是写给你看的,不是写给代码看的,随时可能改写;字符串不会。

出错的时候
状态码 错误 原因
401 missing_key 没有收到 Authorization 请求头,或者收到的不是 bearer 令牌。
401 bad_key 这把密钥我们不认识,或者它已经作废或被替换了。
400 unknown_service 没有服务对应这个代码。代码取自目录,不是屏幕上显示的名称。
404 no_stock 此刻这个「服务 + 国家」组合没有空闲线路。
404 unknown_country 这个产品下没有国家对应这个代码。两份目录覆盖的范围并不相同。
400 unknown_tech 这个国家不提供该制式的网络。只有美国按都市区出售。
429 rate_limited 调用过于频繁。Retry-After 请求头会给出需要等待的秒数。

可以压到多重 #

每分钟 600 次调用,按账户计,不按地址计。一把密钥本该只跑在一台服务器、也就是一个地址上 —— 按地址计数只会惩罚正常使用,同时让一把被盗的密钥在任何别处照样能用。超过上限会返回 429,并在 Retry-After 中给出秒数。

接口 #

账户与目录查询接口。每个接口都列出可接受的全部参数、返回的全部字段,以及可能出现的所有拒绝方式。

账户里有什么 #

GET /api/v1/balance

当前的可用额度,以及按目录中现有的最低价,这些额度够做多少次激活。

返回字段

账户里有什么 — 返回字段
字段名 类型 说明
ok boolean 状态码为 200 时,此字段存在且为 true。
balance number 余额中的美元数,精确到美分。
currency string 固定为 "USD";之所以返回,是为了让你什么都不必假设。
codes_at_cheapest integer 按目录最低价计算,余额能买多少条验证码。这是一个用来感知规模的估算,不是报价;目录为空时为 null。
cheapest_code number 这个最低价本身,便于你按另一个数字自行重算估算值。

请求

request
curl -s "https://smsactivate.io/api/v1/balance" \
  -H "Authorization: Bearer $SMSACTIVATE_KEY"

响应

200 OK
{
  "ok": true,
  "balance": 42.5,
  "currency": "USD",
  "codes_at_cheapest": 425,
  "cheapest_code": 0.1
}

拒绝方式 missing_key bad_key rate_limited

一个国家里的一个服务 #

GET /api/v1/pricing?service={service}&country={country}

单个组合的实时价格和实时空闲数。两者都会漂移:价格跟随当地持有该服务的最便宜运营商,空闲数则是运营商在那一刻报上来的数值。

请求参数

一个国家里的一个服务 — 请求参数
名称 说明
service 必填 服务的代码,比如 telegram——小写,按目录里的拼写。
country 可省略 国家的代码,比如 england。去掉它,一次调用就会返回所有国家——见下文。

返回字段

一个国家里的一个服务 — 返回字段
字段名 类型 说明
ok boolean 状态码为 200 时,此字段存在且为 true。
service string 服务代码,原样返回。
country string 国家代码,原样返回。
price number 在有货的最便宜运营商上,一条验证码的美元价格。
stock integer 运营商此刻报告的空闲号码数——一个实时读数,每分钟都在变。

请求

request
curl -s "https://smsactivate.io/api/v1/pricing?service=telegram&country=england" \
  -H "Authorization: Bearer $SMSACTIVATE_KEY"

响应

200 OK
{
  "ok": true,
  "service": "telegram",
  "country": "england",
  "price": 0.84,
  "stock": 61213
}

拒绝方式 missing_key bad_key unknown_service no_stock rate_limited

一个服务,所有国家 #

GET /api/v1/pricing?service={service}

不填国家,就会返回所有承载该服务的国家。排序用的是本站自己的规则 —— 先看实际交付情况,同等情况下再看价格 —— 而不是原始价格;否则一条两分钱、但谁也收不到东西的线路会被顶到最前面。

请求参数

一个服务,所有国家 — 请求参数
名称 说明
service 必填 服务的代码,比如 telegram。

返回字段

一个服务,所有国家 — 返回字段
字段名 类型 说明
ok boolean 状态码为 200 时,此字段存在且为 true。
service string 你发送的服务代码。
name string 网站上显示的名称,比如 "Telegram"。
countries array 每个国家一个对象,按上文说明的顺序排列。
countries[].country string 国家的代码,可直接传给配对调用。
countries[].name string 它的英文名称。
countries[].price number 一条验证码的美元价格。
countries[].stock integer 此刻空闲的号码数。

请求

request
curl -s "https://smsactivate.io/api/v1/pricing?service=telegram" \
  -H "Authorization: Bearer $SMSACTIVATE_KEY"

响应

200 OK
{
  "ok": true,
  "service": "telegram",
  "name": "Telegram",
  "countries": [
    { "country": "england", "name": "United Kingdom", "price": 0.84, "stock": 61213 },
    { "country": "poland",  "name": "Poland",         "price": 0.91, "stock": 22140 }
  ]
}

拒绝方式 missing_key bad_key unknown_service rate_limited

移动会话价格 #

GET /api/v1/proxy-pricing?country={country}&tech={tech}

不指定国家,返回我们放置调制解调器的那四个国家,每个附运营商数量和起步价。指定国家,则返回当地全部运营商及各时长的价格。时长以天为单位:1、7、30、365。

请求参数

移动会话价格 — 请求参数
名称 说明
country 可省略 国家的代码,比如 us。去掉它,返回的就是国家列表。
tech 可省略 网络制式,4g 或 5g。只有美国两者都卖;其他地方该字段被忽略,返回唯一可用的号段。

返回字段

移动会话价格 — 返回字段
字段名 类型 说明
ok boolean 状态码为 200 时,此字段存在且为 true。
country string 你发送的国家代码。
tech string 价格所对应的制式,已套用默认值之后。
techs array 该国在售的每一种制式。
terms array 可选的时长,以天计。
carriers array 每家运营商一个对象。
carriers[].carrier string 运营商的代码,下单时传入。
carriers[].name string 运营商的显示名称,比如 "T-Mobile"。
carriers[].prices object 各时长的美元价格,以天数为键。

请求

request
curl -s "https://smsactivate.io/api/v1/proxy-pricing?country=us" \
  -H "Authorization: Bearer $SMSACTIVATE_KEY"

响应

200 OK
{
  "ok": true,
  "country": "us",
  "tech": "4g",
  "techs": ["4g", "5g"],
  "terms": [1, 7, 30, 365],
  "carriers": [
    { "carrier": "us-t-mobile", "name": "T-Mobile", "prices": { "1": 8.67, "7": 52, "30": 130, "365": 1300 } },
    { "carrier": "us-verizon",  "name": "Verizon",  "prices": { "1": 8.67, "7": 52, "30": 130, "365": 1300 } }
  ]
}

拒绝方式 missing_key bad_key unknown_country unknown_tech rate_limited

租用价格 #

GET /api/v1/rental-pricing?country={country}&days={days}

不指定国家,返回所有可以持有线路的国家,每个附起步价。指定国家,则返回当地全部运营商,以及你查询的那个时长在每家上的价格。

请求参数

租用价格 — 请求参数
名称 说明
country 可省略 国家的代码,比如 fr。去掉它,返回的就是国家列表。
days 可省略 以天计的时长——7、14 或 30。其他任何值都按最短时长处理。

返回字段

租用价格 — 返回字段
字段名 类型 说明
ok boolean 状态码为 200 时,此字段存在且为 true。
country string 你发送的国家代码。
days integer 价格所对应的时长。
terms array 在售的每一种时长,以天计。
operators array 每家运营商一个对象,价格最低的在前。
operators[].operator string 运营商的代码,下单时传入。
operators[].name string 它的显示名称。
operators[].type string physical、virtual 或 premium 三者之一。
operators[].price number 整个时长的美元价格。

请求

request
curl -s "https://smsactivate.io/api/v1/rental-pricing?country=fr&days=30" \
  -H "Authorization: Bearer $SMSACTIVATE_KEY"

响应

200 OK
{
  "ok": true,
  "country": "fr",
  "days": 30,
  "terms": [7, 14, 30],
  "operators": [
    { "operator": "fr-lycamobile", "name": "Lycamobile", "type": "virtual",  "price": 14.32 },
    { "operator": "fr-orange",     "name": "Orange",     "type": "physical", "price": 20.76 }
  ]
}

拒绝方式 missing_key bad_key unknown_country rate_limited

示例代码 #

可直接运行的完整程序,而非单行片段——包括最容易出错的两个环节。

Shell #

找出某个服务最便宜的国家,然后确认余额够不够。

cheapest.sh
#!/usr/bin/env bash
set -euo pipefail
: "${SMSACTIVATE_KEY:?export your key first}"
API="https://smsactivate.io/api/v1"

auth=(-H "Authorization: Bearer $SMSACTIVATE_KEY")

# The catalogue is already ordered: the first country is the one to take.
best=$(curl -sf "${API}/pricing?service=telegram" "${auth[@]}" \
        | jq -r ".countries[0] | \"\(.country) \(.price)\"")
country=${best% *}
price=${best#* }

balance=$(curl -sf "${API}/balance" "${auth[@]}" | jq -r .balance)

# ⚠️ Compare as numbers, not as strings: "9.5" > "10" is true in a string sort.
if awk "BEGIN{exit !($balance >= $price)}"; then
  echo "ok: $country at \$$price, balance \$$balance"
else
  echo "top up first: need \$$price, have \$$balance" >&2
  exit 1
fi

JavaScript #

同样的事在 Node 里做一遍,并妥当处理错误结构。

pricing.mjs
const API = "https://smsactivate.io/api/v1";
const key = process.env.SMSACTIVATE_KEY;

async function call(path) {
  const r = await fetch(API + path, {
    headers: { Authorization: `Bearer ${key}` },
  });
  const body = await r.json();
  // A non-2xx always carries { ok:false, error }. Branch on `error`, never on
  // the sentence — the string is stable, the wording is not.
  if (!r.ok || !body.ok) {
    if (body.error === "rate_limited") {
      const wait = Number(r.headers.get("Retry-After") || 60);
      await new Promise((s) => setTimeout(s, wait * 1000));
      return call(path);
    }
    throw new Error(body.error ?? `http_${r.status}`);
  }
  return body;
}

const { countries } = await call("/pricing?service=telegram");
const { balance }   = await call("/balance");

const best = countries[0];
console.log(`${best.name}: $${best.price} (${best.stock} in stock)`);
console.log(balance >= best.price ? "balance covers it" : "top up first");

Python #

再用 Python 做一遍,同样在遇到 429 时重试。

pricing.py
import os, time, requests

API = "https://smsactivate.io/api/v1"
S = requests.Session()
S.headers["Authorization"] = f"Bearer {os.environ['SMSACTIVATE_KEY']}"

def call(path):
    r = S.get(API + path, timeout=20)
    body = r.json()
    if not r.ok or not body.get("ok"):
        if body.get("error") == "rate_limited":
            time.sleep(int(r.headers.get("Retry-After", 60)))
            return call(path)
        raise RuntimeError(body.get("error", f"http_{r.status_code}"))
    return body

countries = call("/pricing?service=telegram")["countries"]
balance   = call("/balance")["balance"]

best = countries[0]
print(f"{best['name']}: ${best['price']} ({best['stock']} in stock)")
print("balance covers it" if balance >= best["price"] else "top up first")

还没有的部分 #

今天这把密钥能读取账户和目录。下一步是下单,它会在同一个基础地址上、用同一把密钥应答 —— 所以现在照着这些接口写的代码,等下单上线时不需要拆掉重写。

下单取号 暂未支持 暂未提供。目前下单在网站上完成。
轮询验证码 暂未支持 计划中,与下单一起上线。
释放号码 暂未支持 计划中,与下单一起上线。
通过 API 租用 暂未支持 计划中。
Webhook 暂未支持 暂未提供——等有订单事件可推送时,它会一起到来。

这一页会随之扩充:现有内容留在原处,新增的接口出现在「接口」一节下。

更新记录 #

  • 首个公开版本:API 密钥、GET /balance、GET /pricing。