How to Send Bulk SMS in Uganda with PHP (MTN MoMo Top-Up, No VISA Card Needed)
If you've ever tried to integrate SMS into a Ugandan app, you've hit the same wall every developer here hits:
- Twilio wants a VISA card your client doesn't have
- International APIs charge in USD with no Mobile Money option
- Local options are either expensive, poorly documented, or both
I spent months dealing with this before building Yoola SMS — a bulk SMS API designed specifically for Uganda and East Africa. You top up with MTN Mobile Money or Airtel Money. No VISA. No USD. Prices start at UGX 35/SMS dropping to UGX 20 at volume.
Here's how to integrate it today.
Step 1: Get Your Free Account and API Key
Go to yoolasms.com/accounts/register — 60 seconds, no credit card. You get 3 free SMS to test immediately.
Once in, go to My API Key and copy your key.
Step 2: Top Up With Mobile Money
Click Top Up Credits in your dashboard. Enter your MTN MoMo or Airtel Money number. You get a payment prompt on your phone. Approve it. Credits appear instantly.
UGX 10,000 = 285 credits at the Basic rate. Each credit = 1 SMS.
Step 3: Send Your First SMS in PHP
<?php
function yoolaSend($phone, $message, $apiKey, $sender = 'YoolaSMS') {
$ch = curl_init('https://yoolasms.com/api/v1/send.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'phone' => $phone,
'message' => $message,
'api_key' => $apiKey,
'sender' => $sender,
]),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
return $result;
}
$apiKey = 'YOUR_API_KEY_HERE';
// Single SMS
$r = yoolaSend('0704487563', 'Hello! Your order is confirmed. Thank you.', $apiKey, 'MyShop');
echo "Sent: {$r['status']} | Credits used: {$r['credits_used']} | Balance: {$r['balance']}\n";
// Bulk SMS — comma-separate numbers
$phones = '0704487563,0772727716,0756111222,0701333444';
$r = yoolaSend($phones, 'Dear customer, Term 2 fees are due Friday. Pay via MoMo to 0704484563.', $apiKey, 'SchoolSMS');
echo "Bulk sent to {$r['recipients']} people. Credits used: {$r['credits_used']}\n";
That is it. No SDK to install. No npm package. Just cURL.
Real Example: Automated Fee Reminders for a School
This is one of the most common use cases in Uganda. Here is a complete script:
<?php
// fee_reminders.php — add to cron: 0 8 * * 1 (runs every Monday at 8AM)
require 'db.php'; // your database connection
$API_KEY = 'YOUR_API_KEY';
$due_date = date('j F Y', strtotime('+7 days'));
$students = $pdo->query("
SELECT s.parent_phone, s.student_name, f.balance_due
FROM students s
JOIN fees f ON f.student_id = s.id
WHERE f.balance_due > 0 AND f.term = 'Term2'
")->fetchAll();
foreach ($students as $s) {
$msg = "Dear Parent, {$s['student_name']}'s fees balance of UGX "
. number_format($s['balance_due'])
. " is due {$due_date}. Pay via MTN MoMo to 0704484563. Thank you.";
$result = yoolaSend($s['parent_phone'], $msg, $API_KEY, 'SchoolSMS');
if ($result['status'] !== 'success') {
error_log("SMS failed: {$s['parent_phone']} — {$result['message']}");
}
usleep(100000); // 0.1s pause between sends
}
echo "Fee reminders sent.\n";
Set this as a cron job. Your bursar never chases parents manually again.
Handling Errors Properly
$result = yoolaSend($phone, $message, $apiKey);
match($result['status']) {
'success' => handleSuccess($result),
'insufficient_fund'=> notifyAccountOwner('Low SMS credits! Top up now.'),
'invalidkey' => throw new Exception('Check your Yoola SMS API key'),
'missing' => throw new Exception('Phone or message field is empty'),
default => retryAfterDelay(3),
};
Sending to East Africa and International
Same API, same code — just use international format:
// Uganda — 1 credit
yoolaSend('0704487563', $msg, $apiKey);
// Kenya — 3 credits
yoolaSend('+254712345678', $msg, $apiKey);
// Tanzania — 3 credits
yoolaSend('+255756123456', $msg, $apiKey);
// UK Diaspora — 12 credits
yoolaSend('+447384014597', $msg, $apiKey);
// Mix Uganda + Kenya in one bulk call — credits calculated per number
$phones = '0704487563,+254712345678,0772727716';
yoolaSend($phones, $msg, $apiKey);
Check Balance Anytime
$balance = json_decode(file_get_contents(
'https://yoolasms.com/api/v1/balance.php?api_key=' . $apiKey
), true);
echo "Credits: {$balance['balance']}\n";
echo "UGX value: {$balance['amount']}\n";
The API Response Object
{
"status": "success",
"code": 200,
"message": "SMS sent successfully",
"recipients": 4,
"credits_used": 4,
"balance": 281,
"message_parts": 1
}
message_parts tells you if your message was split (over 160 characters = multiple parts = multiple credits per recipient). Keep messages under 160 characters for best value.
Get Started
🔗 Create free account — 3 SMS free, no card
📖 Full API docs
💬 Community Q&A — ask anything
📞 WhatsApp support: +256 704 484 563
Built in Uganda 🇺🇬 — works across East Africa and 40+ countries worldwide.
Drop your questions in the comments — I reply to every one.
Top comments (0)