短信验证API

短信验证API就是三次调用:买号码、等验证码、释放号码。它覆盖225个国家的517个平台,号码€0.07起,什么都没收到的订单不用开工单就会退款。密钥有权限范围,购买是幂等的,老的handler_api客户端不用改就能跑。

一屏看完这套API

更新于2026年8月28日
基础URL
https://virtualsmsnumbers.com/api/v1
认证方式
Bearer密钥,或X-Api-Key
限流
120次/分,突发20次/秒
幂等窗口
24小时
自动退款时限
20分钟
最便宜的一次接码
€0.07
覆盖国家数
225
格式
JSON,以分计价,ISO 8601 UTC

价格与库存来自运营商号码池的当前状态,而不是一份对外公布的价目表。

三次调用的流程

参考文档里的其他内容都是可选的。这就是完整的验证路径,从2019年至今没变过。

  1. 1

    POST /activations

    指定一个平台,如果你在意国家,再加上国家。响应里带着号码、以分为单位的价格和到期时间戳。带上Idempotency-Key,重试的请求就不会再买一个号码;如果调用方是自动化程序,再加上max_price_cents。

  2. 2

    GET /activations/{id}?wait=180

    长轮询。连接会一直挂着,直到短信落地或者超时,所以一个请求就顶掉了本来要发九十次的循环。状态返回为code_received,解析出的验证码和原始短信文本一起给出,因为不同发送方的格式并不一样。

  3. 3

    POST /activations/{id}/complete

    关闭订单,释放线路。如果什么都没来,那就什么都别做:窗口期会自己到期,余额会退回来。提前取消是同一个调用的另一个名字。

一个能跑的示例

设好VSN_KEY就能运行。注释里的响应是真实结构,包括那些你会想存下来的字段。

buy-and-wait.sh
# 1. Buy a Telegram number in Portugal
-kw">curl -X POST https://virtualsmsnumbers.com/api/v1/activations \
  -H "Authorization: Bearer $VSN_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"service":"telegram","country":"PT"}'

# {"id":"1043872915","phone_number":"351926114508","status":"waiting",
#  "price_cents":11,"expires_at":"2026-08-26T12:41:07Z"}

# 2. Long-poll until the code lands (blocks up to 120 s)
-kw">curl "https://virtualsmsnumbers.com/api/v1/activations/1043872915?wait=120" \
  -H "Authorization: Bearer $VSN_KEY"

# {"id":"1043872915","status":"code_received",
#  "messages":[{"code":"48219","text":"Telegram code: 48219"}]}

# 3. Release the number
-kw">curl -X POST https://virtualsmsnumbers.com/api/v1/activations/1043872915/complete \
  -H "Authorization: Bearer $VSN_KEY"

限流

每把密钥每分钟120个请求,突发到每秒20个。每个响应都带X-RateLimit-Limit、X-RateLimit-Remaining和X-RateLimit-Reset,429会告诉你要等多久,而不是让你猜。

只要按设计意图使用,限流很少成为瓶颈:一次长轮询代替九十次轮询,用Webhook代替定时器,国家和平台列表在启动时缓存好。如果你确实需要更多余量,直接说一声,把限额调高对我们双方都比调试一堆密钥容易。

幂等

在POST /activations上发送Idempotency-Key请求头。24小时内重放同一个键,返回的是最初那笔订单的当前状态,并带上Idempotent-Replay响应头,而不是再买一个号码、再扣你一次钱。

这个键不是请求体的哈希:用已经用过的键发送不同的载荷,返回的仍然是第一笔订单。按一次逻辑购买生成一个键——用UUID就行——重试时复用它,但绝不要跨购买复用。

老的handler_api客户端

按经典handler_api.php协议写的脚本可以继续用。把它们指向兼容端点,换掉api_key,同样的动作仍然返回和以前一样的ACCESS_和STATUS_令牌。

它是同一个号码池之上的一层转换,所以两边的价格、库存和退款行为完全一致。新代码还是应该用/api/v1:JSON响应里带着时间戳、错误码和价格字段,令牌格式装不下这些。

legacy-compat.sh
# Existing scripts keep working: same actions, same response tokens.
BASE="https://virtualsmsnumbers.com/stubs/handler_api.php"

-kw">curl "$BASE?api_key=$KEY&action=getNumber&service=tg&country=117"
# ACCESS_NUMBER:1043872915:351926114508

-kw">curl "$BASE?api_key=$KEY&action=getStatus&id=1043872915"
# STATUS_WAIT_CODE
# STATUS_OK:48219

-kw">curl "$BASE?api_key=$KEY&action=setStatus&id=1043872915&status=6"
# ACCESS_ACTIVATION

-kw">curl "$BASE?api_key=$KEY&action=getBalance"
# ACCESS_BALANCE:41.87

代理、MCP与发现

我们提供托管的MCP服务器、一份OpenAPI 3.1文档和一份llms.txt索引,代理不用手写胶水代码就能发现它需要的那三个工具。把你的客户端指向下面这个端点,它会找到购买、等待和释放。

在让任何自主程序动用余额之前:把密钥的权限范围限定为numbers:read和numbers:write,每次购买都设一个max_price_cents上限,并且给它单独一把密钥,这样吊销它不会连带弄停你的生产集成。

mcp.json
{
  "mcpServers": {
    "virtualsmsnumbers": {
      "type": "http",
      "url": "https://virtualsmsnumbers.com/api/mcp",
      "headers": { "Authorization": "Bearer vsn_live_xxxxxxxxxxxx" }
    }
  }
}

上生产之前

  • 每次购买都设max_price_cents。价格跟着号码池走,没有上限的代理会兴高采烈地买下最贵的那一头。
  • 把各种ID当成不透明字符串。订单ID今天恰好是十位数字;这不是承诺,它的长度也不是。
  • 按错误码分支,永远不要按错误消息分支。错误码在一个版本内是稳定的;消息是写给人看的,会被改写。
  • 订阅sms.received并校验签名——对原始请求体做HMAC-SHA256,重放窗口五分钟——而不是去轮询一个本可以直接通知你的状态。
  • 用以分为单位的整数字段做运算。旁边那些格式化过的字符串是用来显示的,而且随地区设置而变。

完整参考文档

六个端点、错误码表、Webhook载荷、权限范围,以及旧令牌对照表。

常见问题

从平台发出算起,送达中位数不到九秒。长轮询之所以存在,是因为长尾很长:订单会保持打开20分钟,什么都没到就过期并退款。

没有你需要处理的东西。窗口期关闭,余额自动退回,订单以过期状态结束,这个状态可以从API读到。

需要,而且不花钱。密钥的权限范围是固定的,还可以有IP允许列表和创建时选定的有效期,所以测试环境的密钥即使泄露,也买不了生产环境的号码。

有很薄、无依赖的封装,但这套API就是六个基于JSON的端点——fetch、requests或者curl,跑完这三次调用真的够用。

在同一笔订单里可以:只要它还开着,后续短信都会到同一个号码。跨会话就需要租用号码,它能把线路从四小时一直持有到十二个月。

不会。同一个号码池,同样的价格;不同的只有响应的编码方式。