e-SMS

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 URLhttps://e-sms.lk/api/v3
ProtocolHTTPS only. Requests and responses are JSON (UTF-8).
AuthenticationBearer token in the Authorization header.
CoverageSri Lankan mobile numbers on Dialog, Mobitel, Hutch and Airtel.
BillingCredits, one per SMS segment per recipient. Sends that are refused are refunded automatically.
Time zoneSri 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.

EncodingOne segmentEach segment of a longer message
plain (GSM-7)160 characters153 characters
unicode70 characters67 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

POST /api/v3/sms/send

Send one message to one number or to a comma-separated list of up to 1,000 numbers.

ParameterDescription
recipient requiredOne number, or several separated by commas. See Phone numbers.
sender_id requiredAn approved sender name on your account, up to 11 characters.
message requiredThe text of the SMS.
schedule_timeSend later, as YYYY-MM-DD HH:MM in Sri Lanka time. See Scheduling.
typeplain 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
  }
}
FieldDescription
uidID of the first message. With one recipient, this is the message.
uidsOne ID per recipient, in the order of to. Store these to track delivery.
toThe valid, normalised numbers the message was accepted for.
sms_typeplain or unicode.
sms_countSegments in one message.
costTotal credits taken: segments multiplied by recipients.
statusQueued, Pending or Scheduled. See Message statuses.
scheduled_atThe scheduled time, or null for an immediate send.
blockedNumbers skipped because they are on your blacklist. Not charged.

Cost a message without sending

POST /api/v3/sms/estimate

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

POST /api/v3/sms/campaign

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.

ParameterDescription
contact_list_id requiredThe group uid from GET /api/v3/contacts.
sender_id requiredAn approved sender name.
message requiredThe text of the SMS.
schedule_timeSend 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

GET /api/v3/sms/{uid}
{
  "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

GET /api/v3/sms

Your messages, newest first, in pages.

QueryDescription
start_dateFrom this time, e.g. 2026-10-01 or 2026-10-01 08:00:00.
end_dateUp to this time. For a whole day use 2026-10-31 23:59:59.
sms_typeplain or unicode.
directionapi for messages sent through the API, dashboard for those sent from the dashboard.
pagePage number, from 1.
per_pageResults 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

GET /api/v3/balance
{
  "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

GET /api/v3/me

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

GET /api/v3/contacts
{
  "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.

POST /api/v3/blacklist
ParameterDescription
number requiredOne number, several separated by commas, or a JSON array as numbers. Up to 10,000 at a time.
noteWhy 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": [] }
}
GET /api/v3/blacklist

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.

DELETE /api/v3/blacklist/{number}

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.

StatusMeaning
ScheduledWaiting for its schedule_time. Credits are already reserved.
PendingAccepted and being handed to the network. Usually moves on within a minute.
QueuedAccepted by the network for delivery.
SentHanded to the recipient's mobile network; waiting for the delivery report.
DeliveredFinal. The handset confirmed delivery.
UndeliveredFinal. The network could not deliver it, for example the phone was off for too long.
FailedFinal. The message could not be submitted, for example an invalid number.
RejectedFinal. The network refused the message.
ExpiredFinal. 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 / codeMeaning and what to do
401 UNAUTHENTICATEDToken missing, wrong or revoked, or the account is suspended. Do not retry; fix the token.
402 INSUFFICIENT_CREDITSNot enough credits. The body includes required and available. Top up, then retry.
404 NOT_FOUNDNo such message or endpoint.
422 SENDER_NOT_APPROVEDThe sender_id is not an approved sender name on your account.
422 SENDER_NOT_ACTIVEThe sender name is approved but not yet active for sending. Contact support.
422 NO_RECIPIENTSNo valid Sri Lankan mobile number in recipient.
422 ALL_BLOCKEDEvery number is on your blacklist. The body lists them in blocked. Nothing is charged.
422 TOO_MANYMore than 1,000 numbers in one request. Split it or use a contact group.
422 EMPTY_MESSAGEThe message is empty.
422 BAD_SCHEDULEschedule_time is not YYYY-MM-DD HH:MM or is in the past.
422 GROUP_NOT_FOUNDThe contact group is not on your account.
422 EMPTY_GROUPThe group has no valid Sri Lankan mobile numbers.
422 BLACKOUTNot accepted during the overnight window. Schedule it for the morning.
422 SEND_FAILEDThe network did not accept the send. Credits returned. Retry later; contact support if it persists.
429 RATE_LIMITEDToo many requests. Wait for the number of seconds in the Retry-After header.
503 NETWORK_BUSYTemporarily busy. Credits returned. Retry after a minute.
500 SERVER_ERRORA 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

TokenStored on your server only; a separate token for test and production.
Never blockSend from a background job so a slow network never holds up your checkout, login or sale.
Store IDsSave each uid with the order, user or event that triggered it.
Handle codesRetry only 429, 503, 500 and SEND_FAILED, with a growing delay. Do not retry other 4xx errors.
TimeoutsIf a request times out, check GET /sms for the last few minutes before sending again, to avoid a duplicate.
TemplatesCheck each template with the estimate endpoint at its longest realistic length.
BalanceCheck GET /balance daily and alert before credits run out.
OTPsKeep 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.