kenya-sms
Africa's Talking SMS wrapper for Kenya — bilingual templates, delivery tracking, bulk sending.
SMS is the primary notification channel for Kenya. This library wraps the Africa's Talking API with the patterns that production Kenya systems actually need: bilingual English/Kiswahili templates, phone number normalisation, delivery receipt tracking, rate-limit backoff, and bulk broadcast sending.
Install
pip install kenya-sms
Quickstart
from kenya_sms import SMSClient, Templates, Language
client = SMSClient(
api_key="your_at_api_key",
username="your_at_username",
sender_id="MYAPP", # Registered with Communications Authority of Kenya
)
# Single message — phone normalised automatically
result = client.send("0712345678", "Your order is confirmed.")
print(result.succeeded, result.cost) # True, "KES 1.0000"
# Bilingual template — user's language preference
result = client.send_template(
"254712345678",
Templates.CONTRIBUTION_RECEIVED,
language=Language.KISWAHILI,
name="Jane", amount="2,000", cycle="January", receipt="NLJ7RT61SV",
)
# → "Habari Jane, mchango wako wa KES 2,000 kwa January umepokelewa. Risiti: NLJ7RT61SV."
Bilingual templates
All templates render in English or Kiswahili with the same variables:
from kenya_sms import Template, Language
# Built-in templates
Templates.CONTRIBUTION_RECEIVED # Chama contribution confirmed
Templates.CONTRIBUTION_REMINDER # Upcoming contribution due
Templates.LOAN_APPROVED # Loan disbursement notice
Templates.DROUGHT_ALERT # OpenResilience early warning
Templates.PAYMENT_PROMPT # M-Pesa payment instructions
Templates.OTP # Verification code
# Custom template
tpl = Template(
en="Dear {name}, your loan repayment of KES {amount} is due on {date}.",
sw="Habari {name}, malipo yako ya mkopo ya KES {amount} yanastahili tarehe {date}.",
)
msg = tpl.render(Language.KISWAHILI, name="John", amount="4,333", date="31 Jan")
parts = tpl.sms_parts(Language.KISWAHILI, name="John", amount="4,333", date="31 Jan")
# → 1 part (within 160 chars)
Phone number normalisation
Accepts any Kenyan format:
from kenya_sms import normalise_phone
normalise_phone("0712345678") # → "254712345678"
normalise_phone("+254712345678") # → "254712345678"
normalise_phone("712345678") # → "254712345678"
normalise_phone("0112345678") # → "254112345678" (Safaricom Home)
Bulk sending
# Different message per recipient
results = client.send_bulk([
("0712345678", "Your contribution for January is confirmed."),
("0723456789", "Mchango wako wa Januari umethibitishwa."),
("0734567890", "Your contribution is pending. Please pay by Friday."),
])
# Same message to many — uses AT's batch endpoint
results = client.send_broadcast(
phones=["0712345678", "0723456789", "0734567890"],
message="Chama meeting Saturday 10am at the usual venue.",
)
successful = [r for r in results if r.succeeded]
Delivery tracking
# Mount at your AT delivery report callback URL
@app.post("/sms/delivery")
async def delivery_report(request: Request):
payload = await request.json()
receipt = client.handle_delivery_receipt(payload)
return {"status": "ok"}
# Query later
store = client.delivery_store
failed = store.failed() # Messages that did not deliver
print(f"{len(failed)} undelivered messages")
Sandbox mode
client = SMSClient(
api_key="sandbox",
username="sandbox",
sandbox=True, # Routes to AT sandbox — no real SMS sent
)
Use with OpenResilience
from kenya_sms import SMSClient, Templates, Language
from kenya_counties import get
# Send drought alert to all Turkana subscribers
turkana = get("Turkana")
client = SMSClient(api_key=AT_KEY, username=AT_USER, sender_id="RESILIENCE")
for subscriber_phone in get_turkana_subscribers():
client.send_template(
subscriber_phone,
Templates.DROUGHT_ALERT,
Language.KISWAHILI,
county=turkana.name,
level="Kali",
deficit="48",
action="kuvuna mapema",
)
Design decisions
Zero runtime dependencies. The Africa's Talking API is a simple REST + form-encoded endpoint. urllib.request handles it without httpx or requests.
Template-first design. In production Kenya systems, the same message goes out in both languages constantly. Encoding this as a first-class type — not a function or a string — means templates are testable, reusable, and auditable.
Phone normalisation is non-negotiable. Kenyan users enter phone numbers in at least four different formats. Failing on anything other than E.164 means real users get dropped. The normaliser accepts all common formats and rejects genuinely invalid input.
Part of the nairobi-stack East Africa engineering ecosystem. Maintained by Gabriel Mahia. Kenya × USA.
Metadata
Release files for kenya-sms 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| kenya_sms-1.0.1.tar.gz | 8.9 kB | With provenance | Details for kenya_sms-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| kenya_sms-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for kenya_sms-1.0.1-py3-none-any.whl |
Total release size: 16.9 kB
Release files / kenya_sms-1.0.1.tar.gz
| Download URL | kenya_sms-1.0.1.tar.gz |
|---|---|
| Size | 8.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d8ba43b65bfe42e348e909cbd95bcd823ac4e18a6df196a9dc0d49e1dbcb8a3d
|
|
BLAKE2b-256 checksum How to use checksums |
8e564ab0a00b2750fbd6cc1b13ad3764e562fe727583901369cb72fd8e876d3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logRelease files / kenya_sms-1.0.1-py3-none-any.whl
| Download URL | kenya_sms-1.0.1-py3-none-any.whl |
|---|---|
| Size | 8.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8c7bf9569add9426d548eba4edcb3a6b4b2fbbd837b0b24cf4c8cc430896441c
|
|
BLAKE2b-256 checksum How to use checksums |
50d66fabcfe9fd520e0206343fd616dff637f12840b97ef27f72052d7b9c3596
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logSecurity for kenya-sms 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for kenya_sms-1.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.