PTA-Approved SMS Aggregator · Serving Pakistan since 2014

SMS API

Bulk SMS API Documentation

Send Single, Bulk, Unicode, Dynamic (mail-merge) and OTP SMS directly from your website or software over a plain HTTP request — no SDK and no OAuth handshake required. This is the exact same API that powers our own web portal, fully compatible with PHP, ASP, .NET, JSP, C# and Java integrations. Prefer to explore it visually first? Browse our full Postman collection — every endpoint on this page is pre-built there, ready to run with your own credentials.

Local traffic only: this channel is strictly for terminating local/Pakistani-origin companies, products and services. Terminating traffic on behalf of any international entity, directly or indirectly, is treated as a violation of these terms and can lead to legal action for damages or losses incurred.

Important

Account Verification Required

Before your Sender ID can be approved and activated, account verification is required. Verify your account to display your own message; otherwise, our default demo text will be appended.

  • CNIC
  • Face ID
  • Legal agreement on letterhead
  • NTN (National Tax Number)
See Verification Options

Plain HTTP GET/POST

No SDK required — call the endpoint from any language that can make an HTTP request.

Single & Bulk sends

Send to one recipient or a comma-separated list in exactly the same call.

Dynamic mail-merge

Upload a spreadsheet and send a uniquely personalized message to every row in one call.

Unicode-ready

Send emoji and regional-script text by setting type=unicode.

JSON & XML output

Add format=json or format=xml for a structured response.

Fixed-SMS & OTP templates

Fill a pre-approved template with template_id and a small JSON payload.

Onboarding

Getting Started

From signup to your first delivered SMS in a few minutes.

1

Create your free account

Sign up with your name, mobile number and email — no paperwork to get started.

2

Get your Sender ID approved

Verify your account for the route your business needs — Semi Branded, Branded or Short Code (OTP) SMS.

3

Grab your API key

Copy your unique api_key from your dashboard's Profile page — it authenticates every API call you make.

4

Call the API

Hit api/sms.php with your API key, sender ID, recipient and message.

5

Track delivery & balance

Poll delivery.php for a message's status and balance.php for your remaining credit.

Reference

Authentication

Every request is authenticated with a single api_key parameter. The older username / password login-pair auth is deprecated — use api_key on every call shown on this page.

Where to find it

Log in to your dashboard and open Profile → API Key to copy your unique api_key.

View My API Key

Sending it

Pass api_key as a plain request parameter on every call, via GET query string or POST body:

?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=YourBrand&mobile=923001234567&template_id=YOUR_TEMPLATE_ID&message={"name":"Aslam","otp":"1234"}

Treat your api_key like a password. Never expose it in client-side JavaScript, public repositories, or mobile app bundles — always call this API from your own backend server.

Reference

Sender ID (Masking)

The sender parameter is the name recipients see as the message sender. It must match a Sender ID that's been approved for your account.

RouteSender ID
Simple SMSSent from a pre-approved shared name such as SMS Alert — instant approval.
Branded SMSYour own company or brand name — requires a one-time mask approval.
Short Code / OTPDelivered via our dedicated short code route for time-critical codes — default short code is 8584.
Reference

Base URL & Endpoints

There's one endpoint per operation — sending, dynamic/mail-merge sending, checking a delivery report, and checking your balance.

PurposeEndpoint
Send SMS (single, bulk, unicode, fixed/OTP)https://sendpk.com/api/sms.php
Dynamic / personalized SMS from a filehttps://sendpk.com/api/personalised.php
Delivery reporthttps://sendpk.com/api/delivery.php
Check balancehttps://sendpk.com/api/balance.php
List my templateshttps://sendpk.com/apps/fetch_all_fixed_templates.php
GET POST Both are supported. GET is the simplest for quick testing; POST keeps your api_key out of server access logs in production.
Reference

Request Parameters

Our docs are friendly enough that you don't need deep developer knowledge to integrate — here's every parameter the sms.php endpoint accepts.

