Python · SQLite FTS5 · MCP · static site
norapi
A searchable registry of Norwegian public APIs, for developers and AI agents. One registry file, harvested from the national catalogue and curated by hand, served four ways.
Open the site ↗
View on GitHub ↗
Four front doors
The same registry file, served four ways, with no service to run and no database to install.
- Webone page per API, instant search and a command palettestatic site
- CLIthe same search locally, plus endpoint search across every specificationnorapi search, norapi ops
- MCPfive tools for an AI assistant, with the caveats passed onnorapi-mcp
- llms.txtone line per API, ready to paste into a promptgenerated from the registry
The problem
Norway's public sector publishes hundreds of APIs, but finding the right one means knowing which agency owns it, what it is called internally, whether it needs Maskinporten, and where the documentation moved to. The official catalogue holds the metadata, but it is written for registries, not for someone with a terminal open.
norapi takes that catalogue, adds hand-written entries for the APIs people actually reach for, reads the OpenAPI documents where they exist, and makes the result searchable.
What I built
- A harvester for Felles datakatalog (Digdir) that joins its search API and its SPARQL endpoint, plus Geonorge for access rights the catalogue leaves blank.
- 1,193 entries: the harvested ones plus 145 hand-curated, each with access, auth, formats, docs and a first call that was run before it was committed.
- 137 OpenAPI documents resolved, including the ones hidden behind a Swagger UI page, with 1,842 operations indexed and searchable.
- A link checker whose verdicts show on each page, and a weekly GitHub Actions run that harvests again and commits the result.
- A «Forbehold» field for restrictions to respect before relying on an API, shown on the page, in
llms.txt and in MCP results.
Decisions worth explaining
- Catalogues and curation, not scraping
- The bulk comes from a national catalogue where agencies register their own data services. Detail that needs a person, such as what an API is good for and a call that works, is written by hand. Scraping agency sites was rejected as fragile and against several terms of use.
- The JSON file is the product
registry.json is the one distributable. The SQLite FTS5 index is a cache built from it on first use, so cloning the repo is enough to search.
- Say where a fact came from
- 969 of 1,048 harvested APIs first showed access as unknown. They now fall back to Geonorge, then to the datasets served, taking the strictest value. 48 were checked by hand, 5 stay unknown with the reason written down, and every entry says which source decided.
- A failed probe never drops an entry
- Many endpoints reject anonymous requests by design. The verdict is recorded and shown, not used to delete.
- A guard on every outbound fetch
- Requests only go to public hosts, pin the resolved address, validate each redirect hop and cap body sizes, since the harvester follows addresses it did not write.
- Tests that need no network
- Unit tests run on recorded fixtures, so the suite is fast and cannot be broken by an agency's server being down.
Stack
Data and credit
Metadata comes from Felles datakatalog, operated by Digitaliseringsdirektoratet, with access constraints for map services from Geonorge's kartkatalog (Kartverket). The registry holds descriptions and links, not the APIs' data, and every API has its own terms and licence.