The SMS API sends one message with a JSON request, or many messages from an uploaded file, over direct operator connectivity. This page covers single sends, encoding and segments, bulk jobs and number lookup.
Endpoints at a glance#
| Method | Path | Use it to |
|---|---|---|
| POST | /sms/sendSms |
Send one SMS (JSON). Returns the message ID. |
| POST | /sms/identify |
Detect encoding and count characters and segments. |
| POST | /sms/sendbulk |
Send one message to a file of numbers (multipart). |
| GET | /sms/list |
List bulk jobs with processed and pending counts. |
| GET | /sms/edit |
Read the details of one bulk job. |
| GET | /sms/pause |
Pause a running bulk job. |
| GET | /sms/resume |
Resume a paused bulk job. |
| DELETE | /sms/abort |
Abort a bulk job; pending messages are not sent. |
| GET | /sms/listSchedule |
List scheduled campaigns. |
| POST | /sms/send-now |
Release a scheduled campaign immediately. |
| DELETE | /sms/abort/schedule |
Cancel a scheduled campaign. |
| POST | /sms/lookup |
Check a number's status (HLR / MNP) before sending. |
Send a single SMS#
POST /sms/sendSms takes a JSON body and returns 200 OK with the message ID as plain text.
Request fields#
| Field | Type | Required | Description |
|---|---|---|---|
senderId |
string | Yes | Your approved sender ID, shown to the recipient. |
destinationNumber |
string | Yes | Country code plus number, digits only. No +, 00, spaces or leading zero. |
message |
string | Yes | The message text. |
messageType |
string | Yes | TEXT for GSM-7 text, UNICODE for Arabic, emoji and other scripts. |
charCount |
integer | No | Character count, as returned by /sms/identify. |
smsParts |
integer | No | Number of segments, as returned by /sms/identify. |
charLimit |
integer | No | Characters per segment, as returned by /sms/identify. |
expiryHour |
integer | No | Validity period in hours. |
campaignName |
string | No | Your label, used in reporting. |
isSchedule |
boolean | No | true to schedule instead of sending now. |
time |
— | No | Scheduled send time (used with isSchedule). |
gmt |
string | No | GMT offset for the scheduled time, for example +04:00. |
repeat |
— | No | Repeat setting for scheduled sends. |
delay |
number | No | Delay before sending. |
peId |
string | No | Principal entity ID, where a regulator requires one. |
templateId |
string | No | Registered content template ID, where a regulator requires one. |
telemarketerId |
string | No | Telemarketer ID, where a regulator requires one. |
Use exactly these field names. Fields such as to, from or text are not part of this endpoint. Leave the scheduling fields out to send immediately; for scheduled campaigns, the bulk endpoints below are the better fit, and our team can confirm the expected time format for your account.
Example#
cURL
curl -X POST https://api.broadnet.me/sms/sendSms \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"senderId": "OmniDemo",
"destinationNumber": "971500000000",
"message": "رمز التحقق الخاص بك هو 482913",
"messageType": "UNICODE",
"campaignName": "login-otp"
}'Node.js
const res = await fetch("https://api.broadnet.me/sms/sendSms", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BROADNET_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
senderId: "OmniDemo",
destinationNumber: "971500000000",
message: "رمز التحقق الخاص بك هو 482913",
messageType: "UNICODE",
campaignName: "login-otp",
}),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const messageId = await res.text();Python
import os
import requests
r = requests.post(
"https://api.broadnet.me/sms/sendSms",
headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
json={
"senderId": "OmniDemo",
"destinationNumber": "971500000000",
"message": "رمز التحقق الخاص بك هو 482913",
"messageType": "UNICODE",
"campaignName": "login-otp",
},
timeout=15,
)
r.raise_for_status()
message_id = r.textPHP
<?php
$ch = curl_init("https://api.broadnet.me/sms/sendSms");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BROADNET_TOKEN"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"senderId" => "OmniDemo",
"destinationNumber" => "971500000000",
"message" => "رمز التحقق الخاص بك هو 482913",
"messageType" => "UNICODE",
"campaignName" => "login-otp",
], JSON_UNESCAPED_UNICODE),
]);
$messageId = curl_exec($ch);
curl_close($ch);The response body is the message ID:
Response
2603121407537573731Phone number format#
| Input | Valid? | Why |
|---|---|---|
447700900123 |
Yes | Country code 44 plus the national number, digits only. |
+44 7700 900123 |
No | Remove the + and spaces first. |
07700900123 |
No | National format: replace the leading 0 with the country code. |
Store numbers in E.164 (+447700900123) in your own database and strip the + when you build the request.
Encoding and segments#
messageType sets the encoding, and the encoding sets how many characters fit in each SMS segment. Long messages are split into segments and reassembled on the handset; most operators bill per segment.
messageType |
Single SMS | Per segment when split | Use for |
|---|---|---|---|
TEXT |
160 characters | 153 characters | Text that uses only the GSM-7 alphabet. |
UNICODE |
70 characters | 67 characters | Arabic, Cyrillic, Chinese, emoji, and characters outside GSM-7. |
A single non-GSM-7 character switches the whole message to Unicode. Common culprits are curly quotes, the em dash, the ellipsis character and emoji.
Let the API count for you#
POST /sms/identify analyses a message and returns its encoding, character count, segment count and per-segment limit. You can pass the values straight into a send request.
cURL
curl -X POST https://api.broadnet.me/sms/identify \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "message": "Your OmniDemo code is 482913" }'The response describes the message:
Response
{
"message": "Your OmniDemo code is 482913",
"messageType": "TEXT",
"smscount": 1,
"charCount": 28,
"charLimit": 160
}| Response field | Description |
|---|---|
messageType |
Detected encoding. GSM-7 text may be labelled GSM-7; send it as TEXT. |
smscount |
Number of segments needed. Map it to smsParts in a send request. |
charCount |
Total characters. |
charLimit |
Characters per segment: 160, 153, 70 or 67. |
Send in bulk#
POST /sms/sendbulk sends one message to every number in an uploaded file. It is a multipart/form-data request with two parts:
| Part | Type | Required | Description |
|---|---|---|---|
destinationNumberFile |
file | Yes | CSV or TXT file of numbers, one per line, in the same format as destinationNumber. |
bulkRequest |
string (JSON) | Yes | The message settings, as a JSON string. |
Fields inside bulkRequest:
| Field | Type | Required | Description |
|---|---|---|---|
senderId |
string | Yes | Your approved sender ID. |
message |
string | Yes | The message text. |
messageType |
string | Yes | TEXT or UNICODE. |
campaignName |
string | No | Your label for reporting. |
allowDuplicate |
boolean | No | false removes duplicate numbers from the file. |
expiryHour |
integer | No | Validity period in hours. |
delay |
number | No | Throttle between messages. |
schedule |
boolean | No | true to schedule the job. |
smsParts, charCount, charLimit |
integer | No | Values from /sms/identify. |
peId, templateId, telemarketerId |
string | No | Regulatory IDs, where required. |
cURL
curl -X POST https://api.broadnet.me/sms/sendbulk \
-H "Authorization: Bearer $BROADNET_TOKEN" \
-F destinationNumberFile=@recipients.csv \
-F 'bulkRequest={"senderId":"OmniDemo","message":"Your statement is ready in the app.","messageType":"TEXT","campaignName":"oct-statements","allowDuplicate":false}'Python
import json
import os
import requests
bulk_request = {
"senderId": "OmniDemo",
"message": "Your statement is ready in the app.",
"messageType": "TEXT",
"campaignName": "oct-statements",
"allowDuplicate": False,
}
with open("recipients.csv", "rb") as f:
r = requests.post(
"https://api.broadnet.me/sms/sendbulk",
headers={"Authorization": f"Bearer {os.environ['BROADNET_TOKEN']}"},
files={"destinationNumberFile": ("recipients.csv", f, "text/csv")},
data={"bulkRequest": json.dumps(bulk_request)},
timeout=60,
)
print(r.json())A successful upload returns a job summary:
Response
{
"status": "success",
"bulkSessionId": "1_20260228105000887",
"credits": "9966465.42850",
"deductcredits": "0.60000",
"bulkListInfo": {
"total": 4,
"validCount": 3,
"invalidCount": 0,
"duplicate": 1,
"errors": {}
}
}bulkListInfo tells you how many numbers were accepted, rejected as invalid, or removed as duplicates. credits is your remaining balance and deductcredits the amount charged for the job. Store bulkSessionId with your campaign record.
Control bulk jobs#
Running jobs are identified by the id returned from GET /sms/list. Pass it as the batch_Id query parameter to the other job endpoints.
cURL
# 1. List jobs: each entry has an id, total, processed, pending and active
curl https://api.broadnet.me/sms/list \
-H "Authorization: Bearer $BROADNET_TOKEN"
# 2. Pause, resume or abort a job by its id
curl "https://api.broadnet.me/sms/pause?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"
curl "https://api.broadnet.me/sms/resume?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"
curl -X DELETE "https://api.broadnet.me/sms/abort?batch_Id=1024" -H "Authorization: Bearer $BROADNET_TOKEN"Each entry in the list looks like this (abridged):
GET /sms/list
[
{
"id": 1024,
"senderId": "OmniDemo",
"reqType": "bulk",
"messageType": "GSM-7",
"campaignName": "oct-statements",
"total": 1000,
"processed": 450,
"pending": 550,
"active": true,
"createdOn": "2026-10-08T21:15:20"
}
]| Action | Call | Notes |
|---|---|---|
| Inspect | GET /sms/edit?batch_Id=… |
Full details of one job. |
| Pause | GET /sms/pause?batch_Id=… |
Only active jobs can be paused. |
| Resume | GET /sms/resume?batch_Id=… |
Continues from where it stopped. |
| Abort | DELETE /sms/abort?batch_Id=… |
Permanent; pending messages are not sent. |
GET /sms/list is not paginated; it returns all of your bulk jobs in one response.
Scheduled campaigns#
GET /sms/listSchedule returns your scheduled campaigns, each with an id. Use that ID with POST /sms/send-now to release a campaign immediately, or with DELETE /sms/abort/schedule?scheduleId=… to cancel it.
Look up a number before sending#
POST /sms/lookup checks a number against the network (HLR and MNP) so you can clean lists and route ported numbers correctly. Pass the number in the destinationNumber query parameter. The optional reqType parameter selects the kind of lookup; ask our team which values are enabled on your account.
cURL
curl -X POST "https://api.broadnet.me/sms/lookup?destinationNumber=447700900123" \
-H "Authorization: Bearer $BROADNET_TOKEN"Lookup traffic has its own default rate limit. See Errors and limits.
High volume over SMPP#
If you already run an SMPP client or need sustained high throughput, the same SMS routes are available over SMPP v3.4 with TLS. See SMPP.
Next steps#
- Webhooks: receive
DELIVERED,FAILEDand other statuses. - Errors and limits: retries, rate limits and duplicate protection.
- RCS: upgrade SMS to branded rich messages where handsets support it.
- SMPP: bind for high-volume SMS.
Ready to send your first message?
Request test credentials. An engineer sets up your account, and test sends go to your own verified numbers.
Request test access