ParameterDescription
api_key requiredYour unique API key, from Profile → API Key.
sender requiredYour approved Sender ID / brand name.
mobile requiredRecipient mobile number, or a comma-separated list for bulk sends, e.g. 923001234567,923101234567.
message requiredYour template's variable values as a JSON string, e.g. {"name":"Aslam","otp":"1234"} for the template Hello #name#, your OTP code is #otp#. Send it with your template_id — see Fixed / OTP SMS.
network optionalOne of Jazz, Zong, Ufone, Telenor. Use it to route SMS on ported/MNP numbers, or to force a specific operator.
type optionalSet to unicode to send emoji or non-Latin script text.
template_id requiredYour approved Fixed-SMS/OTP template ID, from SMS Templates → Fixed-SMS-Templates in your dashboard. Only the old free-text methods left this out.
format optionalSet to json, xml or plain (case-insensitive) to choose the response format.

The Dynamic / Personalized SMS endpoint accepts its own url and duplicate parameters instead of mobile — see its dedicated section below.

Reference

Response Format

By default the API replies with a short plain-text line. Add format=json or format=xml on any request to get the same status/ID pair back as structured data instead.

OK  Successful response

OK ID:29346

The number after ID: is your unique message ID — save it if you plan to check the delivery report later.

Reference

Status Codes

Every response starts with OK on success, or a numeric code that tells you exactly what went wrong.

CodeMeaning
OKMessage was successfully accepted for delivery.
1API key is invalid, expired, or your account is disabled.
2API key is empty.
3Reserved (legacy code, unused with api_key auth).
4Sender ID is empty.
5Recipient is empty.
6Message is empty.
7Invalid recipient number.
8Insufficient credit — top up your balance to continue sending.
9SMS rejected.
API Endpoints

Overview

One core endpoint, several ways to use it — plus a dedicated endpoint for file-based mail-merge sending. Pick the flavor that matches what you're sending.

Please read before you start

The message parameter now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side, in your approved template, so message carries only the values that change — sent as JSON, with your template_id. See Fixed / OTP SMS for how templates work.

This applies to Single, Bulk, Unicode and Dynamic / Personalized sending alike. Nothing else changes: the URL, api_key, sender, mobile and the response stay the same.

If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.

Fixed / OTP SMS

Start here. Every send needs an approved template and a template_id, so this section explains both — the other sections build on it.

Per PTA guidelines, SMS falls into two categories: transactional (critical/informational messages and OTPs) and promotional (marketing content). Fixed-SMS templates keep your transactional content pre-approved and PTA-compliant — only the variable parts change per message. Variables are written as #variable_name# in the approved template; you fill them in with a JSON object of matching keys.

Video Tutorial

Prefer to watch it in action? This walkthrough covers Fixed / OTP SMS end-to-end.

POSTGET
https://sendpk.com/api/sms.php

Creating a template

1

Log in to your dashboard

Open the SMS Templates section from your account.

2

Open Fixed-SMS-Templates

Found in the top-right corner of the SMS Templates page.

3

Submit your content

Write your message with #variable_name# placeholders for anything that changes per recipient.

4

Wait for approval

Once approved you'll get a template_id — ready to use immediately.

Required Parameters

ParameterDescription
template_id requiredYour approved Fixed-SMS/OTP template ID, from the Fixed-SMS Templates page in your dashboard.
message requiredYour template's variable values as a JSON string. For the template Hello #name#, your OTP code is #otp#, send {"name":"Aslam","otp":"1234"} and the recipient reads Hello Aslam, your OTP code is 1234.

Request Example

HTTP
GET https://sendpk.com/api/sms.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=8584&mobile=923001234567&template_id=YOUR_TEMPLATE_ID&message={"name":"Aslam","otp":"1234"}

Short Code / OTP templates typically send from a shared short code (e.g. 8584) rather than a custom brand name. Read the full walkthrough in our Fixed-SMS Templates guide.

Send Single SMS

Sends one message to one recipient.

