This comprehensive guide covers how to send SMS messages using PHP with Text.lk SMS Gateway, Sri Lanka’s leading SMS service provider.
Table of Contents #
- Prerequisites
- Method 1: Using cURL (Direct API)
- Method 2: Using Text.lk PHP Package
- API Parameters
- Response Handling
- Error Handling
- Advanced Features
- Best Practices
- Troubleshooting
Prerequisites #
Before you begin, ensure you have:
- Text.lk Account: Sign up at https://text.lk
- API Key: Obtain from your Text.lk dashboard
- Sender ID: Register your sender ID (alphanumeric, max 11 characters)
- PHP 7.4+: Ensure your server supports PHP 7.4 or higher
- cURL Extension: Required for API calls
Method 1: Using cURL (Direct API) #
Basic SMS Sending Function #
<?php
function sendSMS($apiKey, $recipient, $senderId, $message, $scheduleTime = null) {
$url = 'https://app.text.lk/api/v3/sms/send';
$data = [
'recipient' => $recipient,
'sender_id' => $senderId,
'type' => 'plain',
'message' => $message
];
// Add schedule time if provided
if ($scheduleTime) {
$data['schedule_time'] = $scheduleTime;
}
$headers = [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
'Accept: application/json'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return [
'success' => false,
'error' => 'cURL Error: ' . $error
];
}
$responseData = json_decode($response, true);
return [
'success' => $httpCode == 200,
'http_code' => $httpCode,
'response' => $responseData
];
}
// Usage Example
$apiKey = 'your_api_key_here';
$recipient = '94712345678'; // Sri Lankan number format
$senderId = 'YourName';
$message = 'Hello! This is a test message from Text.lk';
$result = sendSMS($apiKey, $recipient, $senderId, $message);
if ($result['success']) {
echo "SMS sent successfully!\n";
print_r($result['response']);
} else {
echo "Failed to send SMS: " . $result['error'] . "\n";
}
?>
Sending SMS to Multiple Recipients #
<?php
function sendBulkSMS($apiKey, $recipients, $senderId, $message) {
// Recipients can be an array or comma-separated string
if (is_array($recipients)) {
$recipients = implode(',', $recipients);
}
return sendSMS($apiKey, $recipients, $senderId, $message);
}
// Usage Example
$recipients = ['94712345678', '94771234567', '94701234567'];
$result = sendBulkSMS($apiKey, $recipients, $senderId, $message);
?>
Scheduled SMS Sending #
<?php
// Send SMS at a specific date and time
$scheduleTime = '2025-12-25 10:00'; // Format: Y-m-d H:i
$result = sendSMS($apiKey, $recipient, $senderId, $message, $scheduleTime);
?>
Method 2: Using Text.lk PHP Package #
Installation #
composer require textlk/textlk-php
Basic Usage #
<?php
require_once 'vendor/autoload.php';
use TextLK\SMS\TextLKSMSMessage;
// Method 1: Setting API key in code
$textLKSMS = new TextLKSMSMessage();
$result = $textLKSMS
->recipient("94712345678")
->message("Hello, this is a test message.")
->senderId("YourName")
->apiKey('your_api_key_here')
->send();
echo $result ? "SMS sent successfully!" : "Failed to send SMS";
?>
Using Environment Variables #
Create a .env
file in your project root:
TEXTLK_SMS_API_KEY=your_api_key_here
TEXTLK_SMS_SENDER_ID=YourName
<?php
// Method 2: Using environment variables
$textLKSMS = new TextLKSMSMessage();
$result = $textLKSMS
->recipient("94712345678")
->message("Hello from Text.lk!")
->send();
?>
Multiple Recipients with Package #
<?php
$recipients = "94712345678,94771234567,94701234567";
$result = $textLKSMS
->recipient($recipients)
->message("Bulk SMS message")
->send();
?>
API Parameters #
Parameter | Required | Type | Description |
---|---|---|---|
recipient | Yes | string | Phone number(s) to send SMS. Use comma (,) for multiple numbers. Format: 94XXXXXXXXX |
sender_id | Yes | string | SMS Sender Name |
type | Yes | string | Message type. Use "plain" for text messages |
message | Yes | string | SMS content |
schedule_time | No | datetime | Schedule SMS for future delivery (Format: Y-m-d H:i) |
Phone Number Format #
- Correct:
94712345678
(country code + number without leading zero) - Incorrect:
0712345678
,+94712345678
,712345678
Response Handling #
Success Response #
{
"status": "success",
"data": "sms reports with all details"
}
Error Response #
{
"status": "error",
"message": "A human-readable description of the error."
}
Complete Response Handler #
<?php
function handleSMSResponse($result) {
if (!$result['success']) {
return [
'sent' => false,
'message' => $result['error'] ?? 'Unknown error occurred'
];
}
$response = $result['response'];
if ($response['status'] === 'success') {
return [
'sent' => true,
'message' => 'SMS sent successfully',
'data' => $response['data']
];
}
return [
'sent' => false,
'message' => $response['message'] ?? 'API error occurred'
];
}
// Usage
$result = sendSMS($apiKey, $recipient, $senderId, $message);
$status = handleSMSResponse($result);
if ($status['sent']) {
echo "Success: " . $status['message'];
} else {
echo "Error: " . $status['message'];
}
?>
Error Handling #
Common HTTP Status Codes #
Code | Meaning | Action |
---|---|---|
200 | Success | SMS processed successfully |
400 | Bad Request | Check parameters and format |
401 | Unauthorized | Verify API key |
403 | Forbidden | Check account permissions/balance |
429 | Rate Limited | Implement retry logic |
500 | Server Error | Retry after some time |
Comprehensive Error Handler #
<?php
class TextLKErrorHandler {
public static function handleError($httpCode, $response) {
$errors = [
400 => 'Bad Request - Check your parameters',
401 => 'Unauthorized - Invalid API key',
403 => 'Forbidden - Insufficient balance or permissions',
404 => 'Not Found - Invalid endpoint',
429 => 'Rate Limited - Too many requests',
500 => 'Server Error - Try again later'
];
$defaultError = $errors[$httpCode] ?? 'Unknown error occurred';
if (is_array($response) && isset($response['message'])) {
return $response['message'];
}
return $defaultError;
}
}
// Usage in your SMS function
if (!$result['success']) {
$errorMessage = TextLKErrorHandler::handleError(
$result['http_code'],
$result['response']
);
echo "Error: " . $errorMessage;
}
?>
Advanced Features #
SMS Class with Full Features #
<?php
class TextLKSMS {
private $apiKey;
private $baseUrl = 'https://app.text.lk/api/v3/sms/send';
private $defaultSenderId;
public function __construct($apiKey, $defaultSenderId = null) {
$this->apiKey = $apiKey;
$this->defaultSenderId = $defaultSenderId;
}
public function send($recipients, $message, $options = []) {
$data = [
'recipient' => is_array($recipients) ? implode(',', $recipients) : $recipients,
'sender_id' => $options['sender_id'] ?? $this->defaultSenderId,
'type' => $options['type'] ?? 'plain',
'message' => $message
];
if (isset($options['schedule_time'])) {
$data['schedule_time'] = $options['schedule_time'];
}
if (isset($options['dlt_template_id'])) {
$data['dlt_template_id'] = $options['dlt_template_id'];
}
return $this->makeRequest($data);
}
public function sendBulk($recipientsList, $message, $options = []) {
$results = [];
// Process in batches of 100 (adjust based on API limits)
$batches = array_chunk($recipientsList, 100);
foreach ($batches as $batch) {
$results[] = $this->send($batch, $message, $options);
// Add delay between batches to avoid rate limiting
sleep(1);
}
return $results;
}
public function schedule($recipients, $message, $dateTime, $options = []) {
$options['schedule_time'] = $dateTime;
return $this->send($recipients, $message, $options);
}
private function makeRequest($data) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->baseUrl,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json',
'Accept: application/json'
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return ['success' => false, 'error' => $error];
}
return [
'success' => $httpCode == 200,
'http_code' => $httpCode,
'response' => json_decode($response, true)
];
}
}
// Usage Example
$sms = new TextLKSMS('your_api_key', 'YourName');
// Send single SMS
$result = $sms->send('94712345678', 'Hello World!');
// Send bulk SMS
$recipients = ['94712345678', '94771234567'];
$results = $sms->sendBulk($recipients, 'Bulk message');
// Schedule SMS
$scheduledResult = $sms->schedule(
'94712345678',
'Scheduled message',
'2025-12-25 10:00'
);
?>
Best Practices #
1. Configuration Management #
<?php
// config/textlk.php
return [
'api_key' => env('TEXTLK_API_KEY'),
'sender_id' => env('TEXTLK_SENDER_ID', 'YourApp'),
'timeout' => 30,
'retry_attempts' => 3,
'rate_limit_delay' => 1 // seconds
];
?>
2. Message Validation #
<?php
class MessageValidator {
public static function validatePhoneNumber($number) {
// Sri Lankan mobile number validation
$pattern = '/^94[0-9]{9}$/';
return preg_match($pattern, $number);
}
public static function validateMessage($message) {
if (empty($message)) {
return false;
}
// Check message length (160 chars for single SMS)
if (strlen($message) > 1600) { // Allow up to 10 concatenated SMS
return false;
}
return true;
}
public static function validateSenderId($senderId) {
// Alphanumeric, max 11 characters
return preg_match('/^[a-zA-Z0-9]{1,11}$/', $senderId);
}
}
// Usage
if (!MessageValidator::validatePhoneNumber($recipient)) {
throw new InvalidArgumentException('Invalid phone number format');
}
?>
3. Logging and Monitoring #
<?php
class SMSLogger {
private $logFile;
public function __construct($logFile = 'sms.log') {
$this->logFile = $logFile;
}
public function log($level, $message, $context = []) {
$timestamp = date('Y-m-d H:i:s');
$contextStr = json_encode($context);
$logEntry = "[$timestamp] [$level] $message Context: $contextStr" . PHP_EOL;
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
public function logSMSSent($recipient, $message, $result) {
$this->log('INFO', 'SMS Sent', [
'recipient' => $recipient,
'message_length' => strlen($message),
'success' => $result['success'],
'response' => $result['response'] ?? null
]);
}
public function logError($error, $context = []) {
$this->log('ERROR', $error, $context);
}
}
// Usage
$logger = new SMSLogger();
$result = sendSMS($apiKey, $recipient, $senderId, $message);
$logger->logSMSSent($recipient, $message, $result);
?>
4. Rate Limiting #
<?php
class RateLimiter {
private $requests = [];
private $maxRequests;
private $timeWindow;
public function __construct($maxRequests = 60, $timeWindow = 60) {
$this->maxRequests = $maxRequests;
$this->timeWindow = $timeWindow;
}
public function canMakeRequest() {
$now = time();
// Remove old requests outside time window
$this->requests = array_filter($this->requests, function($timestamp) use ($now) {
return ($now - $timestamp) < $this->timeWindow;
});
if (count($this->requests) >= $this->maxRequests) {
return false;
}
$this->requests[] = $now;
return true;
}
public function getWaitTime() {
if (empty($this->requests)) {
return 0;
}
$oldestRequest = min($this->requests);
$waitTime = $this->timeWindow - (time() - $oldestRequest);
return max(0, $waitTime);
}
}
// Usage
$rateLimiter = new RateLimiter(100, 60); // 100 requests per minute
if (!$rateLimiter->canMakeRequest()) {
$waitTime = $rateLimiter->getWaitTime();
echo "Rate limit exceeded. Wait {$waitTime} seconds.";
exit;
}
$result = sendSMS($apiKey, $recipient, $senderId, $message);
?>
Troubleshooting #
Common Issues and Solutions #
- Invalid Phone Number Format
// Wrong: 0712345678, +94712345678 // Correct: 94712345678
- Authentication Errors
// Check API key format and validity if (strlen($apiKey) < 20) { echo "API key seems invalid"; }
- Message Not Delivered
// Check account balance, sender ID registration, and recipient number
- Rate Limiting
// Implement retry logic with exponential backoff function retryRequest($callable, $maxRetries = 3) { for ($i = 0; $i < $maxRetries; $i++) { $result = $callable(); if ($result['success'] || $result['http_code'] !== 429) { return $result; } sleep(pow(2, $i)); // Exponential backoff } return $result; }
Debug Mode #
<?php
class TextLKDebug {
public static function debugRequest($data, $headers, $response) {
echo "=== DEBUG INFO ===\n";
echo "Request Data: " . json_encode($data, JSON_PRETTY_PRINT) . "\n";
echo "Headers: " . print_r($headers, true) . "\n";
echo "Response: " . print_r($response, true) . "\n";
echo "==================\n";
}
}
// Enable debug mode
$debug = true;
if ($debug) {
TextLKDebug::debugRequest($data, $headers, $response);
}
?>
Security Considerations #
- Never expose API keys in client-side code
- Use environment variables for sensitive data
- Implement proper input validation
- Use HTTPS for all API calls
- Log SMS activities for audit trails
- Implement rate limiting to prevent abuse
Summary #
This guide covers both direct API integration using cURL and the official Text.lk PHP package. Choose the method that best fits your project needs:
- Use cURL method for: Simple integrations, custom error handling, full control over requests
- Use PHP package for: Quick setup, Laravel integration, simplified code
For production applications, implement proper error handling, logging, rate limiting, and monitoring to ensure reliable SMS delivery.
For additional support, visit Text.lk Documentation or contact their support team.