Quick start
- Open an SMS account. Register in the SMS portal with your work email and company details, then confirm your email. SMS has its own account, balance and credentials, separate from the WhatsApp platform.
- Get a sender ID and a balance. Request your sender ID in the portal and top up your prepaid balance. We register the sender ID and submit it for approval before your first send.
- Create an app. In the portal's Developer area, create an app and choose HTTP (REST API), or SMPP for high-volume systems. Each app has its own credentials.
- Send your first SMS. Call
POST /v2/sendwith your token, as in the example above, then read its delivery status.
Authentication
Every REST call sends your app's token in the Authorization header:
Authorization: Bearer YOUR_TOKEN
Keep the token on your server and never put it in a web page or mobile app. A call without a valid token gets an HTTP 401 error (see Errors).
Endpoints
| Method | Path | What it does |
|---|---|---|
| POST | /v2/send | Send an SMS |
| GET | /v2/status/{message_id} | Read the delivery status of one message |
| GET | /v2/account/balance | Read your prepaid balance |
All paths are relative to https://api.unecast.com, so the full REST base is https://api.unecast.com/v2. Requests and responses use JSON.
Send an SMS
POST https://api.unecast.com/v2/send with a JSON body:
| Field | Example | What it is |
|---|---|---|
channel | "sms" | Always sms for this API. |
from | "YOURBRAND" | Your sender ID. It must be approved and active in your portal. |
to | "94771234567" | The recipient: country code followed by the number, as in the example. |
message | "Your OTP is 482913" | The text of the SMS. See Length and languages. |
priority | 3 | A number from 0 (lowest) to 3 (highest). See Priority levels. |
curl -X POST https://api.unecast.com/v2/send \
-H "Authorization: Bearer $UNECAST_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"from": "YOURBRAND",
"to": "94771234567",
"message": "Your OTP is 482913",
"priority": 3
}'// Node.js 18+ (save as send.mjs)
const res = await fetch("https://api.unecast.com/v2/send", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.UNECAST_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
channel: "sms",
from: "YOURBRAND",
to: "94771234567",
message: "Your OTP is 482913",
priority: 3
})
});
console.log(res.status, await res.json());# Python 3 with requests
import os
import requests
resp = requests.post(
"https://api.unecast.com/v2/send",
headers={"Authorization": f"Bearer {os.environ['UNECAST_TOKEN']}"},
json={
"channel": "sms",
"from": "YOURBRAND",
"to": "94771234567",
"message": "Your OTP is 482913",
"priority": 3,
},
timeout=10,
)
print(resp.status_code, resp.json())// PHP 7.4+ with the curl extension
$ch = curl_init("https://api.unecast.com/v2/send");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("UNECAST_TOKEN"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"channel" => "sms",
"from" => "YOURBRAND",
"to" => "94771234567",
"message" => "Your OTP is 482913",
"priority" => 3,
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;Priority levels
| Value | Level | Use it for |
|---|---|---|
3 | Highest | OTPs and alerts: anything the recipient is waiting for. |
2 | High | General messaging. |
1 | Low | Low-urgency messages. |
0 | Lowest | Messages that are not urgent, such as notifications and marketing campaigns. |
Keep 3 for truly time-critical messages, and send bulk campaigns at 0. The same levels apply to priority_flag over SMPP.
Keep the message ID from the send response: you need it to read the delivery status.
Length and languages
An SMS carries 160 characters in the standard GSM alphabet, or 70 in Unicode. Longer texts are split into parts and joined again on the phone; each part then carries a little less, because some space holds the joining information.
| Text uses | One SMS | Each part of a long SMS |
|---|---|---|
| Standard GSM alphabet (English letters, digits, common symbols) | 160 characters | 153 characters |
| Unicode (Sinhala, Tamil, emoji and other scripts) | 70 characters | 67 characters |
One character outside the GSM alphabet switches the whole message to Unicode. The symbols { } [ ] ~ \ ^ | € are in the GSM alphabet but count as two characters each. Your portal's message details show each message's part count and cost.
Delivery status
Read one message's status with GET /v2/status/{message_id}:
curl https://api.unecast.com/v2/status/MESSAGE_ID \ -H "Authorization: Bearer $UNECAST_TOKEN"
| Status | What it usually means |
|---|---|
pending | Accepted by Unecast and waiting to go out. |
sent | Handed to the mobile network; the delivery report has not arrived yet. |
delivered | The network reported delivery to the phone. |
undelivered | The network could not deliver it, for example to an unreachable number. |
expired | The network could not deliver it in time, for example because the phone stayed off. |
rejected | Refused before delivery. |
failed | Could not be sent. |
The same statuses, with each message's cost, appear in the portal's message log.
Rate limits
The standard limit is 600 SMS submissions a minute, an average of 10 a second. Spread your sends evenly rather than in bursts:
import time
for number in numbers:
send_sms(number) # your call to POST /v2/send
time.sleep(0.1) # 10 a second, 600 a minute- Large lists: use a campaign in the portal, which can also be scheduled.
- Sustained high volume: connect over SMPP. To raise its throughput, open a support ticket in the portal under Messaging - SMS and choose Increase TPS (SMPP).
- Need more on REST? Tell us your volume and we will look at your limit.
Sender IDs and countries
A sender ID is the name your customers see instead of a number, such as your brand. Request it in the portal under Sender ID settings. It shows as Pending while we register it and submit it for approval, then Active; a rejected request shows the reason. Only active sender IDs can send.
Sri Lanka is live on all major mobile networks, in any language. Other countries are switched on per account: request them in the portal's Country settings or tell us where you need to send.
SMPP
SMPP suits platforms that send high volumes or already speak SMPP. The Unecast SMSC uses the standard SMPP 3.4 protocol, so any standard SMPP client or library can connect. Log in with the system ID and password of an SMPP app you create in the portal's Developer area.
| Setting | Value |
|---|---|
| Address | smpp://smsc.unecast.com:2775 |
| Host and port | smsc.unecast.com, port 2775 |
| Protocol | SMPP 3.4 (interface_version 0x34) |
system_id and password | The login of your SMPP app. Each app has its own. |
| Bind | bind_transceiver sends and receives delivery receipts on one connection. A bind_transmitter and bind_receiver pair also works. |
| Connection limit | How many binds the app can hold open at once, set on the app. The minimum is 1. |
| Allowed IPs | Allow every IP address or host, or only the server IPs and hosts you list on the app. |
| Throughput | Raised on request: Support → Messaging - SMS → Increase TPS (SMPP). |
A session, step by step
PDU You SMSC Notes bind_transceiver ---------> system_id, password bind_transceiver_resp <--------- command_status 0x00000000 submit_sm ---------> sender ID, number, text submit_sm_resp <--------- message_id deliver_sm <--------- delivery receipt deliver_sm_resp ---------> enquire_link ---------> about every 30 s when idle enquire_link_resp <--------- unbind ---------> unbind_resp <---------
submit_sm fields
| Field | Set it to |
|---|---|
source_addr | Your active sender ID. For a name such as YOURBRAND, use source_addr_ton 5 (alphanumeric) and source_addr_npi 0. For a number, use TON 1 and NPI 1. |
destination_addr | Country code followed by the number, such as 94771234567, with dest_addr_ton 1 (international) and dest_addr_npi 1 (E.164). |
data_coding | 0 for the GSM alphabet, 8 for Unicode (UCS-2): Sinhala, Tamil, emoji and other scripts. |
short_message | Up to 140 bytes: 160 GSM characters or 70 Unicode characters. |
esm_class | 0x00 for a single SMS; 0x40 when short_message starts with a concatenation header (one part of a long SMS). |
priority_flag | 0 (lowest) to 3 (highest), the same levels as the REST priority: 3 for OTPs and alerts, 0 for notifications and marketing. |
registered_delivery | 1 to get a delivery receipt. |
Long messages: split the text into parts of up to 153 GSM or 67 Unicode characters, start each part with a 6-byte concatenation header (the same reference number in every part, plus the part count and part number), and set esm_class to 0x40. Most SMPP libraries do this for you, as in the example below.
Delivery receipts
With registered_delivery set to 1, the receipt comes back as a deliver_sm with esm_class 0x04. It carries the message ID from your submit_sm_resp in the receipted_message_id field, and the standard receipt text in short_message:
id:MESSAGE_ID sub:001 dlvrd:001 submit date:2611051030 done date:2611051030 stat:DELIVRD err:000 text:Your OTP is 482913
stat | What it usually means |
|---|---|
DELIVRD | Delivered to the phone. |
UNDELIV | The network could not deliver it, for example to an unreachable number. |
EXPIRED | Not delivered in time, for example because the phone stayed off. |
REJECTD | Refused before delivery. |
ACCEPTD, DELETED, UNKNOWN | Other final states defined by SMPP 3.4. |
Answer every deliver_sm with a deliver_sm_resp, or the receipt may be sent again.
Keep the connection healthy
- Keep it alive: send
enquire_linkabout every 30 seconds when the link is idle, and answer the SMSC'senquire_linkwithenquire_link_resp. - Reconnect calmly: if the connection drops, wait a few seconds before you bind again, and wait longer after each failed try.
- Limit what is in flight: keep only a small window of
submit_smwaiting for a response, and stay within your throughput. - Slow down when asked: on
0x00000058(throttled) or0x00000014(queue full), pause and retry that message later. - Close cleanly: send
unbindand wait forunbind_respbefore you disconnect.
Example: send over SMPP
# Python 3 with smpplib (pip install smpplib) import os import smpplib.client import smpplib.consts import smpplib.gsm client = smpplib.client.Client("smsc.unecast.com", 2775) # submit_sm_resp carries the message ID; deliver_sm carries the delivery receipt client.set_message_sent_handler( lambda pdu: print("accepted", pdu.message_id)) client.set_message_received_handler( lambda pdu: print("receipt", pdu.receipted_message_id, pdu.short_message)) client.connect() client.bind_transceiver(system_id=os.environ["SMPP_SYSTEM_ID"], password=os.environ["SMPP_PASSWORD"]) # Splits long text, picks GSM (0) or UCS2 (8) and adds the UDH when needed parts, data_coding, esm_class = smpplib.gsm.make_parts("Your OTP is 482913") for part in parts: client.send_message( source_addr_ton=smpplib.consts.SMPP_TON_ALNUM, source_addr_npi=smpplib.consts.SMPP_NPI_UNK, source_addr="YOURBRAND", dest_addr_ton=smpplib.consts.SMPP_TON_INTL, dest_addr_npi=smpplib.consts.SMPP_NPI_ISDN, destination_addr="94771234567", short_message=part, data_coding=data_coding, esm_class=esm_class, priority_flag=3, # 0 lowest to 3 highest: 3 for OTPs and alerts registered_delivery=True, ) client.listen() # answers receipts with deliver_sm_resp and sends enquire_link when idle
Common command_status codes
| Code | Name | What to do |
|---|---|---|
0x00000000 | ESME_ROK | Success. |
0x00000005 | ESME_RALYBND | Already bound: don't bind twice on one connection. |
0x0000000D | ESME_RBINDFAIL | Bind failed: check the connection limit and allowed IPs on your SMPP app. |
0x0000000E | ESME_RINVPASWD | Wrong password. |
0x0000000F | ESME_RINVSYSID | Wrong system ID. |
0x0000000A | ESME_RINVSRCADR | Sender ID not valid: use an active sender ID and the right TON and NPI. |
0x0000000B | ESME_RINVDSTADR | Recipient number not valid: use the country code followed by the number. |
0x00000014 | ESME_RMSGQFUL | Queue full: pause and retry later. |
0x00000045 | ESME_RSUBMITFAIL | Submit failed: check the fields and your balance, then retry. |
0x00000058 | ESME_RTHROTTLED | Over your throughput: slow down and retry later. |
Errors
Errors come back as JSON with an HTTP status code, a status of error and a readable message. For example, a call without a valid token returns HTTP 401:
{
"status": "error",
"message": "Authentication required. Please provide an Authorization header with your API key.",
"code": 401
}Check the HTTP status first, log the message, and do not retry a 401 until the token is fixed.
Routes and pricing
Transactional SMS, such as OTPs, alerts and order updates, goes out on a high-priority route. Promotional SMS goes to your opted-in lists. You pay per message from a prepaid balance, with no setup or monthly fees; read your balance with GET /v2/account/balance.
curl https://api.unecast.com/v2/account/balance \ -H "Authorization: Bearer $UNECAST_TOKEN"
Rates in rupees and US dollars, and the Premium, HQ and Wholesale routes, are on the SMS gateway page. For large monthly volumes, ask for a quote by route.