The message parameter has changed

message now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side, in your approved template, so message carries only the values that change — sent as JSON, with your template_id.

Everything else stays exactly the same: the URL, api_key, sender, mobile and the response you get back.

Here is the same message, written both ways:

1
Your approved template — we keep this text fixed for you. You do not send it.
Hello #name#, your OTP code is #otp#
2
What message used to take — you typed the whole line yourself.
Hello Aslam, your OTP code is 1234
3
What message takes now — only the values that change, as JSON.
{"name":"Aslam","otp":"1234"}
4
Add your template_id — the ID you were given when your template was approved. Get it from List My Templates, or read it off your dashboard's Fixed-SMS Templates page.
template_id=YOUR_TEMPLATE_ID

If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.

POSTGET
https://sendpk.com/api/sms.php

Request Example

HTTP
GET https://sendpk.com/api/sms.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=8584&mobile=923001234567&template_id=YOUR_TEMPLATE_ID&message={"name":"Aslam","otp":"1234"}

Response Example

OK ID:29346
cURL
curl -G 'https://sendpk.com/api/sms.php' \
  -d 'api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d' \
  -d 'sender=8584' \
  -d 'mobile=923001234567' \
  -d 'template_id=YOUR_TEMPLATE_ID' \
  -d 'message={"name":"Aslam","otp":"1234"}'
PHP
<?php
$post = http_build_query([
    'api_key'     => '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
    'sender'      => '8584',
    'mobile'      => '923001234567',
    'template_id' => 'YOUR_TEMPLATE_ID',
    ///only the values that change — the message text is fixed in your template
    'message'     => json_encode(['name' => 'Aslam', 'otp' => '1234']),
]);

$result = file_get_contents('https://sendpk.com/api/sms.php?' . $post);

if (strpos($result, 'OK') === 0) {
    echo 'Sent! ' . $result;
} else {
    echo 'Error code: ' . $result;
}
JavaScript
const params = new URLSearchParams({
  api_key: '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
  sender: '8584',
  mobile: '923001234567',
  template_id: 'YOUR_TEMPLATE_ID',
  // only the values that change — the message text is fixed in your template
  message: JSON.stringify({ name: 'Aslam', otp: '1234' }),
});

const res = await fetch('https://sendpk.com/api/sms.php?' + params);
const text = await res.text();
console.log(text); // e.g. "OK ID:29346"
Python
import json, requests

response = requests.get('https://sendpk.com/api/sms.php', params={
    'api_key': '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
    'sender': '8584',
    'mobile': '923001234567',
    'template_id': 'YOUR_TEMPLATE_ID',
    # only the values that change — the message text is fixed in your template
    'message': json.dumps({'name': 'Aslam', 'otp': '1234'}),
})

print(response.text)  # e.g. "OK ID:29346"

Send Bulk SMS

Sends the same message to multiple recipients in a single call. Separate numbers with a comma — duplicates and invalid numbers are filtered automatically.

The message parameter has changed

message now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side, in your approved template, so message carries only the values that change — sent as JSON, with your template_id.

Everything else stays exactly the same: the URL, api_key, sender, mobile and the response you get back.

Here is the same message, written both ways:

1
Your approved template — we keep this text fixed for you. You do not send it.
Dear #name#, your bill of Rs. #amount# is due on #date#
2
What message used to take — you typed the whole line yourself.
Dear Aslam, your bill of Rs. 2500 is due on 15 Sep
3
What message takes now — only the values that change, as JSON.
{"name":"Aslam","amount":"2500","date":"15 Sep"}
4
Add your template_id — the ID you were given when your template was approved. Get it from List My Templates, or read it off your dashboard's Fixed-SMS Templates page.
template_id=YOUR_TEMPLATE_ID

If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.

POSTGET
https://sendpk.com/api/sms.php

Request Example

HTTP
GET https://sendpk.com/api/sms.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=YourBrand&mobile=923001234567,923101234567&template_id=YOUR_TEMPLATE_ID&message={"name":"Aslam","amount":"2500","date":"15 Sep"}

