How I Built Kenya Data: From Administrative Sources to Python and TypeScript SDKs
5 October 2026 · 7 min read
How I Built Kenya Data: From Administrative Sources to Python and TypeScript SDKs
A lot of software depends on basic geographic data.
A developer might need to answer simple questions such as:
- Which constituencies belong to a county?
- Which wards belong to a constituency?
- Does a particular ward exist?
- What is the administrative hierarchy for a location?
The questions are simple. Getting reliable, reusable data behind them is less straightforward.
I built Kenya Data to turn that information into a developer-facing data layer: a versioned dataset distributed through SQLite, with native Python and TypeScript SDKs on top.
The project currently covers Kenya's 47 counties, 290 constituencies, and 1,450 wards. The repository also documents the sources, transformations, validation process, and dataset versions rather than treating the final database as an unexplained collection of records.
The problem
Kenyan administrative data is available in different forms and from different sources. For a developer, having data available somewhere is not the same thing as having a dataset that is easy to consume reliably.
There are several separate problems:
- Finding the source data
- Understanding what each source actually represents
- Normalizing the records into a consistent structure
- Maintaining relationships between administrative levels
- Keeping track of where the data came from
- Making the resulting dataset easy for applications to use
I wanted to approach the problem as a small data engineering system rather than simply publish a JSON file.
The goal was to make it possible to answer another question alongside every data question:
Where did this data come from, and how was it admitted into the dataset?
Starting with the data, not the API
One of the important decisions was to separate the data pipeline from the developer interface.
The project follows this general flow:
Data Sources
↓
Research
↓
Source Registry
↓
Ingestion
↓
Validation
↓
Approved Data
↓
SQLite Database
↓
Python SDK / TypeScript SDK
This means the SDK is not the source of truth.
The database is a distribution layer for the approved dataset, while the repository keeps the research and transformation process visible.
That distinction matters because changing an API should not require changing the underlying data model, and adding another client language should not require rebuilding the data pipeline.
Modeling the administrative hierarchy
The core relationship is hierarchical:
Kenya
└── County
└── Constituency
└── Ward
For the initial dataset, that means:
- 47 counties
- 290 constituencies
- 1,450 wards
The important part is not only storing the records. The relationships between them need to make sense.
A constituency needs to belong to the correct county.
A ward needs to belong to the correct constituency.
Those relationships become useful when applications need queries such as:
kenya.constituencies.in_county("Nakuru")
or:
kenya.wards.in_constituency("Naivasha")
The same underlying dataset is exposed through the TypeScript SDK as well.
Validation is part of the product
A dataset can look correct while still containing relationship errors.
For that reason, validation is part of the pipeline rather than something done once manually after the database has been created.
Among the checks performed by the project are relationship checks between administrative levels.
For example:
Ward → Constituency
Constituency → County
The validation process checks that these references resolve correctly before the data is included in the distributed database.
At the time of the project's recorded validation, there were no invalid constituency-to-county or ward-to-constituency references in the approved dataset.
That does not mean the project treats the data as permanently infallible. Source data can change, discrepancies can be discovered, and future dataset versions may require corrections.
That is why the project also records dataset versions and source information.
Why SQLite?
The next question was how to distribute the data.
For a dataset of this size, a full external database service would introduce unnecessary infrastructure for many use cases.
SQLite provides a useful middle ground.
The database can be distributed with the package itself and queried locally without requiring:
- a database server
- credentials
- network access
- application-specific database setup
The SDK can therefore operate against a bundled, read-only SQLite database.
This makes the package useful for scripts, APIs, prototypes, command-line tools, and applications that simply need reliable access to the administrative data.
It also makes the dataset portable.
The database becomes an artifact that can be versioned alongside the software that consumes it.
One dataset, two programming languages
I wanted the same underlying data to be accessible to both Python and TypeScript developers.
The Python package is available on PyPI as kenya-data.
The TypeScript package is published to npm as kenya-data-core.
A Python application can work with the data like this:
from kenya_data import Kenya
kenya = Kenya()
kenya.counties.list()
kenya.counties.get("Nakuru")
kenya.constituencies.in_county("Nakuru")
The TypeScript SDK provides the equivalent interface:
import { Kenya } from "@kenya-data/core";
const kenya = new Kenya();
kenya.counties.list();
kenya.counties.get("Nakuru");
kenya.constituencies.inCounty("Nakuru");
The important design decision here is that the two SDKs consume the same SQLite data layer.
That reduces the risk of maintaining completely separate datasets for different programming languages.
The language-specific layer is responsible for providing an interface that feels natural to that ecosystem, while the underlying data remains shared.
Provenance and versioning
One of the things I wanted to avoid was a package where the answer to "where did this data come from?" was simply "some JSON file in the repository."
The project therefore keeps source information and dataset metadata alongside the data pipeline.
The repository documents the source research and the transformations used to produce the distributed dataset.
This also makes versioning meaningful.
A dataset version should represent a particular state of the underlying data, rather than simply being a software package version with no explanation of what changed.
That distinction becomes increasingly important if the project is updated later.
An application can depend on a known dataset version, while the repository can document what changed between versions.
What I learned
The project reinforced something I have found repeatedly when working with data:
The difficult part is often not exposing the data through an API. It is establishing what the data should be before exposing it.
Building an SDK is relatively visible work. You can see the classes, methods, types, and documentation.
The less visible work is:
- finding the relevant sources
- understanding conflicting representations
- designing relationships
- validating references
- recording provenance
- deciding what belongs in the approved dataset
- making the resulting artifact reproducible
That work determines whether the API is useful.
It also changes how I think about data products.
A useful data product is not simply a collection of records. It is a combination of:
Data
+
Provenance
+
Validation
+
Versioning
+
A usable interface
Kenya Data is my attempt to apply that approach to a specific, practical problem.
What comes next
There is still more work to do.
Administrative data is not static forever, and a useful dataset needs a clear process for handling future source changes, corrections, and additional administrative information.
The project can also be extended beyond the initial hierarchy as the underlying source research supports it.
For now, the important foundation is in place:
Kenyan source data
↓
structured dataset
↓
validation + provenance
↓
portable SQLite
↓
Python + TypeScript SDKs
That is the part I wanted to get right first.
Kenya Data is open source and available on GitHub: