SMSPING · 使用指南
用 Python 调用 SMSPing API 接收短信验证码
这篇教程用不到 80 行 Python 走完一个完整流程:查服务和国家、选一个有库存的报价、购买一次性号码、等短信、确认订单。下面所有接口和规则都来自接码 API 指南和开发者文档。上线前请再对照文档确认最新的字段和限制。
购买号码会扣除 SMSPing 账户里的真实余额。下面的脚本默认只做演练(dry run),加上
--buy才会真正购买。
准备
- Python 3.8 或更高版本,以及
requests(pip install requests)。 - 一个有余额的 SMSPing 账户和 API 密钥:登录后在账户设置的 API 管理中生成密钥,完整密钥只显示一次。
- 密钥只放在服务端,比如环境变量
SMSPING_API_KEY。不要写进浏览器代码、URL 或公开仓库。
接口一览
所有接口都在 https://smsping.net/api/customer 下。
| 步骤 | 请求 | 是否需要密钥 |
|---|---|---|
| 列出服务和国家 | GET /catalog |
否 |
| 某个组合的价格和库存 | GET /catalog/offers?country=<id>&service=<id> |
否 |
| 购买号码 | POST /orders |
是 |
| 查询订单和短信 | GET /orders/:id |
是 |
| 收到短信后完成 | POST /orders/:id/finish |
是 |
| 等待中取消 | POST /orders/:id/cancel |
是 |
成功响应是 {"code": 0, "data": ...}。失败时返回非 2xx 状态码,响应体里有 message,通常还有 errorCode,比如 INSUFFICIENT_BALANCE(余额不足)或 PRICE_CHANGED(价格变了)。
第 1 步:查服务和国家的 ID
GET /catalog 是公开接口,返回 data.services 和 data.countries,每一项都有一个 id,后面的请求要用:
curl -s https://smsping.net/api/customer/catalog
比如 Telegram 是 telegram,美国是 usa。请以接口返回为准,不要自己猜。
第 2 步:选报价
GET /catalog/offers 必须同时带 country 和 service。每个报价有 operator(运营商)、price(币种见响应里的 currency,即 USD)和 stock(库存)。脚本只保留有库存的报价并选最便宜的,这是示例自己的选择,你也可以固定某个运营商。
第 3 步:带幂等键购买
POST /orders 需要 country、service、operator、expectedPrice(刚查到的价格)和 idempotencyKey(16 到 100 位,只能是字母、数字、_ 或 -)。
- 发请求之前先把幂等键存下来。如果请求超时,同一笔购买要用同一个键、同样的参数重试;换新键可能会再买一单。
- 遇到
PRICE_CHANGED,重新查报价,确认新价格后再买。 - HTTP 200 不代表已经分配到号码,要看
data.status。如果是review,保留订单号,查询该订单或联系客服,不要重复购买。
第 4 步:轮询验证码
调用 GET /orders/:id,两次查询至少间隔 5 秒。status 变成 received 时,验证码在 code 字段里(短信内容在 sms 里)。其他终态有 finished、canceled、timeout、failed 和 review。脚本里 10 分钟停止轮询是我们自己设的本地保护,不是 API 规定。
第 5 步:完成或取消
收到短信后调用 POST /orders/:id/finish。还在等待时可以调用 POST /orders/:id/cancel。只有确认取消或超时且没收到短信才会退款,所以请检查最终的 status 和 refunded 字段。
频率限制
目前每个账户每分钟 60 次请求。收到 HTTP 429 时,按 Retry-After 头里的秒数等待后,重发同一个请求。如果没有这个头,脚本默认等 5 秒,这是示例自己的选择。
完整脚本
"""Receive an SMS verification code with the SMSPing API (Python 3.8+, `pip install requests`).
Set SMSPING_API_KEY in your environment. Nothing is bought unless you pass --buy.
"""
import os
import sys
import time
import uuid
import requests
API = "https://smsping.net/api/customer"
def call(method, path, *, params=None, body=None, auth=False):
headers = {"Accept": "application/json"}
if auth:
headers["Authorization"] = "Bearer " + os.environ["SMSPING_API_KEY"]
for _ in range(5):
r = requests.request(method, API + path, params=params, json=body,
headers=headers, timeout=30)
if r.status_code == 429: # rate limited: wait Retry-After seconds, then resend the same request
time.sleep(float(r.headers.get("Retry-After") or 5))
continue
payload = r.json()
if not r.ok or payload.get("code") != 0:
raise RuntimeError(f"HTTP {r.status_code} {payload.get('errorCode')}: {payload.get('message')}")
return payload["data"]
raise RuntimeError("still rate limited after 5 tries")
def cheapest_offer(country, service):
data = call("GET", "/catalog/offers", params={"country": country, "service": service})
in_stock = [o for o in data["offers"] if o["stock"] > 0]
if not in_stock:
sys.exit("No stock for this country/service right now.")
return min(in_stock, key=lambda o: o["price"])
def wait_for_code(order_id, max_wait=600):
deadline = time.time() + max_wait
while time.time() < deadline:
order = call("GET", f"/orders/{order_id}", auth=True)
status = order["status"]
if status == "received":
return order
if status in ("canceled", "timeout", "failed", "review", "finished"):
sys.exit(f"Order ended with status={status}, refunded={order.get('refunded')}")
time.sleep(5) # the docs ask for at least 5 seconds between queries
sys.exit("No SMS yet; cancel the order or check it again later.")
def main():
country, service = "usa", "telegram" # IDs come from GET /catalog
offer = cheapest_offer(country, service)
order_body = {
"country": country,
"service": service,
"operator": offer["operator"],
"expectedPrice": offer["price"],
"idempotencyKey": uuid.uuid4().hex, # keep it: reuse it if you retry this same purchase
}
print("Cheapest offer:", offer)
if "--buy" not in sys.argv:
print("Dry run. Would POST /orders with:", order_body)
return
order = call("POST", "/orders", body=order_body, auth=True) # spends real balance
if order["status"] not in ("pending", "waiting"):
sys.exit(f"Not waiting for SMS: status={order['status']}") # HTTP 200 alone is not success
print("Number:", order["phone"])
order = wait_for_code(order["id"])
print("Code:", order["code"])
call("POST", f"/orders/{order['id']}/finish", auth=True)
if __name__ == "__main__":
main()
运行:
pip install requests
export SMSPING_API_KEY="你的密钥" # 账户设置 > API 管理
python receive_sms.py # 演练:只显示报价和将要提交的订单
python receive_sms.py --buy # 真正购买(扣真实余额)并等待验证码
注意事项
- 密钥只能访问所属账户的数据;重置、停用密钥,或修改密码、邮箱后,旧密钥会失效。
- 号码是一次性激活号码。购买前请确认目标服务接受你选择的号码类型。
常见问题
查价格需要 API 密钥吗?
不需要。/catalog 和 /catalog/offers 是公开的;订单、短信和余额接口需要 Bearer 密钥。
购买请求超时了,要换新的幂等键重试吗?
不要。用同一个 idempotencyKey 和同样的参数重试,并检查已有订单。只有新的、独立的购买才用新键。
多久可以查一次短信?
至少间隔 5 秒,同时不超过账户每分钟 60 次的限制。
准备开始?
登录平台生成 API 密钥,并在开发者文档里核对当前接口、字段和限额。购买前,请确认所选号码类型被目标服务接受。
打开开发者文档