Response Example

OK ID:29347
cURL
curl -G 'https://sendpk.com/api/sms.php' \
  -d 'api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d' \
  -d 'sender=YourBrand' \
  -d 'mobile=923001234567,923101234567' \
  -d 'template_id=YOUR_TEMPLATE_ID' \
  -d 'message={"name":"Aslam","amount":"2500","date":"15 Sep"}'
PHP
<?php
$post = http_build_query([
    'api_key'     => '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
    'sender'      => 'YourBrand',
    'mobile'      => '923001234567,923101234567',
    'template_id' => 'YOUR_TEMPLATE_ID',
    ///only the values that change — the message text is fixed in your template
    'message'     => json_encode(['name' => 'Aslam', 'amount' => '2500', 'date' => '15 Sep']),
]);

$result = file_get_contents('https://sendpk.com/api/sms.php?' . $post);
echo $result;
JavaScript
const params = new URLSearchParams({
  api_key: '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
  sender: 'YourBrand',
  mobile: '923001234567,923101234567',
  template_id: 'YOUR_TEMPLATE_ID',
  // only the values that change — the message text is fixed in your template
  message: JSON.stringify({ name: 'Aslam', amount: '2500', date: '15 Sep' }),
});

const res = await fetch('https://sendpk.com/api/sms.php?' + params);
console.log(await res.text());
Python
import json, requests

response = requests.get('https://sendpk.com/api/sms.php', params={
    'api_key': '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
    'sender': 'YourBrand',
    'mobile': '923001234567,923101234567',
    'template_id': 'YOUR_TEMPLATE_ID',
    # only the values that change — the message text is fixed in your template
    'message': json.dumps({'name': 'Aslam', 'amount': '2500', 'date': '15 Sep'}),
})

print(response.text)

Send Unicode SMS

Set type=unicode to send emoji, Urdu, or any other non-Latin script text. Unicode messages have a shorter per-part character limit than plain text, exactly as with any standard SMS.

The message parameter has changed

message now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side, in your approved template, so message carries only the values that change — sent as JSON, with your template_id.

Everything else stays exactly the same: the URL, api_key, sender, mobile and the response you get back.

Here is the same message, written both ways:

1
Your approved template — we keep this text fixed for you. You do not send it.
پیارے #name#، آپکا آرڈر #order_id# مل گیا
2
What message used to take — you typed the whole line yourself.
پیارے اسلم، آپکا آرڈر 4471 مل گیا
3
What message takes now — only the values that change, as JSON.
{"name":"اسلم","order_id":"4471"}
4
Add your template_id — the ID you were given when your template was approved. Get it from List My Templates, or read it off your dashboard's Fixed-SMS Templates page.
template_id=YOUR_TEMPLATE_ID

If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.

POSTGET
https://sendpk.com/api/sms.php

Request Example

HTTP
GET https://sendpk.com/api/sms.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=YourBrand&mobile=923001234567&template_id=YOUR_TEMPLATE_ID&message=%7B%22name%22%3A%22%D8%A7%D8%B3%D9%84%D9%85%22%2C%22order_id%22%3A%224471%22%7D&type=unicode

URL-encode the message parameter (as shown above) whenever it contains Unicode characters. Before encoding, that example reads {"name":"اسلم","order_id":"4471"}.

Dynamic / Personalized SMS

Send bulk SMS with unique information for every contact in a file (.txt, .csv, .xls or .xlsx) — no need to loop single sends. Your template's variable values go in message as JSON, each one pointing at a column with #B#, #C#, #D# (matching the spreadsheet's column letters), and every recipient gets their own row's values filled in automatically.

The message parameter has changed

message now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side, in your approved template, so message carries only the values that change — sent as JSON, with your template_id.

Everything else stays exactly the same: the URL, api_key, sender, mobile and the response you get back.

Here is the same message, written both ways:

