On this page
What this API does, and what it does not
Most SMS gateway APIs are built to send: you post a message and a carrier delivers it. This one is built to receive. It answers one question well, what code just arrived on my number?, for numbers on phones you have enrolled and verified.
- Yes: read verification and one-time codes, wait for the next one, list your gateway phones and numbers, and read recently captured messages.
- No: sending SMS, bulk or marketing campaigns, or renting and buying numbers. BusySMS supplies no numbers; you bring the SIM.
If you need to send messages, use a messaging provider. If you need the code a website texts to your number, this is the API. The phone side is covered in the Android SMS gateway guide.
Authentication
Create a key in the dashboard under REST API. It starts with bsk_live_ and is shown once, so store it as a secret, for example BUSYSMS_API_KEY. Send it on every request, in either header:
Authorization: Bearer bsk_live_xxxxxxxx
# or
X-API-Key: bsk_live_xxxxxxxxA key belongs to a person and only sees that person's phones, plus numbers shared with them. A key made in the dashboard can read codes, list your phones and numbers, read your account status and wake a paused phone; it cannot delete a device or manage your account, so a leaked key cannot change what you are billed. Keep keys server-side all the same: anyone holding one can read your codes.
Endpoints
Base URL https://busysms.net/api/v1. Requests are plain GETs and responses are JSON.
GET /codes: the newest code across your numbers. WithtimeoutMsit waits for the next one, up to 90 seconds. Filters:service,phone,deviceId.GET /devices: your gateway phones with their id, name, number, platform, and online, verified and paused state, plus numbers shared with you.GET /numbers: just your numbers, each withcanReceiveand, when it cannot, areason:unverified,verification_pending,inactive,gateway_offorphone_unreachable.GET /devices/:id/messages: recent captured messages for one phone, newest first. Takesservice,limit(50 by default, 200 at most) andsince.GET /account: your account with its trial and subscription status.
Wait for a code: curl, Node.js and Python
The call most integrations need is a long-poll straight after something triggers an SMS. Start the wait, trigger the sign-in, and the request returns as soon as a matching code lands.
curl -s -H "Authorization: Bearer $BUSYSMS_API_KEY" \
"https://busysms.net/api/v1/codes?service=exampleapp&timeoutMs=60000"const params = new URLSearchParams({ service: "exampleapp", timeoutMs: "60000" });
const res = await fetch(`https://busysms.net/api/v1/codes?${params}`, {
headers: { Authorization: `Bearer ${process.env.BUSYSMS_API_KEY}` },
});
if (res.status === 402) throw new Error("Trial over or subscription inactive");
if (!res.ok) throw new Error(`BusySMS ${res.status}: ${await res.text()}`);
const { code, message } = await res.json();
if (code) console.log(`${code} from ${message.sender}`);
else console.log("No new code within 60 seconds");import os
import requests
r = requests.get(
"https://busysms.net/api/v1/codes",
params={"service": "exampleapp", "timeoutMs": 60000},
headers={"Authorization": f"Bearer {os.environ['BUSYSMS_API_KEY']}"},
timeout=75,
)
if r.status_code == 402:
raise RuntimeError("Trial over or subscription inactive")
r.raise_for_status()
data = r.json()
if data["code"]:
print(data["code"], "from", data["message"]["sender"])
else:
print("No new code within 60 seconds")Order matters less than you might fear: a code that arrived up to about a second before the request started still counts as new, and anything older does not. In tests, start the request first and trigger the SMS second; the OTP automation guide shows how in Playwright, Selenium and Cypress.
What comes back
Every /codes response has the same shape (the message object is abridged here):
{
"code": "482913",
"message": {
"otpCode": "482913",
"phone": "+447700900123",
"sender": "ExampleApp",
"service": "exampleapp",
"serviceLabel": "ExampleApp",
"bodyPreview": "Your ExampleApp verification code is 482913",
"receivedAt": 1792674000000,
"deviceName": "Test phone"
}
}code is null when nothing new arrived in time: while waiting, the API never falls back to an older code. receivedAt is in epoch milliseconds. service is a short id BusySMS gives each message: a known brand such as google, telegram or microsoft, otherwise a slug of the sender name. Pass it as service= to wait only for that sender's code.
Errors and how to handle them
401: the key is missing, malformed or revoked. A revoked key says so withkey_revoked.402 payment_required: the trial ended and there is no active subscription. Reading codes stops, but/devicesstill answers so you can see what is wrong, and the body carries abillingUrl.403: the device belongs to another account, or the key lacks the scope for the route (insufficient_scope).404: no such device.409 number_unverified: that phone's number has not been verified yet, so its messages cannot be read.429: rate limited. WaitretryAfterMsbefore retrying.
Limits and good practice
- A single wait lasts at most 90 seconds. If a code can take longer, call again with a fresh timeout instead of holding one request open.
- With several numbers, filter:
phone=with the receiving number, ordeviceId=for one of your phones, so a code meant for another sign-in is not returned. - If none of your phones is capturing when a wait starts, BusySMS sends the phone a notification asking for the gateway to be switched back on. It never switches capture on remotely; a person taps.
- Captured messages are kept for up to 7 days, then deleted.
- Reading codes needs an active trial or subscription: $5 per verified number per month after a 7-day free trial. There is no per-request charge.
Beyond the REST API
The same codes are available to AI agents through the BusySMS MCP server (see SMS codes for AI agents), and to people through the Chrome extension, which fills the code into the website for you.
Numbers shared with you are included too. When a teammate shares a number's codes, /codes returns them alongside your own, and phone= picks out that number.
FAQ
Frequently asked questions
Is there a free SMS gateway API?
Every account starts with a 7-day free trial with full API access. After that it is $5 per verified phone number per month. The price is per number, not per message or per request.
Can I send SMS with this API?
No. BusySMS only receives: it reads verification codes that arrive on your own numbers. For sending, use a messaging provider.
Do I get a phone number from BusySMS?
No, BusySMS does not provide numbers. Install the app on a phone with the SIM you want and verify the number; its codes are then yours to read. An iPhone works too, through a Shortcuts automation.
Can teammates read the same number's codes?
Yes. Share the number with up to five other BusySMS users; once they accept, new codes on it reach their dashboard, apps, Chrome extension, API and MCP, at no cost to them.
Can I call the API from a browser or a mobile app?
Keep the key on a server: anyone holding it can read your codes. For a browser, use the Chrome extension, which signs in with your BusySMS account instead of a key.
Blog
More from the blog
All articlesKeep reading
Related guides
All guidesReceive SMS API
Receive codes through an API on numbers you own, and how that differs from "receive SMS online" virtual-number services.
Read the guideAutomate OTP in tests
Automate SMS two-factor codes in Playwright, Selenium, Cypress and plain scripts by waiting for the code over the API.
Read the guideSMS codes for AI agents
Let Claude, Codex or any MCP client wait for SMS verification codes through the BusySMS MCP server.
Read the guideTry it on a number you own
Install the app, verify your number and read your first code today. Every account starts with a 7-day free trial; after that it is $5 per verified number per month, and you can cancel any time.
