How do I access Brønnøysund Register Centre data through a REST API?
Enhetsregisteret is openly available over REST and published under NLOD, so there is no credential to obtain and no commercial licence to negotiate. Getting the data is not the hard part. The work is normalising registry field names into your own model, caching responsibly, knowing how fresh any given field is, and detecting when something changed. Apier answers the same facts as a normalised company object with freshness and source stated on every response.
How do I look up an organisasjonsnummer programmatically?
The organisasjonsnummer is a 9-digit identifier assigned by Brønnøysund, and it is the join key for nearly every Norwegian company question. If you already hold one, a lookup is a single GET. If you hold a company name instead, resolve it first: call /api/v1/company/search with a name and you get at most ten candidates carrying name, organisation number, organisational form, municipality and status.
Resolve rather than construct. The number carries a MOD-11 control digit, which means a value you invent has a reasonable chance of being structurally valid and belonging to a completely different company. A zero-hit search answers 404 with a hint to broaden the name rather than returning an empty success, which matters when the caller is an agent that would otherwise read an empty list as "this company does not exist".
For portfolios rather than single companies, use the zero-auth bulk endpoint. /api/v1/public/company-status accepts up to 100 organisation numbers in one call and returns a status row per unique number, with a per-row lookup_status so a number that does not resolve is reported as such instead of vanishing from the response. It needs no API key at all, which makes it the fastest way to check whether a customer list is still current.
What fields does Enhetsregisteret actually return?
The open tier answers the identity questions: legal name, organisational form such as AS or ENK, registration status, registration date, municipality, NACE industry codes, whether the entity is registered for VAT, and the distress flags recording bankruptcy, dissolution and forced dissolution. Role data is open too, which is what makes signing authority resolvable without a delegation.
What the open tier does not give you is the commercial picture. Employee counts, turnover and balance-sheet totals sit behind a delegation from the company itself, and on /api/v1/company/{org}/context they arrive as a separate tier_2 block with data_tier telling you which level the response actually carried. Branch on that field rather than checking whether a value happens to be null, which conflates "not permitted" with "not present".
Annual accounts are a separate open register again, Regnskapsregisteret, reachable through /api/v1/company/{org}/accounts and covered in the guide on Norwegian annual accounts data. Treating Brønnøysund as one uniform surface is the most common modelling error here: it is several registers with different contents, different update rhythms and different access rules, sharing one organisation number as their key.
How fresh is registry data and how do I detect changes?
Freshness is a property of the answer, so it belongs on the answer. Every response carries _meta.data_freshness and _meta.served_from, which distinguishes a cached read from a live fetch. Reads inside a 24-hour window are served from cache; past that the gateway fetches from Brønnøysund and stores the result. If Brønnøysund is unreachable and a recent cached copy exists, the response says so rather than failing outright, so you can decide whether slightly stale beats nothing.
Detecting change is a different problem from reading current state, and re-fetching every company on a timer is the expensive way to solve it. The change archive at /api/v1/changes returns detected change events with cursor pagination, filterable by source, entity type, entity id and change type, so you poll one feed instead of re-reading a portfolio.
Each row records what actually moved: a field_path, the before and after values, and a detected_at timestamp. That is enough to drive a workflow directly, rather than diffing two snapshots yourself and inferring what happened between them. The polling patterns, including how to hold a cursor across restarts, are covered in the guide on fetching updated company data.
| Concern | Direct Brønnøysund call | Through Apier |
|---|---|---|
| Access | Open and NLOD-licensed. No credential needed for the open tier. | Open bulk status stays keyless. Per-company reads need a key because they name role holders. |
| Field names | Registry field names and nesting, which you map into your own model and re-map when they change. | One normalised company object with stable names across every upstream. |
| Freshness | Not stated on the response. You track when you last fetched, per field, yourself. | data_freshness and served_from on every response, so cached and live are distinguishable. |
| Name lookup | Free-text search returning the full registry payload per hit. | At most ten candidates, five fields each, 404 rather than an empty success. |
| Change detection | Re-fetch and diff, or build a poller per register and reconcile the results. | One cursor-paginated change feed carrying field path, before and after values. |
Make the first call
The bulk status request below is genuinely keyless, not a sandbox mirror. The TypeScript snippet resolves a name to an organisation number, which is the call to reach for before any other.
# Zero-auth: bulk status for up to 100 organisation numbers.
curl -s "https://www.apier.no/api/v1/public/company-status?org_numbers=999999999"// Name in, organisasjonsnummer out. Never guess a number.
const res = await fetch(
"https://www.apier.no/api/v1/company/search?name=Nordic%20Widgets",
{ headers: { Authorization: `Bearer ${process.env.APIER_API_KEY}` } },
);
if (res.status === 404) {
throw new Error("No match. Broaden the name rather than retrying it.");
}
if (!res.ok) {
const { error_code, explanation } = await res.json();
throw new Error(`${error_code}: ${explanation.summary}`);
}
const { data } = await res.json();
// At most ten candidates, five fields each.
for (const c of data.candidates) {
console.log(c.org_number, c.name, c.org_form, c.municipality, c.status);
}Frequently asked questions
- Is Brønnøysund register data free to use?
- Enhetsregisteret is openly available and published under NLOD, the Norwegian licence for public data, which permits reuse including in commercial products. Access is not the hard part of working with it. Normalisation, caching, freshness tracking and change detection are where the engineering time goes.
- Do I need an API key to look up a Norwegian company?
- Not for bulk status. The zero-auth endpoint /api/v1/public/company-status takes up to 100 organisation numbers in one call and returns registry status, entity type, VAT registration, the distress flags and accounts filing status. Richer per-company reads through /api/v1/company/ need a key, because those responses include role holders.
- How do I find a company when I only have its name?
- Call /api/v1/company/search with a name parameter. It returns at most ten candidates with five fields each, and answers 404 when nothing matches rather than an empty success. Use it instead of constructing an organisation number: a 9-digit value that passes the checksum will resolve to some real company, just not the one you meant.
- How fresh is the company data behind the API?
- Every response states it. The _meta block carries data_freshness and served_from, so you can tell a cached answer from a live fetch on the response itself. Reads inside a 24-hour freshness window are served from cache; beyond that the gateway fetches from Brønnøysund and stores the result.
- What is an organisasjonsnummer?
- The 9-digit identifier Brønnøysund assigns to a registered entity, and the join key for essentially every Norwegian company lookup. It carries a MOD-11 control digit, so a mistyped number usually fails validation rather than silently resolving to a different company, though not always.