npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kenya-utils

v1.0.2

Published

Kenya utilities for JavaScript & TypeScript — phone validation, KRA PIN, counties dataset, currency formatting. Works in Node, browsers, and edge runtimes.

Readme

kenya-utils

Helpers for Kenya-specific data in JavaScript and TypeScript — phone numbers, KRA PINs, national IDs (including Maisha cards), the 47 counties, M-PESA, and shilling formatting.

I built this because I kept copying the same regexes between projects. If you've been doing the same, this is for you.

CI npm bundle size license

Install

npm install kenya-utils
yarn add kenya-utils
pnpm add kenya-utils

Works in Node 18+, browsers, Next.js (server and client), Cloudflare Workers, Deno, Bun. No dependencies.

A quick taste

import { parsePhone, isValidKraPin, findCounty, formatKes } from "kenya-utils";

parsePhone("0712345678");
// { e164: "+254712345678", network: "Safaricom", ... }

isValidKraPin("A123456789B"); // true

findCounty("nairobi");
// { code: 47, name: "Nairobi", capital: "Nairobi", ... }

formatKes(1234567); // "Ksh 1,234,567.00"

You can also import only the module you need so unused code doesn't ship with your bundle:

import { parsePhone } from "kenya-utils/phone";
import { findCounty } from "kenya-utils/counties";

What's in it

phone

Parse, validate, format, and detect the network of any Kenyan mobile number.

import { parsePhone, isValidKePhone, formatPhone, detectNetwork } from "kenya-utils/phone";

parsePhone("+254 712 345 678");
// {
//   e164: "+254712345678",
//   national: "0712345678",
//   international: "+254 712 345 678",
//   subscriber: "712345678",
//   network: "Safaricom",
//   countryCode: "254"
// }

isValidKePhone("0712345678"); // true
isValidKePhone("0202345678"); // false (landline)

formatPhone("0712345678"); // "+254712345678"
formatPhone("+254712345678", "national"); // "0712345678"

detectNetwork("0730123456"); // "Airtel"
detectNetwork("0747123456"); // "Faiba"

Accepts +254..., 254..., 0..., or a bare 9-digit number. Strips whitespace, hyphens, dots, and parens. Returns null on garbage rather than throwing.

A note on network detection: number portability means a "Safaricom-prefixed" number could now be on Airtel. The result is still right ~95% of the time, but treat it as a hint, not a guarantee.

kra-pin

import { isValidKraPin, parseKraPin, formatKraPin } from "kenya-utils/kra-pin";

isValidKraPin("A123456789B"); // true (Individual)
isValidKraPin("P987654321Z"); // true (Non-Individual)
isValidKraPin("a 123-456-789 b"); // true — case + whitespace tolerant

parseKraPin("A123456789B");
// {
//   pin: "A123456789B",
//   type: "Individual",
//   prefix: "A",
//   sequence: "123456789",
//   checkChar: "B"
// }

A prefix is for individuals, P is for everything else (companies, partnerships, trusts, societies).

national-id

Validates the numeric ID format (7 or 8 digits — older IDs are 7) and includes helpers for Maisha Card expiry checks.

import {
  isValidKeId,
  parseKeId,
  parseMaishaCard,
  isMaishaCardExpired,
} from "kenya-utils/national-id";

isValidKeId("12345678"); // true
parseKeId("1234567");    // { id: "1234567", digits: 7, isLegacy: true }

parseMaishaCard({ id: "12345678", expiry: "2030-01-01" });
// { id: "12345678", expiryDate: ..., isExpired: false, daysUntilExpiry: 1234 }

isMaishaCardExpired("2020-01-01"); // true

The Maisha Namba uses the same numeric format as legacy IDs, so isValidKeId works for both. The Maisha Card helpers are for the card-level concerns the new rollout introduced (mainly: expiry dates).

counties

The 47 counties with their codes, capitals, slugs, regions, and constituencies.

import {
  counties,
  findCounty,
  countiesByRegion,
  findCountyBySubCounty,
} from "kenya-utils/counties";

counties.length; // 47

findCounty(47);            // by code
findCounty("Mombasa");     // by name
findCounty("tana-river");  // by slug

countiesByRegion("Coast");
// [Mombasa, Kwale, Kilifi, Tana River, Lamu, Taita Taveta]

findCountyBySubCounty("Westlands")?.name; // "Nairobi"

Each county looks like:

{
  code: 47,
  name: "Nairobi",
  capital: "Nairobi",
  slug: "nairobi",
  region: "Nairobi",
  subCounties: ["Westlands", "Dagoretti North", ...]
}

If you spot a sub-county that's missing or wrong, please open a PR — boundary data shifts and I'd rather have it correct.

currency

import { formatKes, parseKes, toKesWords } from "kenya-utils/currency";

formatKes(1234567);                    // "Ksh 1,234,567.00"
formatKes(1234, { decimals: 0 });      // "Ksh 1,234"
formatKes(1234, { symbol: "KES" });    // "KES 1,234.00"

parseKes("Ksh 1,234,567.50");          // 1234567.5
parseKes("500 shillings");             // 500

toKesWords(1234.50);
// "one thousand two hundred thirty four shillings and fifty cents"

toKesWords is what you actually want for receipts and cheques.

mpesa

Paybill/till validation and a best-effort SMS receipt parser.

import {
  isValidPaybill,
  isValidTillNumber,
  parseMpesaSms,
} from "kenya-utils/mpesa";

isValidPaybill(123456); // true

const r = parseMpesaSms(
  "QHJ7K8L9M0 Confirmed. Ksh1,000.00 sent to JOHN DOE 0712345678 on 9/5/26 at 10:30 AM. New M-PESA balance is Ksh5,000.00. Transaction cost, Ksh23.00.",
);
// {
//   transactionId: "QHJ7K8L9M0",
//   type: "sent",
//   amount: 1000,
//   party: "JOHN DOE",
//   partyPhone: "0712345678",
//   balance: 5000,
//   transactionCost: 23,
//   ...
// }

The parser handles the common formats — sent, received, paybill, buygoods, withdraw, airtime. Safaricom's SMS wording can shift, so it's regex-driven and forgiving. If a format breaks, file an issue with a redacted SMS sample.

What's not here yet

  • Wards for each sub-county. There are over 1,400 of them and I want to pull from a verified source rather than type from memory. Coming in v1.2.
  • VAT / withholding tax helpers — useful but want to get the rates right. v1.3.
  • More M-PESA edge cases — pochi la biashara, M-Shwari, KCB-MPESA.

Contributing

PRs are welcome — see CONTRIBUTING.md for setup and conventions. The most useful contributions right now: sub-county corrections, additional M-PESA SMS formats, and network prefix updates.

License

MIT © Collins Mbathi