1
Your approved template — we keep this text fixed for you. You do not send it.
Your son #name# got #marks# marks in #subject#
2
What message used to take — you typed the whole line yourself.
Your son #B# got #C# marks in #D#
3
What message takes now — only the values that change, as JSON.
{"name":"#B#","marks":"#C#","subject":"#D#"}
4
Add your template_id — the ID you were given when your template was approved. Get it from List My Templates, or read it off your dashboard's Fixed-SMS Templates page.
template_id=YOUR_TEMPLATE_ID

If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.

POST
https://sendpk.com/api/personalised.php

Required Parameters

ParameterDescription
api_key requiredYour API key.
sender requiredYour approved Sender ID / brand name.
url requiredA publicly reachable link to your uploaded .txt/.csv/.xls/.xlsx file.
template_id requiredYour approved Fixed-SMS template ID, from SMS Templates → Fixed-SMS-Templates in your dashboard.
message requiredYour template's variable values as JSON, with a #COLUMN# reference for each one — e.g. #B# pulls from column B of the file.

Optional Parameters

ParameterDescription
format optionalSet to json, xml or plain.
duplicate optional0 removes duplicate numbers, 1 keeps them. Defaults to keeping duplicates.

Request Example

HTTP
GET https://sendpk.com/api/personalised.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=YourBrand&url=https://yourdomain.com/contacts.xlsx&template_id=YOUR_TEMPLATE_ID&message={"name":"#B#","marks":"#C#","subject":"#D#"}

Sample Output

Your approved template is Your son #name# got #marks# marks in #subject#. If column B holds a name, C a score and D a subject, each recipient gets their own filled-in copy:

Output
Your son Ali Ahmad got 1123 marks in Mathematics

Your son Mustafa Kamal got 330 marks in Mathematics

This is the same mail-merge feature behind the "Send Dynamic Message" request in our Postman collection.

Check Balance

Checks your remaining SMS credit through a simple HTTP call — handy for showing a live balance inside your own app or dashboard.

GET
https://sendpk.com/api/balance.php

Request Example

HTTP
GET https://sendpk.com/api/balance.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d

Returns your remaining SMS credit as a plain number, or the matching status code if the request fails.

Delivery Report

Looks up the current delivery status of a previously sent message using the message ID returned by the send API.

GET
https://sendpk.com/api/delivery.php

Request Example

HTTP
GET https://sendpk.com/api/delivery.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&id=29346

Pass the message id from the OK ID: response you received when the message was sent.

List My Templates

Returns every approved Fixed-SMS template on your account, each with the template_id you pass when sending. Use it so you never have to copy an ID out of the dashboard by hand — fetch your templates once at startup, then send against the ID you got back.

You will only see approved templates here

A template has to be approved before it can be sent, so this endpoint lists approved ones only. Anything still waiting, or rejected, is left out — an ID you cannot send with would be no use to you. Create a template and follow its approval in your dashboard:

Fixed-SMS Templates
POSTGET
https://sendpk.com/apps/fetch_all_fixed_templates.php

Required Parameters

ParameterDescription
api_key requiredYour API key — the same one you send to sms.php.

Request Example

HTTP
GET https://sendpk.com/apps/fetch_all_fixed_templates.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d

Response Example

JSON
{
  "success": "true",
  "results": [
    {
      "id": "482",
      "name": "OTP Login",
      "message": "Hello #name#, your OTP code is #otp#",
      "variables": ["name", "otp"]
    },
    {
      "id": "509",
      "name": "Order Delivered",
      "message": "Dear #username#, your order was delivered by #ridername#",
      "variables": ["username", "ridername"]
    }
  ]
}
FieldWhat it is
idSend this as your template_id.
nameThe name you gave the template in your dashboard.
messageThe approved wording, with its #variable# placeholders.
variablesThe placeholder names on their own — these are exactly the JSON keys to put in message when you send.

Putting it together

Take the first template above and send it. id becomes template_id, and variables becomes your message keys:

