DEVELOPER DOCUMENTATION
SMS gateway API reference for Sri Lanka
The REST API of the e-SMS SMS gateway Sri Lanka, for sending bulk SMS, OTPs and alerts to every mobile network in Sri Lanka from your own website, app, POS or CRM. JSON in, JSON out, over HTTPS, from Rs 0.53 per SMS segment (see pricing). New to it? Start with from API token to your first message.
Overview
| Base URL | https://e-sms.lk/api/v3 |
| Protocol | HTTPS only. Requests and responses are JSON (UTF-8). |
| Authentication | Bearer token in the Authorization header. |
| Coverage | Sri Lankan mobile numbers on Dialog, Mobitel, Hutch and Airtel. |
| Billing | Credits, one per SMS segment per recipient. Sends that are refused are refunded automatically. |
| Time zone | Sri Lanka time (UTC+05:30) for scheduling and timestamps. |
Every response has a status of success or error. Successful responses
carry the result in data. Errors carry a machine-readable code and a readable
message you can show to your own users; see Errors.
Authorization: Bearer 12|your-token-here
Content-Type: application/json
Accept: application/json
Quickstart
1. Create an account and request a sender name in the dashboard under Sender names,
with a copy of your business registration (or NIC) and a signed request letter. It must be verified before you can send with it.
2. Generate a token in the dashboard under API tokens and keep it on your server.
3. Send your first message to your own phone with one of the examples below.
cURL
curl -X POST https://e-sms.lk/api/v3/sms/send \
-H 'Authorization: Bearer 12|your-token-here' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{"recipient": "0712345678", "sender_id": "YourBrand", "message": "Hello from e-SMS"}'
PHP
$ch = curl_init('https://e-sms.lk/api/v3/sms/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('SMS_API_TOKEN'),
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'recipient' => '0712345678',
'sender_id' => 'YourBrand',
'message' => 'Hello from e-SMS',
]),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($result['status'] === 'success') {
$uid = $result['data']['uid']; // store it to track delivery
} else {
error_log($result['code'] . ': ' . $result['message']);
}
Node.js (18+)
const res = await fetch('https://e-sms.lk/api/v3/sms/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SMS_API_TOKEN}`,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
recipient: '0712345678',
sender_id: 'YourBrand',
message: 'Hello from e-SMS',
}),
});
const result = await res.json();
if (result.status === 'success') console.log('Queued', result.data.uid);
else console.error(result.code, result.message);
Python
import os, requests
res = requests.post(
"https://e-sms.lk/api/v3/sms/send",
headers={"Authorization": f"Bearer {os.environ['SMS_API_TOKEN']}", "Accept": "application/json"},
json={"recipient": "0712345678", "sender_id": "YourBrand", "message": "Hello from e-SMS"},
timeout=15,
)
result = res.json()
if result["status"] == "success":
print("Queued", result["data"]["uid"])
else:
print(result["code"], result["message"])
Authentication
Every request needs an API token in the header Authorization: Bearer <token>. Generate
tokens in the dashboard under API tokens; the full token is shown once, so copy it when it is created.
You can hold several tokens at once, for example one for testing and one for production, or one per client
system. Revoking a token stops it immediately without affecting the others. Keep tokens on your server, never
in a web page, mobile app bundle or public repository: anyone with the token can send at your cost. A missing,
wrong or revoked token returns 401 with the code UNAUTHENTICATED.
Phone numbers
Send numbers in whichever form you have them. Local (0712345678), international
(94712345678) and +94 forms are accepted and normalised to 94712345678.
Separate several numbers with commas. Duplicates in one request are removed, so a number is never charged
twice in the same send.
Only Sri Lankan mobile numbers are delivered. Anything else is skipped and listed as invalid, and you are not charged for it. Numbers on your blacklist are skipped the same way. One request takes up to 1,000 numbers; for larger audiences use a contact group.
Message length and billing
You are charged per SMS segment, per recipient. The segment size depends on the characters in the message; what counts as one SMS explains it with examples.
| Encoding | One segment | Each segment of a longer message |
|---|---|---|
| plain (GSM-7) | 160 characters | 153 characters |
| unicode | 70 characters | 67 characters |
Sinhala, Tamil and emoji make the whole message unicode. Credits are taken when a message is
accepted and returned automatically if the send is refused; see Message statuses. Use the estimate endpoint to
see the exact segments and credits before you send.
Send a message
Send one message to one number or to a comma-separated list of up to 1,000 numbers.
| Parameter | Description |
|---|---|
| recipient required | One number, or several separated by commas. See Phone numbers. |
| sender_id required | An approved sender name on your account, up to 11 characters. |
| message required | The text of the SMS. |
| schedule_time | Send later, as YYYY-MM-DD HH:MM in Sri Lanka time. See Scheduling. |
| type | plain or unicode. Optional: the encoding is always detected from the message. |
curl -X POST https://e-sms.lk/api/v3/sms/send \
-H 'Authorization: Bearer 12|your-token-here' \
-H 'Content-Type: application/json' \
-d '{
"recipient": "94710000000,94770000000",
"sender_id": "YourBrand",
"message": "Your order #4821 is out for delivery."
}'
Response
{
"status": "success",
"data": {
"uid": "a91f3c7d2e04",
"uids": ["a91f3c7d2e04", "b72e1d9a5c83"],
"to": "94710000000,94770000000",
"from": "YourBrand",
"message": "Your order #4821 is out for delivery.",
"sms_type": "plain",
"sms_count": 1,
"cost": 2,
"status": "Queued",
"scheduled_at": null
}
}
| Field | Description |
|---|---|
| uid | ID of the first message. With one recipient, this is the message. |
| uids | One ID per recipient, in the order of to. Store these to track delivery. |
| to | The valid, normalised numbers the message was accepted for. |
| sms_type | plain or unicode. |
| sms_count | Segments in one message. |
| cost | Total credits taken: segments multiplied by recipients. |
| status | Queued, Pending or Scheduled. See Message statuses. |
| scheduled_at | The scheduled time, or null for an immediate send. |
| blocked | Numbers skipped because they are on your blacklist. Not charged. |
Cost a message without sending
Takes the same recipient and message as a send and returns the analysis without
sending anything or taking credits. Use it to check templates and before large sends.
{
"status": "success",
"data": {
"encoding": "gsm",
"type": "plain",
"length": 37,
"segments": 1,
"capacity": 160,
"remaining": 123,
"recipients": 2,
"invalid": [],
"units": 2,
"cost": 1.6
}
}
units is the credits the send would take and cost its value in rupees at your rate.
remaining is how many more characters fit before the message needs another segment,
invalid lists numbers that would be skipped, and blocked lists numbers on your blacklist.
Send to a contact group
Send one message to every contact in a saved group, with no limit on group size. Create groups and import contacts (including from CSV) in the dashboard, then find their IDs with GET /contacts.
| Parameter | Description |
|---|---|
| contact_list_id required | The group uid from GET /api/v3/contacts. |
| sender_id required | An approved sender name. |
| message required | The text of the SMS. |
| schedule_time | Send later, as YYYY-MM-DD HH:MM in Sri Lanka time. |
{
"status": "success",
"data": {
"uid": "c5d81e0a93f2",
"contacts": 1250,
"skipped": 0,
"units": 1250,
"segments": 1,
"cost": 662.5,
"status": "Pending",
"scheduled_at": null,
"note": null
}
}
uid identifies the campaign. contacts is the numbers messaged, skipped
the contacts that were not valid Sri Lankan mobiles, units the credits taken and
cost their value in rupees. Each recipient also gets a message row you can follow with
GET /sms.
Look up one message
{
"status": "success",
"data": {
"uid": "a91f3c7d2e04",
"to": "94710000000",
"from": "YourBrand",
"message": "Your order #4821 is out for delivery.",
"sms_type": "plain",
"direction": "api",
"status": "Delivered",
"sms_count": 1,
"cost": 1,
"sent_at": "2026-10-27 14:22:08"
}
}
An unknown ID, or one that belongs to another account, returns 404 with the code NOT_FOUND.
List messages
Your messages, newest first, in pages.
| Query | Description |
|---|---|
| start_date | From this time, e.g. 2026-10-01 or 2026-10-01 08:00:00. |
| end_date | Up to this time. For a whole day use 2026-10-31 23:59:59. |
| sms_type | plain or unicode. |
| direction | api for messages sent through the API, dashboard for those sent from the dashboard. |
| page | Page number, from 1. |
| per_page | Results per page, 25 by default, at most 100. |
{
"status": "success",
"message": "SMS data fetched successfully",
"data": {
"current_page": 1,
"data": [ { "uid": "a91f3c7d2e04", "to": "94710000000", "status": "Delivered", "...": "..." } ],
"per_page": 25,
"total": 312,
"last_page": 13,
"next_page_url": "https://e-sms.lk/api/v3/sms?page=2",
"prev_page_url": null
}
}
Each item has the same fields as a single message.
Check your credits
{
"status": "success",
"data": { "remaining_sms_unit": 4820, "rate": 0.58, "currency": "LKR" }
}
remaining_sms_unit is your credits; rate is what one credit is worth in rupees at
your current rate. Check it on a schedule and alert your team before it runs low.
Your account
Returns the account the token belongs to: uid, name, company,
email, phone, remaining_sms_unit, status and
created_at. Useful as a connection test when you set up a new token.
Your contact groups
{
"status": "success",
"data": [
{ "uid": "g4b9e21c07aa", "name": "Loyalty members", "contacts": 1250 }
]
}
Use a group's uid as contact_list_id when you send to a group.
Blacklist (opt-outs)
Numbers on your blacklist are never messaged: every send, campaign and scheduled message skips them, and they are never charged. Add a number as soon as a customer asks you to stop, for example when they reply STOP to your own number. A message scheduled before the number was added is skipped too, and its credit returned.
| Parameter | Description |
|---|---|
| number required | One number, several separated by commas, or a JSON array as numbers. Up to 10,000 at a time. |
| note | Why it is listed, up to 120 characters, e.g. Replied STOP. |
{
"status": "success",
"message": "1 number added to your blacklist.",
"data": { "added": 1, "already_listed": 0, "invalid": [] }
}
Your blacklisted numbers, newest first, with number, note and created_at.
Query search finds a number (local or international form), and page and
per_page (up to 500) page through the list.
Removes a number so sends can reach it again. Returns 404 with NOT_FOUND if it was not listed.
Message statuses
A message moves through these statuses. The last five are final: once a message reaches one of them it does
not change again. Credits are returned when a send is refused before the network accepts it (the API answers
with an error) and when a scheduled message expires. A message the network has accepted is charged even if the
handset later reports it Undelivered or Failed.
| Status | Meaning |
|---|---|
| Scheduled | Waiting for its schedule_time. Credits are already reserved. |
| Pending | Accepted and being handed to the network. Usually moves on within a minute. |
| Queued | Accepted by the network for delivery. |
| Sent | Handed to the recipient's mobile network; waiting for the delivery report. |
| Delivered | Final. The handset confirmed delivery. |
| Undelivered | Final. The network could not deliver it, for example the phone was off for too long. |
| Failed | Final. The message could not be submitted, for example an invalid number. |
| Rejected | Final. The network refused the message. |
| Expired | Final. A scheduled message could not be sent within 24 hours of its time; credits returned. |
Delivery reports arrive from the network over the following seconds to minutes. Poll GET /sms/{uid} for messages you care about, for example after 1, 5 and 15 minutes, or read recent messages in bulk with GET /sms.
Scheduling
Pass schedule_time as YYYY-MM-DD HH:MM in Sri Lanka time to send later. The time must
be in the future. Credits are reserved when you schedule, so a scheduled send can never fail for lack of balance,
and the response status is Scheduled. If a scheduled message cannot be sent within 24 hours of its
time, it becomes Expired and its credits are returned.
Promotional sends may not be accepted overnight (generally 8 PM to 8 AM). A send refused for that reason
returns the code BLACKOUT with your credits returned; schedule it for the morning instead.
Errors
Errors return an HTTP status and a JSON body with status, code and
message. Branch on code, which is stable; the message wording may
improve over time. When a send fails, no credits are taken, or they are returned.
{
"status": "error",
"code": "INSUFFICIENT_CREDITS",
"message": "This send needs 2 credits and you have 0.",
"required": 2,
"available": 0
}
| HTTP / code | Meaning and what to do |
|---|---|
| 401 UNAUTHENTICATED | Token missing, wrong or revoked, or the account is suspended. Do not retry; fix the token. |
| 402 INSUFFICIENT_CREDITS | Not enough credits. The body includes required and available. Top up, then retry. |
| 404 NOT_FOUND | No such message or endpoint. |
| 422 SENDER_NOT_APPROVED | The sender_id is not an approved sender name on your account. |
| 422 SENDER_NOT_ACTIVE | The sender name is approved but not yet active for sending. Contact support. |
| 422 NO_RECIPIENTS | No valid Sri Lankan mobile number in recipient. |
| 422 ALL_BLOCKED | Every number is on your blacklist. The body lists them in blocked. Nothing is charged. |
| 422 TOO_MANY | More than 1,000 numbers in one request. Split it or use a contact group. |
| 422 EMPTY_MESSAGE | The message is empty. |
| 422 BAD_SCHEDULE | schedule_time is not YYYY-MM-DD HH:MM or is in the past. |
| 422 GROUP_NOT_FOUND | The contact group is not on your account. |
| 422 EMPTY_GROUP | The group has no valid Sri Lankan mobile numbers. |
| 422 BLACKOUT | Not accepted during the overnight window. Schedule it for the morning. |
| 422 SEND_FAILED | The network did not accept the send. Credits returned. Retry later; contact support if it persists. |
| 429 RATE_LIMITED | Too many requests. Wait for the number of seconds in the Retry-After header. |
| 503 NETWORK_BUSY | Temporarily busy. Credits returned. Retry after a minute. |
| 500 SERVER_ERROR | A fault on our side. No credits are taken. Retry later. |
Rate limits
Each token can make 120 requests a minute. Every response includes X-RateLimit-Limit and
X-RateLimit-Remaining headers. Beyond the limit you get 429 with the code
RATE_LIMITED and a Retry-After header giving the seconds to wait. To message many
people at once, put up to 1,000 numbers in one request or use a contact group instead of
one request per number.
Integration checklist
| Token | Stored on your server only; a separate token for test and production. |
| Never block | Send from a background job so a slow network never holds up your checkout, login or sale. |
| Store IDs | Save each uid with the order, user or event that triggered it. |
| Handle codes | Retry only 429, 503, 500 and SEND_FAILED, with a growing delay. Do not retry other 4xx errors. |
| Timeouts | If a request times out, check GET /sms for the last few minutes before sending again, to avoid a duplicate. |
| Templates | Check each template with the estimate endpoint at its longest realistic length. |
| Balance | Check GET /balance daily and alert before credits run out. |
| OTPs | Keep codes short-lived and send only after the user asks; do not resend the same code on retry. |
Building for a POS or billing system? Adding an SMS gateway to your POS covers that integration end to end.
Support
Questions about your integration, sender names or billing: email
support@e-sms.lk with your account email and, for a specific message, its
uid. You can also browse the SMS gateway FAQ, the
bulk SMS rates or contact us.