Skip to content
Apier

How do I fetch updated Norwegian company registry data via API?

Through a change archive rather than by re-reading your customer list. Apier polls the upstream registers on a schedule, records what moved as append-only change rows, and serves them from /api/v1/changes with cursor pagination. Each row names the entity, the field path, and the values before and after, so you act on the change itself instead of diffing snapshots. Cost scales with how much actually changed, not with how many companies you follow.

A cycle: your poller calls GET /api/v1/changes with a cursor and filters, receives change rows carrying before and after values, stores the returned next_cursor, and the stored cursor feeds back into the next call from the poller.Your pollerGET /api/v1/changescursor plus filtersChange rowsbefore and after valuesStore next_cursor
The cursor closing the loop is what keeps the next call cheap. Following a thousand companies costs the same as following ten, because the request asks what changed rather than asking about each company.

How do I detect a change of daglig leder or board?

Filter the feed by entity and read the field path. A role change arrives as a row whose entity_type is the company, whose entity_id is the organisation number, and whose field_path points at the part of the record that moved. The before_value and after_value carry the two states, so you can tell a replacement from an addition without reconstructing the record yourself.

Role changes are the highest-value signal in this feed for most consumers, and the reason is authority rather than curiosity. A new general manager or a reconstituted board can change who is entitled to bind the company, which means any cached answer you hold about signing authority is now suspect. The correct reaction to a role row is usually to re-resolve authority for that organisation rather than to patch your local copy of the role list.

The public projection withholds personal fields and states that on the response through personal_fields_withheld. That is a deliberate boundary: you learn that a role changed and which field moved, which is what a monitoring workflow needs, without the change archive quietly becoming a register of named individuals. When you need the current holders, read them from /api/v1/company/{org}/context or resolve authority directly.

How do I poll efficiently without refetching everything?

Hold a cursor and persist it. Each response returns a next_cursor alongside a has_more flag; pass the cursor back on the following call and you resume exactly where you stopped. Rows are ordered by detection time, newest first, and the cursor is opaque, so treat it as a token to store rather than a timestamp to reconstruct.

Persist the cursor only after the batch it came with has been handled. Saving it first turns any crash mid-batch into silently skipped changes, which is the failure mode you least want in a monitoring pipeline because nothing reports it. Saving it after risks reprocessing a batch on restart, which is why handlers should be idempotent. Reprocessing a change is cheap; missing one is not.

Narrow the feed with filters rather than after the fact: source, entity_type, entity_id, change_type and a from and to range. If an idle polling loop is the wrong shape for your service, subscribe a webhook through /api/v1/subscriptions and have deliveries pushed instead. Both read the same archive, so the choice is operational rather than a difference in what you can learn.

What triggers a change record?

A scheduled poll of an upstream source that observes something different from what was stored. Rows carry a source naming which upstream produced them, so you can tell a Brønnøysund registry movement from an exchange-rate update, and a change_type of created, updated or deleted describing the shape of the movement.

The consequence worth designing around is that the feed is eventual, not instant. Latency is bounded by the poll cadence rather than by the moment the register writes its record, so a workflow that assumes a change becomes visible seconds after it happens will be intermittently wrong. When a decision genuinely needs the current state, read the company endpoint and check _meta.data_freshness rather than inferring currency from the absence of a change row.

Rows are append-only. A correction arrives as a new row rather than as an edit to an old one, which is what makes the archive usable as evidence: what you saw at a given moment stays reconstructable afterwards. That property is also what lets a compliance reviewer ask when you first learned something, and get an answer that does not depend on your own logging. The same archive backs the monitoring described in the guide on automated company due diligence.

Make the first call

The sandbox request below needs no key and shows the company read a change row points you back at. The TypeScript snippet drains the feed and persists the cursor in the order that survives a crash.

# Zero-auth sandbox: the same company read a change feed points you at.
curl -s https://www.apier.no/api/v1/sandbox/public/company/999999999/context
// Drain the feed, then persist the cursor. Order matters on restart.
let cursor: string | null = loadCursor();

do {
  const url = new URL("https://www.apier.no/api/v1/changes");
  url.searchParams.set("source", "brreg");
  url.searchParams.set("entity_type", "company");
  url.searchParams.set("limit", "100");
  if (cursor !== null) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.APIER_API_KEY}` },
  });
  if (!res.ok) {
    const { error_code, explanation } = await res.json();
    throw new Error(`${error_code}: ${explanation.summary}`);
  }

  const { data } = await res.json();

  for (const row of data.data) {
    // field_path says WHAT moved; before/after say how.
    await handle(row.entity_id, row.field_path, row.before_value, row.after_value);
  }

  // Persist AFTER the batch is handled, and only when non-null: on the last
  // page next_cursor is null, and storing that would send the next run back
  // to the newest row instead of resuming here.
  cursor = data.pagination.next_cursor;
  if (cursor !== null) await saveCursor(cursor);
} while (cursor !== null);

Frequently asked questions

How do I get notified when Norwegian company data changes?
Poll the change archive at /api/v1/changes, which returns detected change events with cursor pagination, or subscribe a webhook so deliveries are pushed to you instead. Both read the same archive. Polling is simpler to operate and needs no public endpoint; webhooks avoid an idle loop when changes are rare.
How quickly does a registry change appear in the feed?
Change rows are produced by periodic upstream polls, so the delay is bounded by that cadence rather than by the moment Brønnøysund writes the record. Treat the feed as reliably eventual rather than instant, and do not build a workflow that assumes a change is visible seconds after it happens.
What does a single change row tell me?
Which entity moved, which field, and both values. A row carries entity_id, field_path, before_value and after_value, a change_type of created, updated or deleted, and a detected_at timestamp. That is enough to drive a workflow directly rather than diffing two snapshots and inferring what happened between them.
Do change rows contain personal data?
The public projection withholds personal fields, and the response says so through a personal_fields_withheld flag rather than leaving you to guess from an absence. You get the fact that a role changed and which field moved, which is what a monitoring workflow needs, without the archive becoming a personal-data store.
Can I filter the feed to only the companies I care about?
Yes. Filter by source, entity_type, entity_id, change_type and a from and to time range. Filtering by entity_id is the direct way to follow a specific company. For a portfolio, filtering by source and entity type and matching locally is usually simpler than one subscription per customer.