HTTP
GET https://sendpk.com/api/sms.php?api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d&sender=8584&mobile=923001234567&template_id=482&message={"name":"Aslam","otp":"1234"}
cURL
curl -G 'https://sendpk.com/apps/fetch_all_fixed_templates.php' \
  -d 'api_key=3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d'
PHP
<?php
$json = file_get_contents('https://sendpk.com/apps/fetch_all_fixed_templates.php?' . http_build_query([
    'api_key' => '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
]));

$data = json_decode($json, true);

if (($data['success'] ?? '') !== 'true') {
    exit('Error: ' . $data['results'][0]['error']);
}

foreach ($data['results'] as $tpl) {
    echo $tpl['id'] . ' — ' . $tpl['name'] . ' — needs: ' . implode(', ', $tpl['variables']) . "\n";
}
JavaScript
const params = new URLSearchParams({ api_key: '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d' });
const res = await fetch('https://sendpk.com/apps/fetch_all_fixed_templates.php?' + params);
const data = await res.json();

if (data.success !== 'true') {
  throw new Error(data.results[0].error);
}

for (const tpl of data.results) {
  console.log(tpl.id, tpl.name, 'needs:', tpl.variables.join(', '));
}
Python
import requests

response = requests.get('https://sendpk.com/apps/fetch_all_fixed_templates.php', params={
    'api_key': '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d',
})
data = response.json()

if data.get('success') != 'true':
    raise SystemExit('Error: ' + data['results'][0]['error'])

for tpl in data['results']:
    print(tpl['id'], tpl['name'], 'needs:', ', '.join(tpl['variables']))

When there is nothing to return

JSON
{"success":"false","results":[{"status":"1","error":"No Record Found"}]}

Nothing listed yet? Submit one on the Fixed-SMS Templates page and it appears here once approved. If your account has an IP whitelist, it does not apply here: you can read your templates from your development machine, and still keep sending locked to your server's address. This endpoint also accepts the older username/password pair for existing integrations, but api_key is the way to go — it never puts your account password in a URL.

Developer Tools

Code Examples

A reusable helper function you can paste directly into your project to start sending SMS in one line. It uses the current Fixed / OTP SMS method: your message text stays fixed in your approved template, and you pass only the values that change.

cURL
# Reusable shell function — $2 is your template's values as JSON
send_sms() {
  curl -s -G 'https://sendpk.com/api/sms.php' \
    -d "api_key=$SENDPK_API_KEY" \
    -d "sender=$SENDPK_SENDER" \
    -d "mobile=$1" \
    -d "template_id=$SENDPK_TEMPLATE_ID" \
    -d "message=$2"
}

# template: Hello #name#, your OTP code is #otp#
send_sms "923001234567" '{"name":"Aslam","otp":"1234"}'
PHP
<?php
///$vars holds only the values that change — the wording lives in your template
function sendSms(string $mobile, array $vars): string {
    static $apiKey     = '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d';
    static $sender     = 'YourBrand';
    static $templateId = 'YOUR_TEMPLATE_ID';
    static $url        = 'https://sendpk.com/api/sms.php';

    return file_get_contents($url . '?' . http_build_query([
        'api_key'     => $apiKey,
        'sender'      => $sender,
        'mobile'      => $mobile,
        'template_id' => $templateId,
        'message'     => json_encode($vars),
    ]));
}

///template: Hello #name#, your OTP code is #otp#
$result = sendSms('923001234567', ['name' => 'Aslam', 'otp' => '1234']);
echo strpos($result, 'OK') === 0 ? 'Sent! ' . $result : 'Error: ' . $result;
JavaScript
// vars holds only the values that change — the wording lives in your template
async function sendSms(mobile, vars) {
  const API_KEY = '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d';
  const params = new URLSearchParams({
    api_key: API_KEY,
    sender: 'YourBrand',
    mobile,
    template_id: 'YOUR_TEMPLATE_ID',
    message: JSON.stringify(vars),
  });
  const res = await fetch('https://sendpk.com/api/sms.php?' + params);
  return res.text();
}

