If you've ever tried to find a clean, structured, up-to-date list of Nigerian tertiary institutions, you've probably run into the same wall I did: every result is a static blog post or a PDF from a few years ago, half of them contradict each other, and none of them are usable as data — just prose to be re-typed by hand.
There wasn't an open API for this. So I built one.
What it is
nigeria-tertiary-institutions-api is a free, open-source REST API and dataset covering every Nigerian university, polytechnic, and college of education — 752 institutions in total:
| Type | Count | Federal | State | Private |
|---|---|---|---|---|
| Universities | 312 | 77 | 67 | 168 |
| Polytechnics | 200 | 41 | 61 | 98 |
| Colleges of education | 240 | 30 | 43 | 167 |
For each one, it includes category (Federal/State/Private), state, year established where known, and — where publicly available — the faculty/school and department structure underneath it.
It's not a hosted service you call against my server. It's a self-hosted API: you clone it, run one command, and you have your own instance running locally or deployed wherever you like. That was a deliberate choice — a single free-tier server can't reliably serve production traffic for everyone, but a project that's trivial to self-host can scale to however many people need it.
Quick start
git clone https://github.com/reality-farouqk/nigeria-tertiary-institutions-api.git
cd nigeria-tertiary-institutions-api
npm install
npm start
That's it. No database server to install, no config file to write, no API key required by default. The whole dataset lives in three CSV files, and a build step compiles them into a local SQLite file at startup — a prestart script handles that automatically every time.
curl http://localhost:3000/api/v1/stats
When you're ready to put it on the internet, the README walks through Render, Railway, Fly.io, Docker, or a plain VPS — all one-command deploys since there's no database to provision.
A quick tour of the API
Search institutions:
curl "http://localhost:3000/api/v1/institutions?type=university&state=Lagos"
Search departments across all 752 institutions at once:
curl "http://localhost:3000/api/v1/departments?q=Computer+Science"
Every department also exposes its course-of-study data:
curl "http://localhost:3000/api/v1/departments/123/courses"
The data model goes four levels deep:
Institution (university | polytechnic | college_of_education)
└─ School / Faculty / College e.g. "Faculty of Engineering"
└─ Department e.g. "Computer Engineering"
└─ Course of study e.g. "B.Eng. Computer Engineering"
"Course of study" here means the actual named degree/programme a student enrolls in — B.Sc. Computer Science, HND Electrical/Electronic Engineering, NCE Mathematics/Physics — not an individual class or module.
Being honest about what's verified and what isn't
This is the part I think is worth talking about, because it's the difference between a useful dataset and a misleading one.
The institution → school → department layer was compiled from live research — one institution at a time, checking published faculty structures against official sources. That's slow but solid: it's either backed by something a school actually published, or the record honestly says the structure "isn't clearly itemized in public sources" instead of guessing.
Course-of-study data is different. Confirming an actual, verified list of conferred qualifications for every one of the 1,700+ parsed departments isn't practical — most institutions don't publish that anywhere indexable, and doing it properly would mean repeating the entire research effort at a much finer grain. So instead, course-of-study rows are generated from a disclosed, documented naming convention: a polytechnic department gets the standard ND → HND ladder, a college of education department gets NCE, and a university department gets a qualification chosen by matching subject keywords against a documented table ("engineering" → B.Eng., "law" → LL.B., and so on, defaulting to B.Sc.).
Every single generated row is tagged source: "inferred" in the API response itself — not just in a README nobody reads. If someone later verifies a specific institution's actual programme list, that's a source: "verified" tier waiting to be built.
A bug the new feature accidentally caught
While building the course-of-study generator, I ran a sanity check on the department names it was about to turn into qualifications — and found things like "ND Aba" and "HND est. 1992" in the output. Not great.
It turned out some source cells append a closing note about an institution — an establishment year, a renaming history, an ownership note — directly inside the last school's own parentheses, instead of using the separator the parser expected. Something like:
Faculty of Science (7-8 faculties per recruitment notice; est. 2023, temporary site at FUNAAB)
Without a check for this, that trailing note gets split exactly like a department list and produces fabricated-looking department names. Once I went looking, this pattern had silently produced about 500 fake department rows across the dataset — before it ever became a visible problem, it was just quietly wrong.
I added a filter that recognizes note-like content (years, ownership/renaming language, sourcing caveats, bare counts like "3 departments") and treats it as what it is — a note, not a department — while leaving the original raw text untouched so nothing is lost. It's a good reminder that "never fabricate structure" needs to be a design constraint checked in the data itself, not just a policy in a doc.
Locking it down (optional)
By default the API is open. If you're running a public instance and want to control who calls it, set one environment variable:
API_KEY=your-secret-here
Every request under /api/v1/* then needs to send it back via X-API-Key or Authorization: Bearer, while /healthz stays open so your host's uptime checks keep working. You can also set a comma-separated list of keys to hand out separately per consumer. It's a shared-secret check, not full user auth — good for stopping anonymous scraping of a public instance, not a substitute for a real auth layer if you need per-user access control.
Open source, and open to corrections
The code is MIT-licensed, the data is CC0 (public domain) — do whatever you want with either. Nigerian tertiary institutions get renamed, merged, and upgraded to university status a few times a year, so this dataset will drift out of date the moment it's published; corrections and PRs are genuinely welcome, and CONTRIBUTING.md covers how.
If you're building anything that touches Nigerian higher education — admissions tools, EdTech products, research — this exists so you don't have to start from a pile of stale blog posts.
Repo: https://github.com/reality-farouqk/nigeria-tertiary-institutions-api
I'd love feedback, stars, issues, or PRs — especially from anyone who can verify course-of-study data for a specific institution and help move it from "inferred" to "verified."
Top comments (0)