Part of a Data Solution
- Explore →
Postal Codes Solution
Every worldwide postal-code dataset in one bundle — the ultimate global reference for precise postal codes.
Premium
You're viewing a free sample
Get the complete dataset — all rows and files — delivered instantly after checkout, with lifetime access to the latest version.
- Secure checkout via Stripe
- Instant download after payment
- Lifetime access to the latest version
- Norwegian Licence for Open Government Data 2.0 license
API Access
Access dataset files directly from scripts, code, or AI agents.
Browse dataset files
API Access
Access dataset files directly from scripts, code, or AI agents.
Each file has a stable URL (r-link) that you can use directly in scripts, apps, or AI agents. These URLs are permanent and safe to hardcode.
Start with these files — they give you everything you need to understand and access the dataset.
- 1. Fetch datapackage.yaml to inspect schema and resources
- 2. Download data resources listed in datapackage.yaml
- 3. Read README.md for full context
Data Files
postal-codes-no-sample
| Field | Type | Description | Constraints | Title |
|---|---|---|---|---|
| country_code | string | ISO 3166-1 alpha-2 -- always NO | Country Code | |
| postal_code | string | Norwegian postal code (4 digits) | { "pattern": "^\\d{4}$" } | Postal Code |
| place_name | string | Postal town/place name, as maintained by Bring/Posten Norge | Place Name | |
| admin_name1 | string | Fylke (county) -- 17/17 covered, including the special territories Svalbard and Jan Mayen | Administrative Name 1 | |
| admin_code1 | string | Norway's own official fylkesnummer, NO- prefixed. 10 of 17 have a current ISO 3166-2:NO entry; the other 7 (Ostfold, Akershus, Buskerud, Vestfold, Telemark, Troms, Finnmark) are real post-2024 fylkesnummer with no individual ISO entry yet -- ISO's register has not caught up with Norway's 2024 fylke reform, see ATTRIBUTION.txt | Administrative Code 1 | |
| admin_name2 | string | Kommune (municipality) -- 359/359 covered | Administrative Name 2 | |
| admin_code2 | string | Official kommunenummer (4-digit) | Administrative Code 2 | |
| admin_name3 | string | Not populated -- Bring's own register does not carry a tier below kommune | Administrative Name 3 | |
| admin_code3 | string | Not populated | Administrative Code 3 | |
| latitude | number | Not populated in this release -- Bring's postnummerregister carries no coordinates | Latitude | |
| longitude | number | Not populated in this release | Longitude | |
| accuracy | string | Not populated | Accuracy | |
| alternative_city_name | string | Not populated in this release | Alternative City Name |
Download
Download sample CSVAbout
- Last updated
- 11 October 2026
- Total rows
- 100
- Format
- CSV
- File size
- 4.16 kB
About this dataset
Norway (NO): migration notes
Internal record of the P5 migration from the old script to the producer: the discrepancies found, their causes, and who decided what. This is a dated record (2026-10-11), so the numbers are as of the migration and not kept current. It is not shipped in the zip.
- Issue: datopian/postal-codes#41 (no builder scripts in the repo)
- Commits: the recovered builder, then the producer (this branch)
- Parity verdict:
unexplained(gate: blocked), accepted as upstream drift under the owner's under-1% rule - Producer:
custom, refresh disabled
Recovered builder
build_postal_codes_product.py (dated 2026-08-31) was found uncommitted in the working copy, together with the saved Bring file. It is committed unchanged in the commit before this one, then removed by the producer commit.
Re-run on 2026-10-11 against its saved inputs it reproduces the live no.csv byte for byte (sha256 3ac8f37eda…). The producer was then checked the same way: its transform() on those saved inputs gives the live data, differing only in line endings.
Sources
| Input | URL | Notes |
|---|---|---|
| Bring / Posten Norge postnummerregister | https://www.bring.no/postnummerregister-ansi.txt | NLOD 2.0 (attribution required, resale permitted). ISO-8859-1, tab-delimited |
Parity against the live zip (2026-10-11)
Rows: 5,122 live and 5,112 fresh. 14 postal codes only in live, 4 only in fresh, 0 changed cells in the 5,108 rows that remain (-0.2% of rows, inside the 1.5% max_row_drop).
Cause: edits to Bring's register (upstream drift). The raw file differs from the saved August copy by 15 lines removed and 5 added:
| Change | Rows |
|---|---|
| Removed from the register | 2620 Mesnali, 2681 Lalm, 3197 Nykirke, 3245 Kodal, 5213 Lepsøy, 6331 Verma, 6623 Ålvundfjord, 7053 Ranheim, 7151 Storfosna, 7333 Storås, 8374 Ballstad, 8419 Sortland, 9391 Stonglandseidet, 9456 Engenes |
| Added | 1426 Solberg, 5245 Fana, 7053 Charlottenlund, 9653 Hellefjord |
| Type changed only (not in the output) | 2309 Hamar, P → G |
The old script's EXPECTED_ROWS = 5122 assert refuses today's file, which is why the producer has no row-count assert.
Decisions
| Decision | By | Basis |
|---|---|---|
| Port the recovered builder faithfully; no new source or logic | User, 2026-10-11 | The 2026-10-03 rule ("no producer from scratch when no builder exists") doesn't apply: the builder was found, and re-running it reproduces the live CSV byte for byte |
| LF line endings (the old script wrote CRLF) | Standing rule (user, 2026-10-02) | format-only verdicts pass; every shared producer writes LF |
Drop the hardcoded row-count assert (the old script's EXPECTED_ROWS = 5122 stopped on Bring's 5,112) | P5 rule | Structural checks (columns, uniqueness, formats, full admin coverage) stand in |
| Keep README.md and ATTRIBUTION.txt from the repo; don't re-harvest them from the live zip | Agent, following PR #38 | The repo copies were corrected there (no hardcoded data, true sources); the zip copies still carry counts and the old wording |
refresh.enabled: false | Agent | Nothing publishes before the owner signs off; flip it when R2 is updated |
| Accept the 14 removed and 4 added rows | Owner, 2026-10-11 ("if they differ by less than 1%, then ok"); standing rule for traced upstream drift (user, 2026-10-03) | Traced to Bring's own register above; 0.27% of rows removed, 0 cells changed |
Transforms carried over
- Source rows →
place_nameandadmin_name2(kommune) withstr.title();admin_code2is the 4-digit kommune number; the register's type column is dropped. admin_name1/admin_code1(NO-<fylkesnummer>) from the first two digits of the kommune number. Seven of the 17 fylker (31, 32, 33, 39, 40, 55, 56) have no ISO 3166-2:NO entry yet and keep the real fylkesnummer.- Sorted by postal code. Checks (4-digit codes, unique postal code, no
nan, all 17 fylker present, a known fylkesnummer on every row) areProducerErrors.
Open questions
- The public sample at
postal.datahub.io/no/no.csvis an older build than the zip (0 of its 100 rows match the zip's first 100; it starts at 1300 Sandvika withadmin_code11). Publishing the new sample fixes this; nothing has been uploaded. - R2 is untouched. Publishing waits for the owner's sign-off.
Provenance
Written 2026-10-11 from the recovered script, the re-run against R2, scripts/parity.py no (reads the live zip, writes nothing) and the Frictionless validation of the built zip.