// template: Hello #name#, your OTP code is #otp#
const result = await sendSms('923001234567', { name: 'Aslam', otp: '1234' });
console.log(result);
Python
import json, requests

API_KEY = '3f9a2b7c6e1d4c2a9b0e8d5f1a2b3c4d'
SENDER = 'YourBrand'
TEMPLATE_ID = 'YOUR_TEMPLATE_ID'
API_URL = 'https://sendpk.com/api/sms.php'

# vars holds only the values that change — the wording lives in your template
def send_sms(mobile: str, vars: dict) -> str:
    response = requests.get(API_URL, params={
        'api_key': API_KEY,
        'sender': SENDER,
        'mobile': mobile,
        'template_id': TEMPLATE_ID,
        'message': json.dumps(vars),
    })
    return response.text

# template: Hello #name#, your OTP code is #otp#
result = send_sms('923001234567', {'name': 'Aslam', 'otp': '1234'})
print(result)

Best Practices

  • Always check that the response starts with OK before assuming a message was accepted.
  • Save the message ID from each OK ID: response if you plan to check its delivery report later.
  • Call the API from your backend, never directly from a browser or mobile app, to keep your api_key private.
  • Use format=json if you'd rather parse a structured response than a plain-text line.
  • Put your wording in an approved template and send message as fixed JSON data with your template_id — see Fixed / OTP SMS. Free text in message is no longer accepted.
  • For bulk sends, pass a comma-separated mobile list instead of looping single sends.
Developer Tools

Postman & Downloads

Prefer exploring the API outside your code editor first? Grab our Postman collection or the full offline documentation bundle.

Postman Collection

Every endpoint pre-built and ready to run — just drop in your own api_key.

Open Collection

Offline Documentation

Download a zip with the full PHP & HTTP code samples for offline reference.

Download Api.zip
Developer Tools

Frequently Asked Questions

What do I use as my API key?
Copy your unique api_key from your dashboard's Profile page. It replaces the older username/password login-pair auth, which is now deprecated for this API.
Can I send to multiple recipients in one call?
Yes — pass a comma-separated mobile list, e.g. 923001234567,923101234567, and the same message is sent to everyone in the list.
How do I get my template_id?
Call the List My Templates endpoint with your api_key. It returns every approved template on your account with its id, its wording, and a variables list naming the exact JSON keys you have to send in message. You can also read the ID off the Fixed-SMS Templates page in your dashboard, which is also where you submit a new template for approval.
What changed in the message parameter?
It now expects fixed JSON data instead of dynamic content you write yourself. Your wording is already fixed on our side in your approved template, so message carries only the values that change, sent as JSON with your template_id — see Fixed / OTP SMS. Everything else (the URL, api_key, sender, mobile and the response) stays the same. If fixed content is not enabled in your dashboard, please contact the site admin to update your account so it supports this feature.
How do I send a unique message per recipient at scale?
Use the Dynamic / Personalized SMS endpoint — upload a .csv/.xls/.xlsx file and point each JSON value at a column with #B#, #C#, etc., so every row gets its own values filled in.
How do I send emoji or Urdu text?
Set type=unicode and URL-encode your message parameter.
What is Fixed-SMS / template_id?
It's a pre-approved message template with #variable# placeholders. You fill the placeholders by passing a small JSON object as your message parameter alongside your approved template_id — commonly used for OTP and other transactional messages.
Why did I get status code 8?
Your account has insufficient credit. Top up your balance from your dashboard to continue sending.
How do I check if a message was delivered?
Call delivery.php with the message id you received in the original OK ID: response.
Can I get JSON or XML responses instead of plain text?
Yes — add format=json or format=xml to any request and the same status/ID pair is returned in that structure instead.
Developer Tools

Support

Need a hand integrating?

Our team can help with integration questions, Sender ID approval, or troubleshooting delivery issues. Reach out any time.