Automate SMS Verification: A Complete API Guide
Everything from creating an API key to placing an order and polling for the code — real curl examples, rate limits, and the gotchas that trip people up.
The website is fine for the occasional signup: click a few times, wait for one text. It stops being fine when you're writing automated tests, a bulk-signup script, a CI pipeline, or a bot that needs to grab a verification code unattended. The website can't do that. The API can. This is everything you need to go from zero to a working flow.
Step 1: Get an API key
Log in and go to /account/api-keys to create one. Keys look like jm_ followed by a random string. The plaintext is shown exactly once — we store a hash, not the key itself, so there's no "recover my key" option if you lose it. Delete it and make a new one.
Every request uses standard Bearer auth:
Authorization: Bearer jm_your_keyTreat the key like a password — don't commit it to a public repo, don't paste it into a chat for someone else to debug with. If you suspect it leaked, go back to /account/api-keys, delete it, and issue a new one; the old one stops working immediately.
Step 2: Pick a service and a country
Placing an order needs two things: service (a service code) and country (a country id). Start by listing the catalog:
curl https://jiema.my/api/v1/services \
-H "Authorization: Bearer jm_your_key"Each item has a code field (Telegram is tg) — pass that directly to the order endpoint, it's more reliable than reconstructing a slug yourself. To see live pricing and stock for a specific service across countries, add a service param:
curl "https://jiema.my/api/v1/prices?service=tg" \
-H "Authorization: Bearer jm_your_key"Each entry in items has countryId / priceCents / count (current available numbers). Check count before ordering — zero means the order will fail, so don't waste a call finding that out the hard way.
Step 3: Place the order
curl -X POST https://jiema.my/api/v1/orders \
-H "Authorization: Bearer jm_your_key" \
-H "Content-Type: application/json" \
-d '{"service":"tg","country":"6"}'A successful order returns a phone number and an expiry:
{
"ok": true,
"data": {
"id": "cm...",
"status": "WAITING",
"phone": "62812xxxxxxx",
"expiresAt": "2026-08-01T12:15:00.000Z",
"chargedCents": "40"
}
}The charge happens at this step — chargedCents is what was actually deducted (in cents). Hand this number to the target app to receive the verification code, then move to the next step.
Step 4: Poll for the code
There's no WebSocket or webhook push — you get the SMS content by polling GET /api/v1/orders/:id until smsBody stops being null:
while true; do
RESP=$(curl -s https://jiema.my/api/v1/orders/$ORDER_ID \
-H "Authorization: Bearer jm_your_key")
BODY=$(echo "$RESP" | jq -r '.data.smsBody')
if [ "$BODY" != "null" ]; then
echo "Code received: $BODY"
break
fi
sleep 5
doneFive seconds is a reasonable starting point — the number is valid for 15 minutes, and the 60-requests-per-minute query limit leaves plenty of headroom. Three seconds works too if you're impatient; polling once a second doesn't get you the code any faster, it just burns through your rate limit.
Gotchas
- Rate limits are per user, not per key. Write endpoints (order / cancel / next-sms) are capped at 10/minute per user; queries at 60/minute. Creating extra keys doesn't get you a higher ceiling — they all share the same one.
- Numbers expire after 15 minutes. An unused number that expires without receiving a code is refunded automatically — no need to ask. But if your own pipeline sits on the number too long before actually using it (stuck in a queue, say), it'll be dead by the time you get to it.
- One number can receive more than one code. If the target app sends SMS in two steps (a signup confirmation, then a separate login code), call
POST /api/v1/orders/:id/next-smsafter the first one arrives to tell us "done with this one, keep listening" — no need to place a new order for a new number. - Once a code arrives, cancel stops working.
POST /api/v1/orders/:id/cancelrefunds instantly while the number is still waiting for a code. OncesmsBodyhas ever been non-null, the same call returns aCODE_RECEIVEDerror instead — you already got what you paid for, so there's no walking that back. - Don't just check status once and give up.
statusmoves fromWAITINGtoRECEIVED. If it sits atWAITINGuntil expiry, that's usually a delivery-rate issue with that particular country/service combination — placing a fresh order in a different country tends to resolve faster than waiting it out.
Where to go from here
That covers the main flow from order to code. The full field list, error codes, and edge cases for every endpoint live at /api-docs. If you're running this at scale — say, keeping dozens of accounts' verification flows alive at once — time each order's polling loop independently. Don't queue them through a single serial loop, or the earlier numbers will expire while you're still waiting on the first one.
Earn 10% on every order from anyone you invite
No cap, no expiry. Share your link, collect a commission for the lifetime of every account that signs up through it.
Related articles
Number Renewal Is Here: Keep a Number That Already Worked
jiema.my now lets you renew a number that already received a code, extending it for more hours instead of buying a new one — and the renew button only shows up when it's genuinely available.
An Open-Source Comparison of SMS Verification Services
A community-maintained, open-source list on GitHub compares the main SMS verification services across price, countries, payment and API — and where jiema.my fits.
What Is SMS Verification? OTP Codes Explained
A plain-English explanation of SMS verification and one-time passcodes (OTP): how they work, why apps use them, and how temporary phone numbers fit in.