Getting automated SMS verification working is easy; making it reliable needs an error-handling strategy. Building on Automated ordering and polling, this tutorial covers the four common failure cases: no code arrives, timeouts, rate limits, and duplicate orders.

Prerequisite: read Get started in 5 minutes first so you can complete the minimal order -> receive -> cancel flow.
1. Separate the two kinds of “failure”
- Business failure: the request succeeded, but the outcome is bad (timeout with no code, number rejected). You should switch to a new number and retry, not hammer the same one.
- Transport/service failure: network blips, HTTP 429/5xx. You should back off and retry the same request.
These two are handled completely differently — never blindly retry everything.
2. Polling: set a cadence and a timeout
Don’t poll at high frequency right after ordering. Use a fixed interval plus a total timeout:
interval = 3s # check every 3 seconds
max_wait = 120s # wait at most 2 minutes
while elapsed < max_wait:
order = GET /api/sms/orders/{id}
if order.status == "received": return order.latestCode
if order.status in ("canceled", "expired"): break
sleep(interval)
# timed out with no code -> cancel and switch number
Rule of thumb: if no code after 2–3 minutes, treat it as failed — switching numbers beats waiting.
3. No code: auto-cancel and switch numbers
A reliable platform doesn’t charge for uncoded orders, so cancel on timeout and order again:
curl -X POST https://api.simsmsbox.com/api/sms/orders/19/cancel \
-H "X-API-Key: psk_xxxxxxxx"
After a successful cancel, run the ordering flow again. Give each task a switch cap (e.g. at most 3 numbers) so you don’t loop forever burning time.
4. Rate limits and 5xx: exponential backoff
On 429 Too Many Requests or 5xx, retry the same request with exponential backoff:
delays = [1s, 2s, 4s, 8s] # up to 4 retries
for d in delays:
resp = request()
if resp.ok: break
if resp.status in (429, 500, 502, 503, 504):
sleep(d + random_jitter()) # add jitter so retries don't sync up
else:
break # 4xx business errors: don't retry
Note: 4xx (except 429) usually means bad params or auth — retrying is pointless; fix and report instead.
5. Idempotency: avoid duplicate orders
When retrying the purchase (order) endpoint, if the previous call actually succeeded but its response was lost, a blind retry double-charges you. Handle it by:
- attaching your own reference id to each purchase and de-duplicating server-side / locally;
- querying “recent orders” before retrying to confirm whether the last one already exists;
- only retrying read (GET) endpoints freely — write (POST) endpoints need de-duplication.
6. A complete loop skeleton
for attempt in 1..MAX_NUMBER_SWITCH: # switch cap
order = purchase() # with idempotency key + backoff
code = poll(order.id, interval=3s, max_wait=120s)
if code: return code # success
cancel(order.id) # no code: cancel and switch
return FAIL # hit the switch cap, raise an alert
Summary
Reliable automated verification = separate the two failure types + a sane polling cadence + switch numbers on no-code + exponential backoff + idempotent de-duplication. Bake these into your integration code, pair it with real SIM-backed numbers, and you can control both success rate and cost at scale. Next, see Custom receive URL and templated responses.