SMSPING · GUIDES
Receive SMS verification codes with the SMSPing API in Python
This tutorial walks through one complete flow in under 80 lines of Python: look up a service and a country, choose an offer that is in stock, buy a one-time number, wait for the SMS, and confirm the order. Every endpoint and rule below comes from the SMS receiving API guide and the developer documentation. Check the docs for the current fields and limits before you go to production.
Buying a number deducts real balance from your SMSPing account. The script below only does a dry run unless you add
--buy.
What you need
- Python 3.8 or newer and the
requestspackage (pip install requests). - An SMSPing account with balance, and an API key. Sign in and generate the key in Account Settings under API Management. The full key is shown once.
- Keep the key on your server. Put it in an environment variable such as
SMSPING_API_KEY, never in browser code, a URL or a public repository.
How the API is shaped
All endpoints live under https://smsping.net/api/customer.
| Step | Request | Key needed |
|---|---|---|
| List services and countries | GET /catalog |
No |
| Prices and stock for one pair | GET /catalog/offers?country=<id>&service=<id> |
No |
| Buy a number | POST /orders |
Yes |
| Read the order and the SMS | GET /orders/:id |
Yes |
| Confirm after the SMS arrives | POST /orders/:id/finish |
Yes |
| Cancel while still waiting | POST /orders/:id/cancel |
Yes |
A successful response looks like {"code": 0, "data": ...}. A failure comes back with a non-2xx status and a body with message and usually an errorCode such as INSUFFICIENT_BALANCE or PRICE_CHANGED.
Step 1: find the service and country IDs
GET /catalog is public. It returns data.services and data.countries, each with an id you pass to the other calls:
curl -s https://smsping.net/api/customer/catalog
For example, Telegram is telegram and the United States is usa. Look the IDs up rather than guessing them.
Step 2: pick an offer
GET /catalog/offers needs both country and service. Each offer has an operator, a price (in the currency field of the response, USD) and a stock. The script keeps offers with stock and picks the cheapest one. That rule is our choice for the example; you may prefer a specific operator.
Step 3: buy with an idempotency key
POST /orders takes country, service, operator, expectedPrice (the price you just saw) and an idempotencyKey of 16 to 100 letters, digits, _ or -.
- Save the key before you send the request. If the request times out, retry the same purchase with the same key and the same body. A new key can start a second purchase.
- If you get
PRICE_CHANGED, fetch offers again and confirm the new price before buying. - HTTP 200 does not prove a number was assigned. Check
data.status. If it isreview, keep the order ID, query that order or contact support, and do not buy again.
Step 4: poll for the code
Call GET /orders/:id and wait at least 5 seconds between queries. When status becomes received, the code is in code (and the messages in sms). The other final statuses are finished, canceled, timeout, failed and review. The 10-minute stop in the script is a local safety limit we chose, not an API rule.
Step 5: finish or cancel
After the SMS arrives, call POST /orders/:id/finish. While you are still waiting you can call POST /orders/:id/cancel. A refund happens only after a confirmed cancellation or timeout with no SMS, so check the final status and the refunded field.
Rate limits
The current limit is 60 requests per minute per account. On HTTP 429, wait the number of seconds in the Retry-After header and send the same request again. The script falls back to 5 seconds if the header is missing; that fallback is our choice.
The full script
"""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()
Run it:
pip install requests
export SMSPING_API_KEY="your-key" # from Account Settings > API Management
python receive_sms.py # dry run: shows the offer and the order it would place
python receive_sms.py --buy # buys a number (spends real balance) and waits for the code
Notes and limits
- A key can access only the data of its own account. Resetting or disabling it, or changing the password or email, invalidates the old key.
- Numbers are one-time activation numbers. Before buying, check that the target service accepts the number type you pick.
Help and FAQ
Do I need an API key to list prices?
No. /catalog and /catalog/offers are public. Orders, messages and balance need the Bearer key.
My purchase request timed out. Should I retry with a new key?
No. Retry with the same idempotencyKey and the same parameters, and check your existing orders. Use a new key only for a new, separate purchase.
How often can I check for the SMS?
At least 5 seconds apart, and within the 60 requests per minute account limit.
Ready to get started?
Sign in to the platform to generate an API key, and check the developer documentation for the current endpoints, fields and limits. Before purchasing, confirm that your chosen number type is accepted by the target service.
Open developer documentation