A REST API for Norwegian postal codes, addresses and administrative geography, built on the official registries — Bring's postal code file, Kartverket's Matrikkel and Kommuneinfo, and Geonorge's map data.
Kilda answers the questions a Norwegian address field asks: does this postal code
exist, which city does it belong to, what does this address look like in the official
register, and where is it on a map. The data comes from the registries themselves and
is re-imported on a schedule, so what you read here is what Kartverket and Bring
published — not a copy that was correct once.
Products
Endpoints are grouped by the product they belong to — the same products the pricing
page names, so what a plan includes is what you see documented here.
Location API is the product documented below. Its postal code, address, county
and municipality endpoints all require the same location.read scope:
Postal codes — look up a code, verify it, read what Bring has changed about it,
or fetch its delivery area as GeoJSON. Keyed on the four digit code, no query
parameters.
Addresses — search the full address register for autocomplete, look up a single
address by its localId, verify one, or find what is near a coordinate. Search is
folded for case and accents, so storgata 10 oslo finds what a user typed it as.
Counties and municipalities — read the administrative geography and its borders.
The location.read scope grants access to all currently documented Location API
endpoints. A key does not receive separate scopes for these endpoint families.
Conventions
Every request needs Accept: application/json. Responses are always JSON, UTF-8.
A successful payload is wrapped in data, except the /geometry endpoints, which
return a bare GeoJSON Feature so it stays valid GeoJSON.
Coordinates are WGS 84 (EPSG:4326) decimal degrees. GeoJSON puts longitude first;
the latitude and longitude fields elsewhere are named, so there is nothing to
get the wrong way round.
Dates are YYYY-MM-DD. Timestamps are ISO 8601 in UTC.
Enum-like fields (status, type, change_type) are stable upper case identifiers
meant to be branched on. A value the API has not mapped yet arrives as UNKNOWN
rather than as the raw Norwegian source text, so parsing prose is never necessary.
Errors
Every error, from validation to rate limiting, uses one envelope:
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": ["The postal code must be four digits."]
}
}
}
error.code is the stable, machine readable part — branch on it, not on message,
which is written for a human and may be reworded. fields is present only when a
specific input was at fault.
Status
error.code
Meaning
401
unauthenticated
The token is missing, malformed or revoked.
403
forbidden
The token is valid but lacks the scope this endpoint needs.
404
resource_not_found
Nothing matched. Read message: /geometry distinguishes an unknown code from a known one with no outline.
422
validation_failed
An input failed validation. fields says which.
429
rate_limit_exceeded
Too many requests this minute. Wait Retry-After seconds.
429
quota_exceeded
The plan's monthly quota is used up. message states when it resets.
Rate limits and quota
Every answered request carries its own limits in the response headers, so a client can
pace itself before it is ever refused:
Header
Meaning
X-RateLimit-Limit
Requests allowed per minute on your plan.
X-RateLimit-Remaining
Requests left in the current minute.
X-RateLimit-Reset
Unix timestamp when the minute window resets.
X-Quota-Period
The billing period being counted, as YYYY-MM.
X-Quota-Limit
Requests included in the plan this month. Absent on uncapped plans.
X-Quota-Used
Requests used this month, including this one.
X-Quota-Remaining
Requests left this month.
X-Quota-Reset
Unix timestamp when the monthly quota resets.
The budget belongs to the team, not to the key — issuing more keys does not buy more
requests. The quota headers are absent entirely on an uncapped plan; treat a missing
X-Quota-Limit as "no monthly cap", not as zero.
Every response counts against your quota except a 5xx, which is ours to fix. That
includes a 404 ("this postal code does not exist" is still an answer your software
acted on), a 304 Not Modified (see Conditional requests below) and a 422.
401, 403 and 429 never count, because they are refused before reaching an
endpoint.
Caching
Responses are cached server side and refreshed whenever the source data changes, so
repeated lookups are cheap and still current. Postal code data changes a few
times a year and addresses daily at most — caching responses on your side is both safe
and encouraged, particularly for the geometry payloads, which are large.
The postal code and geography endpoints say so in the response, so most HTTP clients
will do it for you:
max-age is how long the answer may be reused without asking again, and it varies by
endpoint: a day for lookups and lists, a week for geometry. It is private because the
answer belongs to your key — a shared cache must not serve it to anyone else, which is
also what Vary: Authorization enforces.
The address endpoints do not send these headers. Their answers are query-shaped and
short lived, so there is nothing stable to reuse.
Conditional requests
ETag identifies the exact body you received. Send it back in If-None-Match and,
if nothing has changed, you get 304 Not Modified with no body at all:
Keep using the copy you already have. If the data has changed you get 200 with the
new body and a new ETag instead, so a client that always sends the header is never
stale and never pays for a payload twice.
The comparison is weak, as HTTP defines it: send the tag back exactly as you received
it, including the W/ prefix. Several tags separated by commas are fine, and so is
*. An unrecognised tag simply yields the full response.
Data sources
Kilda republishes data from the official Norwegian registries and claims no ownership
of it:
The postal code area outlines served by /postal-codes/{code}/geometry, and the county and municipality borders served by their own /geometry endpoints.
Kartverket's and Geonorge's open data is published under the Norwegian Licence for
Open Government Data (NLOD). If you republish what you receive from this API, credit
the original source accordingly.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_API_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
Create a key in your dashboard under API-nøkler. The key is shown once, at the
moment it is created, and is stored only as a hash afterwards — so copy it then, and
if you lose it, issue a new one and revoke the old.
Scope. A key must be granted the location.read scope to use the Location API.
This scope covers every documented endpoint:
Scope
Grants
location.read
Every /postal-codes, /addresses, /counties and /municipalities endpoint.
A missing or revoked key answers 401 unauthenticated; a valid key without
location.read answers 403 forbidden. The two are worth distinguishing in your
error handling — the first means re-authenticate, the second means issue a key with
the required scope.
Keys carry the quota and rate limit of the team that owns them, so a key belongs on a
server. Do not ship one in a mobile app or in browser JavaScript, where anyone can
read it and spend your quota.
Location API
Addresses, postal codes and the geography they sit in, taken from the official
Norwegian registries — Bring's postal code file, Kartverket's Matrikkel and
Geonorge's map data — and re-imported on a schedule.
The product answers the questions a Norwegian address field asks: does this
postal code exist, which city does it belong to, what does this address look
like in the official register, and where is it on a map.
The product includes three endpoint families, all covered by the same
location.read scope:
Postal codes — keyed on the four digit code.
Addresses — search, lookup and proximity over the full address register.
Counties and Municipalities — the administrative geography those two
report, as reference lists with borders.
A key with location.read can access every currently documented endpoint in
this product; these endpoint families do not use separate scopes.
Postal codes
Norwegian postal codes as Bring publishes them, joined to the municipality and
county they belong to and to the area outline Geonorge publishes for them.
Each endpoint answers a different question:
/postal-codes — the register itself, filtered by city, municipality or county.
/postal-codes/{code} — everything known about a code.
/postal-codes/{code}/verify — is this code in use, and what is its city.
/postal-codes/{code}/history — what has Bring changed about this code.
/postal-codes/{code}/geometry — the code's area as a GeoJSON Feature.
The four keyed on a code take no query parameters to get wrong, and each refuses
a code that is not four digits with 422.
All five require a token carrying the location.read scope.
GET/v1/postal-codesRequires authentication
List postal codes
The register itself, ordered by code and served a page at a time: every
postal code, or the subset matching a city fragment, a municipality or a
county.
q matches anywhere in the postal place and ignores case, so bergen
finds BERGEN. It is a plain substring match rather than the folded token
search /addresses uses — a postal place is one name from a closed list,
not free text.
municipality_number and county_number take the same numbers a lookup
reports under municipality.number and county.number, so every postal
code in Oslo is municipality_number=0301.
Only active codes are listed unless status asks for others. status
takes a comma separated list, so status=active,inactive returns both in
one request. A code is
unique per municipality and city rather than on its own, so a retired
entry can sit alongside the active one that replaced it, and listing both
by default would hand a dropdown the same code twice.
Entries carry the same fields as /postal-codes/{code}. The area
outlines are left out — /postal-codes/{code}/geometry serves those one
at a time.
422The search term was too short, or a filter was not the right shape.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"q": [
"The search term must be 2 characters or longer."
]
}
}
}
Response
Request failed
Response fields
data
object
postal_code
string
The four digit postal code.
city
string
The postal place (poststed), as Bring writes it — upper case.
type
string
What the code is used for: STREET_ADDRESSES, PO_BOX, COMBINED, SPECIAL_SERVICE or UNKNOWN. Omitted when the source did not state one.
status
string
active for a code in use, retired for one that has been withdrawn, inactive for one not currently in service.
municipality
object
The municipality number and name.
county
object
The county number and name.
country
string
Always NO. Present only alongside coordinates.
latitude
number
Latitude of the area centre, WGS 84. Omitted for a code that has no area outline.
longitude
number
Longitude of the area centre, WGS 84.
meta
object
total
integer
How many postal codes match the filters, across every page.
page
integer
The page returned.
per_page
integer
Results per page.
last_page
integer
The last page number available.
GET/v1/postal-codes/{code}Requires authentication
Look up a postal code
Returns the city, the postal code's category and status, the municipality and
county it belongs to, and the coordinates of its centre.
A code is unique per municipality and city rather than on its own, so a
retired entry can sit alongside the active one that replaced it. The active
entry is the one served; a retired code with nothing replacing it is still
served, marked status: retired, because a caller cleaning up old records
needs to know what the code used to mean.
The area outline is deliberately left out. It is an order of magnitude larger
than the rest of the payload and most callers never need it, so it is served
by /postal-codes/{code}/geometry instead.
This is the endpoint that answers whether a code exists at all.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": [
"The postal code must be four digits."
]
}
}
}
Response
Request failed
Response fields
data
object
postal_code
string
The four digit postal code.
city
string
The postal place (poststed), as Bring writes it — upper case.
type
string
What the code is used for: STREET_ADDRESSES, PO_BOX, COMBINED, SPECIAL_SERVICE or UNKNOWN. Omitted when the source did not state one.
status
string
active for a code in use, retired for one that has been withdrawn, inactive for one not currently in service.
municipality
object
number
string
The four digit municipality number (kommunenummer).
name
string
The municipality name.
county
object
number
string
The two digit county number (fylkesnummer).
name
string
The county name.
country
string
Always NO. Present only alongside coordinates.
latitude
number
Latitude of the area centre, WGS 84. Omitted for a code that has no area outline.
longitude
number
Longitude of the area centre, WGS 84. Omitted for a code that has no area outline.
The cheap check for a form or a checkout: is this code currently in use, and
which city does it belong to.
A miss is a 200 with valid: false, not a 404 — the same contract as
/addresses/verify. "No" is an answer to the question, and a form asking on
every blur should not have to treat it as a failure. A 404 from this
endpoint would mean the route is wrong, not that the code is.
Only active codes count. A retired code answers valid: false here even
though /postal-codes/{code} still serves it, because it is no longer a code
anyone should be entering.
The payload carries the code and the city and nothing else — use
/postal-codes/{code} when the municipality, county or coordinates matter.
200No active postal code carries that code. Note the status is 200, not 404.
{
"data": {
"valid": false
}
}
422The code is not four digits.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": [
"The postal code must be four digits."
]
}
}
}
Response
Request failed
Response fields
data
object
valid
boolean
Whether an active postal code carries that code. This is the only field guaranteed to be present — the rest appear only when it is true.
postal_code
string
The four digit postal code.
city
string
The postal place (poststed) the code belongs to, upper case.
Everything Bring's change log records about one postal code, newest first.
Each entry is shaped as a before and an after state, so a caller can tell
what the change actually did rather than only that something happened.
Both sides of the log are matched: a change that turned some other code into
this one is logged against the old code, and that is half of what a history
is asked for. So 0150 returns both the changes made to 0150 and the
change that created it out of something else.
A code with nothing logged answers 200 with an empty list rather than
404, even when no such postal code exists. The log only reaches back to the
changes Bring publishes, so "nothing has happened to it" and "it was never a
postal code" are not distinguishable here, and the honest answer to both is
an empty history. Use /postal-codes/{code} to ask whether a code exists.
The whole history comes in one payload and is not paginated: a code carries a
handful of changes at most.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": [
"The postal code must be four digits."
]
}
}
}
Response
Request failed
Response fields
data
object
change_date
string
The date the change took effect, YYYY-MM-DD.
change_type
string
What happened: CREATED, RETIRED, RETIRED_AND_REPLACED, CODE_CHANGED, CITY_CHANGED, MUNICIPALITY_CHANGED, CATEGORY_CHANGED, or UNKNOWN for a label this API has not mapped yet. Branch on this value, never on the prose.
type
string
The postal code category at the time of the change.
old
object
The state before the change. Null only for an entry with nothing recorded on that side.
new
object
The state after the change. Null when the change has no after state at all — a newly created code, or one retired without a replacement.
meta
object
postal_code
string
The code that was asked for, echoed back.
total
integer
The number of entries in data.
latest_change_date
string
The date of the newest entry, or null when there are none.
The outline of the postal code's delivery area as a GeoJSON Feature, ready
to hand straight to Leaflet, Mapbox or anything else that reads GeoJSON.
Coordinates are WGS 84 (EPSG:4326), longitude before latitude, as GeoJSON
requires.
The response is the Feature itself, not wrapped in a data key, so that it
is valid GeoJSON on its own. Geometry is Polygon for a code that covers one
area and MultiPolygon for one split across several. properties repeats
the code, city, municipality and county so a map can label the shape without
a second request.
Not every code has an area: post box and special service codes are addresses
rather than territories, and Geonorge publishes no outline for them. Those
answer 404 with Postal code geometry not found, which is a different
message from the 404 for a code that does not exist at all.
These payloads are large — a detailed outline runs to hundreds of kilobytes.
Request them only when you are drawing a map, and cache what you get.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": [
"The postal code must be four digits."
]
}
}
}
Response
Request failed
Response fields
type
string
Always Feature.
geometry
object
type
string
Polygon for a single area, MultiPolygon for a code split across several.
coordinates
object
GeoJSON coordinate rings in WGS 84, longitude before latitude.
properties
object
postal_code
string
The four digit postal code.
city
string
The postal place (poststed).
municipality
string
The municipality name.
municipality_number
string
The four digit municipality number.
county
string
The county name.
county_number
string
The two digit county number.
accuracy
integer
The positional accuracy of the outline in centimetres, as recorded by Geonorge. Null when the source did not state one.
Addresses
Every official Norwegian address from Kartverket's Matrikkel — both street
addresses and matrikkeladresser — searchable by free text and identified by the
localId the register itself assigns.
Search folds case and accents on both sides, so what a user types matches what
the register stores. Tokens may arrive in any order: storgata 10 oslo and
oslo storgata 10 find the same address.
All four endpoints require a token carrying the location.read scope.
GET/v1/addressesRequires authentication
Search addresses
The endpoint an address autocomplete is built on. The free text is folded
and split into tokens, and every token has to appear somewhere in the
address, its postnummer, its poststed or its kommune. That is what makes
storgata 10 oslo work in any order, and why the postnummer is searchable
even though it is not part of the address line.
q, street and postal_city are all folded into the same token list, so
they are three ways of feeding the same search rather than three separate
filters — sending street=Storgata and postal_city=Oslo is equivalent to
q=Storgata Oslo. postal_code, municipality_number, number, letter
and type are exact filters applied on top.
At least one token or one filter is required, and at least one token must be
three characters or longer. Only one has to be — storgata 10 passes,
because storgata is long enough on its own.
Only active addresses are returned. Results are ordered by street, number,
letter and then id, so paging is stable.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"q": [
"A search term or at least one filter is required."
]
}
}
}
422Every search term was too short to be selective.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"q": [
"At least one search term must be 3 characters or longer."
]
}
}
}
Response
Request failed
Response fields
data
object
id
string
The Matrikkel lokalid. This is the identifier to hand back to /addresses/{localId}, and the one to store — Matrikkelen assigns it, and it stays stable.
uuid
string
The Matrikkel UUID for the same address.
type
string
vegadresse for an address on a named road, matrikkeladresse for one identified by its cadastral numbers.
address_text
string
The official address line, including the tilleggsnavn when the address has one.
address_text_without_additional_name
string
The same line with the tilleggsnavn stripped. For a matrikkeladresse this is only the cadastral numbers, so it is rarely the form to show a user.
street
string
The street name (adressenavn) on its own. Null for a matrikkeladresse.
number
integer
The house number. Null for a matrikkeladresse.
letter
string
The house letter, as in 10B. Null when there is none.
additional_name
string
The tilleggsnavn — a farm or property name such as Myklebust. Null when there is none.
cadastral
object
The Matrikkel numbers: unit_number (gårdsnummer), property_number (bruksnummer), leasehold_number (festenummer) and sub_number (undernummer).
postal_code
string
The four digit postal code.
postal_city
string
The postal place (poststed), upper case.
municipality
object
The municipality number and name.
basic_district
object
Grunnkrets number and name, the smallest statistical unit Statistisk sentralbyrå publishes.
parish
object
Kirkesogn number and name.
urban_settlement
object
Tettsted number and name. Null members for an address outside any.
electoral_district
object
Valgkrets number and name.
country
string
Always NO.
latitude
number
Latitude of the address point, WGS 84.
longitude
number
Longitude of the address point, WGS 84.
updated_at
string
When Matrikkelen last changed this address, ISO 8601 in UTC. Not when Kilda imported it.
meta
object
total
integer
Total matches across all pages.
page
integer
The page returned.
per_page
integer
Results per page.
last_page
integer
The last page number available.
GET/v1/addresses/{localId}Requires authentication
Look up an address
The full record for one address, by the Matrikkel lokalid that every other
endpoint here hands out as id. This is the second half of an autocomplete:
search for suggestions, then look up the one the user picked.
Store the lokalid, not the address line. It is what Matrikkelen assigns and
it stays stable, so it is the only reliable way to refer back to an address.
Unlike search and verify, this endpoint does not filter on status: an address
that has since been retired is still served, because a caller resolving a
stored id needs to know what it used to mean rather than get a 404.
A non numeric id answers 422 rather than 404, so a malformed identifier
is distinguishable from one that simply does not exist.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"id": [
"The address id must be numeric."
]
}
}
}
Response
Request failed
Response fields
data
object
The same address object /addresses returns in its list, without distance_metres.
GET/v1/addresses/verifyRequires authentication
Verify an address
Answers whether one specific address exists, and nothing more. The check to
run against a shipping address at checkout, or against a field a user has
finished typing.
A miss is a 200 with valid: false, not a 404. "No" is an answer to the
question, and a form asking on every blur should not have to treat it as a
failure. A 404 from this endpoint would mean the route is wrong, not that
the address is.
Give the address either as one address_text line or as street plus
number and letter; one of the two is required, as is postal_code. Both
forms are folded the same way, so case, accents and spacing do not matter —
storgata 10b and Storgata 10B verify alike. The letter belongs against
the number, as Matrikkelen writes it: Storgata 10B, not Storgata 10 B.
An address pasted back from an earlier response still verifies even though it
carries a tilleggsnavn that the folded key leaves out.
Only active addresses count. A retired address answers valid: false.
200No active address matches. Note the status is 200, not 404.
{
"data": {
"valid": false
}
}
422Neither `address_text` nor `street` was given, or the postal code is not four digits.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"postal_code": [
"The postal code field format is invalid."
]
}
}
}
422The address was given, but nothing readable was left of it once folded.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"address_text": [
"The address could not be read."
]
}
}
}
Response
Request failed
Response fields
data
object
valid
boolean
Whether an active address matched. This is the only field guaranteed to be present — the rest appear only when it is true.
id
string
The Matrikkel lokalid of the address that matched. Pass it to /addresses/{localId} for the full record.
address_text
string
The official address line, which may differ from what you sent — this is the form to store.
postal_code
string
The four digit postal code.
postal_city
string
The postal place (poststed), upper case.
municipality_number
string
The four digit municipality number.
GET/v1/addresses/nearbyRequires authentication
Find addresses near a point
Every active address within a radius of a coordinate, nearest first. Use it
to turn a GPS fix or a map click into the address the user is standing at.
The radius is in metres and defaults to 100, with a ceiling of 2000.
Each result carries distance_metres, which only this endpoint reports —
the other address endpoints measure nothing, so the field is absent there.
Results are truncated at per_page, not paginated. There is no page two: if
the nearest ten are not enough, narrow or widen the radius instead.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"latitude": [
"The latitude field is required."
]
}
}
}
Response
Request failed
Response fields
data[]
object
The same address object /addresses returns, with distance_metres added.
data
object
distance_metres
number
Straight line distance from the point you gave, in metres, to one decimal.
meta
object
radius_metres
integer
The radius actually searched, so a caller who omitted it learns the default.
returned
integer
How many addresses are in data. Equal to per_page when the radius held more, so treat that as "there may be more" rather than as a total.
Counties
The Norwegian counties (fylker) as Kartverket's Kommuneinfo publishes them,
each with the municipalities it contains and the outline Geonorge draws for it.
A county is identified by its two digit number (fylkesnummer). Anything that is
not two digits is refused with 422 rather than looked up.
/counties — every county, in one response.
/counties/{code} — one county.
/counties/{code}/municipalities — the municipalities it contains.
/counties/{code}/geometry — its outline as a GeoJSON Feature.
Svalbard (21) and Jan Mayen (22) are served as counties because addresses
and postal codes there are registered under them, but Geonorge publishes no
outline for either. has_geometry says so before you ask.
All four require a token carrying the location.read scope.
GET/v1/countiesRequires authentication
List counties
Every Norwegian county in one response, ordered by name. There are
seventeen — the fifteen counties plus Svalbard and Jan Mayen — so there is
nothing to paginate and no query parameters to get wrong.
This is a reference list, not a search: it changes when the counties
themselves are reorganised, which last happened on the first of January
2024. Fetch it once, cache it, and build your dropdown from it.
The outlines are left out — /counties/{code}/geometry serves those one at
a time, and has_geometry tells you which counties have one.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"county_number": [
"The county number must be two digits."
]
}
}
}
Response
Request failed
Response fields
data
object
number
string
The two digit county number (fylkesnummer).
name
string
The county name.
municipality_count
integer
How many municipalities the county contains.
country
string
Always NO.
latitude
number
Latitude of the county's representative point, WGS 84.
longitude
number
Longitude of the county's representative point, WGS 84.
has_geometry
boolean
Whether /counties/{code}/geometry will return an outline.
Every municipality in one county, ordered by name. The county is the URL,
so it is reported once in meta rather than repeated on every entry.
An unknown county number answers 404 rather than an empty list: a county
with no municipalities does not exist, so an empty response here would only
ever mean the number was wrong.
This is the same data as /municipalities?county_number={code}, in the
shape you want when you are drilling down rather than searching.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"county_number": [
"The county number must be two digits."
]
}
}
}
Response
Request failed
Response fields
data
object
number
string
The four digit municipality number (kommunenummer).
name
string
The municipality name.
country
string
Always NO.
latitude
number
Latitude of the municipality's representative point, WGS 84.
longitude
number
Longitude of the municipality's representative point, WGS 84.
has_geometry
boolean
Whether /municipalities/{code}/geometry will return an outline.
updated_at
string
When the source last changed this municipality, ISO 8601 in UTC.
The county's border as a GeoJSON Feature, ready to hand straight to
Leaflet, Mapbox or anything else that reads GeoJSON. Coordinates are WGS 84
(EPSG:4326), longitude before latitude, as GeoJSON requires.
The response is the Feature itself, not wrapped in a data key, so that it
is valid GeoJSON on its own. Geometry is Polygon for a county in one piece
and MultiPolygon for one that includes islands. properties repeats the
number and name so a map can label the shape without a second request.
This is the real border, not a bounding box. Svalbard and Jan Mayen have no
published outline and answer 404 with County geometry not found, which
is a different message from the 404 for a county that does not exist at
all. has_geometry on the lookup tells you which you are dealing with.
These payloads are large — a detailed county border runs to hundreds of
kilobytes. Request them only when you are drawing a map, and cache what you
get.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"county_number": [
"The county number must be two digits."
]
}
}
}
Response
Request failed
Response fields
type
string
Always Feature.
geometry
object
type
string
Polygon for a county in one piece, MultiPolygon for one that includes islands.
coordinates
object
GeoJSON coordinate rings in WGS 84, longitude before latitude.
properties
object
county_number
string
The two digit county number.
county
string
The county name.
Municipalities
Every Norwegian municipality (kommune) as Kartverket's Kommuneinfo publishes
them, with the county it belongs to and the outline Geonorge draws for it.
A municipality is identified by its four digit number (kommunenummer), the same
number an address or a postal code reports under municipality.number. Anything
that is not four digits is refused with 422 rather than looked up.
/municipalities — every municipality, optionally filtered by name or county.
/municipalities/{code} — one municipality.
/municipalities/{code}/geometry — its outline as a GeoJSON Feature.
Municipality numbers change when the counties are reorganised. Trondheim was
1601 until 2018 and has been 5001 since, and the 2024 county split moved
every 38xx municipality to 39xx or 40xx. Store the number the register
gave you, but resolve the name from here rather than storing both.
All three require a token carrying the location.read scope.
GET/v1/municipalitiesRequires authentication
List municipalities
Every Norwegian municipality in one response, ordered by name, or the
subset matching a name fragment or a county.
There are 359 rows — the 357 municipalities plus Svalbard and Jan Mayen,
which are registered as municipalities so that addresses and postal codes
there have one. The whole list is returned in one payload rather than
paginated: a caller building a dropdown wants all of it, and paging would
cost them 36 requests to learn nothing new. It is a reference list that
changes on the first of January and almost never otherwise — fetch it once
and cache it.
q matches anywhere in the name and ignores case, so trond finds
Trondheim. It is a plain substring match rather than the folded token
search /addresses uses: a municipality name is one word from a closed
list, not free text.
The outlines are left out — /municipalities/{code}/geometry serves those
one at a time, and has_geometry tells you which have one.
422The search term was too short, or the county number was not two digits.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"q": [
"The search term must be 2 characters or longer."
]
}
}
}
Response
Request failed
Response fields
data
object
number
string
The four digit municipality number (kommunenummer).
name
string
The municipality name.
county
object
The county number and name.
country
string
Always NO.
latitude
number
Latitude of the municipality's representative point, WGS 84. Null when it has no outline to derive one from.
longitude
number
Longitude of the municipality's representative point, WGS 84.
has_geometry
boolean
Whether /municipalities/{code}/geometry will return an outline.
updated_at
string
When the source last changed this municipality, ISO 8601 in UTC. Not when Kilda imported it.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"municipality_number": [
"The municipality number must be four digits."
]
}
}
}
Response
Request failed
Response fields
data
object
number
string
The four digit municipality number (kommunenummer).
name
string
The municipality name.
county
object
The county number and name.
country
string
Always NO.
latitude
number
Latitude of the municipality's representative point, WGS 84.
longitude
number
Longitude of the municipality's representative point, WGS 84.
has_geometry
boolean
Whether /municipalities/{code}/geometry will return an outline.
updated_at
string
When the source last changed this municipality, ISO 8601 in UTC.
The municipality's border as a GeoJSON Feature, ready to hand straight to
Leaflet, Mapbox or anything else that reads GeoJSON. Coordinates are WGS 84
(EPSG:4326), longitude before latitude, as GeoJSON requires.
The response is the Feature itself, not wrapped in a data key, so that it
is valid GeoJSON on its own. Geometry is Polygon for a municipality in one
piece and MultiPolygon for one that includes islands. properties repeats
the municipality and county so a map can label the shape without a second
request.
This is the real border, not a bounding box. A municipality with no
published outline answers 404 with Municipality geometry not found,
which is a different message from the 404 for a number that belongs to no
municipality at all. has_geometry on the lookup tells you which you are
dealing with.
These payloads are large — a detailed border runs to hundreds of kilobytes.
Request them only when you are drawing a map, and cache what you get.
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid",
"fields": {
"municipality_number": [
"The municipality number must be four digits."
]
}
}
}
Response
Request failed
Response fields
type
string
Always Feature.
geometry
object
type
string
Polygon for a municipality in one piece, MultiPolygon for one that includes islands.
coordinates
object
GeoJSON coordinate rings in WGS 84, longitude before latitude.