Phone numbers
Everything about which numbers we accept, how to format them, and what happens to the ones we don’t.
The short version
A Kenyan mobile number is +254 followed by nine digits starting with 7 or
1 — written locally as 07XXXXXXXX or 01XXXXXXXX. Send them in whatever
format you have; we normalise to E.164 before sending.
{
"recipients": ["0712345678", "254733123456", "+254110456789", "712345678"]
}All four of those are accepted and become +254712345678,
+254733123456, +254110456789 and +254712345678.
Formats we accept
| You send | We send |
|---|---|
0712345678 | +254712345678 |
254712345678 | +254712345678 |
+254712345678 | +254712345678 |
712345678 | +254712345678 |
0712 345 678 | +254712345678 |
+254 712-345-678 | +254712345678 |
Spaces, dashes and brackets are ignored.
The bare nine-digit form exists for a reason. Excel drops the leading
zero from any column it treats as a number, so 0712345678 becomes
712345678 in exported CSVs. Rather than make you fix the file, we put the
zero back.
Which ranges work
Every allocated Kenyan mobile range, across Safaricom, Airtel and Telkom, plus the smaller operators and MVNOs that sit on them.
| Range | |
|---|---|
07XX | All of it |
010X – 015X | All currently allocated ranges |
0180 – 0189 | All of it |
We don’t maintain that list by hand. Validation accepts anything in the shape of a Kenyan mobile number, so a range allocated by the Communications Authority next month works the day it goes live, without waiting for us to ship anything.
This changed in October 2026. We previously checked numbers against a
fixed list of known ranges, which silently rejected 0116–0119,
0140–0145 and all of 0180–0189 — roughly twenty million live
numbers. If you worked around it by filtering those out, you can stop.
What we reject
| Not accepted | Why |
|---|---|
0202345678 | A landline. It can’t receive an SMS, so we refuse it rather than charge you |
0712345 | Too short |
07123456789 | Too long |
+254912345678 | 09 isn’t a Kenyan mobile range |
+1234567890 | Not a Kenyan number, and we only route Kenyan traffic |
Rejected numbers come back in invalid_recipients and are not charged:
{
"recipients_count": 2,
"cost_kes": 0.9,
"invalid_recipients": ["0202345678"]
}If every number is rejected you get a 422 and nothing is sent:
{ "detail": "No valid phone numbers to send to" }Ported numbers
Kenya has had mobile number portability since 2011. Someone can keep
0722... after moving from Safaricom to Airtel, so a number’s prefix tells
you where it started, not where it is now.
This matters in one place: we show you which network each number looks like it’s on, so you can spot a batch that may not reach its recipients with your current Sender ID. That guess is right most of the time and wrong sometimes, which is why it’s only ever a warning — we never drop a number because of it.
curl -X POST https://api.wakalisms.com/sms/coverage -H "X-API-Key: YOUR_KEY" -H "Content-Type: application/json" -d '{"recipients":["0712345678","0733123456"]}'{
"total": 2,
"by_network": [
{ "network": "safaricom", "label": "Safaricom", "count": 1, "covered": true },
{ "network": "airtel", "label": "Airtel", "count": 1, "covered": false }
],
"unreachable_count": 1,
"warning": "1 of 2 numbers don't look like Safaricom numbers. ..."
}See Sender IDs for what covered depends on.
Validating before you send
You don’t have to pre-validate — we tell you which numbers failed in
invalid_recipients. But if you want to check at the point of data entry,
this is the same rule we apply:
const KENYAN_MOBILE = /^(?:\+?254|0)?([71]\d{8})$/;
function toE164(input) {
const digits = String(input).replace(/[^\d+]/g, "");
const match = digits.match(KENYAN_MOBILE);
return match ? `+254${match[1]}` : null;
}
toE164("0712345678"); // "+254712345678"
toE164("0181229508"); // "+254181229508"
toE164("0202345678"); // null — landlineStoring numbers
Store E.164 (+254712345678). It’s unambiguous, it’s what we return, and it
sorts and de-duplicates properly. If your users type whatever they like, run
it through the function above on the way in.
Duplicates within one request are removed before billing, so sending the same number twice in a batch charges once.