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
- opendata.swiss terms of use, open tier (commercial use permitted, attribution recommended) 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-ch-sample
| Field | Type | Description | Constraints | Title |
|---|---|---|---|---|
| country_code | string | ISO 3166-1 alpha-2 -- always CH | Country Code | |
| postal_code | string | Swiss 4-digit postal code (PLZ) | { "pattern": "^\\d{4}$" } | Postal Code |
| place_name | string | Locality name. Large-account subdivisions appear as distinct rows (e.g. 'Lausanne 25'), as do PO-box/corporate codes (e.g. 'Zofingen PF') | Place Name | |
| admin_name1 | string | Canton -- official ISO 3166-2:CH subdivision name (all 26 covered) | Administrative Name 1 | |
| admin_code1 | string | ISO 3166-2:CH canton code, e.g. CH-ZH | Administrative Code 1 | |
| admin_name2 | string | District (Bezirk) -- empty where the register is ambiguous after a municipality merger | Administrative Name 2 | |
| admin_code2 | string | Official BFS district number (Bezirksnummer) | Administrative Code 2 | |
| admin_name3 | string | Municipality (Gemeinde) | Administrative Name 3 | |
| admin_code3 | string | Official BFS municipality number (BFS-Nr) | Administrative Code 3 | |
| latitude | number | WGS84 latitude | Latitude | |
| longitude | number | WGS84 longitude | Longitude | |
| accuracy | string | Not populated -- the official source carries no coordinate-accuracy grade | Accuracy | |
| alternative_city_name | string | Not populated in this release | Alternative City Name | |
| code_type | string | Extra field, beyond the core 13. 'locality' = official swisstopo locality row; 'po_box_or_large_account' = PO-box/corporate/branch code that the official locality register does not list | { "enum": [ "locality", "po_box_or_large_account" ] } | Code Type |
| address_share_pct | string | Extra field, beyond the core 13. For localities split across municipalities, this municipality's percentage share of the locality's addresses. Empty for po_box_or_large_account rows | Address Share Percent |
Download
Download sample CSVAbout
- Last updated
- 6 October 2026
- Total rows
- 100
- Format
- CSV
- File size
- 12.7 kB
About this dataset
Switzerland (CH): migration notes
Internal record of the P5 migration from the old scripts to the producer: the discrepancies found, their causes, and who decided what. This is a dated record, so the numbers are as of the migration and not kept current. It is not shipped in the zip.
- Issue: pc-28e.7.10 (closed 2026-10-03)
- Commit:
8947b96(worktree commit1f2124d, same content), landed in batch 3 after the user's sign-off (6c221ca) - Parity verdict:
unexplained(the gate blocks on any unclassified cell); accepted by the user as upstream drift - Producer:
custom, refresh disabled
Sources
| Input | URL | Notes |
|---|---|---|
| swisstopo Amtliches Ortschaftenverzeichnis (WGS84 CSV) | https://data.geo.admin.ch/ch.swisstopo-vd.ortschaftenverzeichnis_plz/ortschaftenverzeichnis_plz/ortschaftenverzeichnis_plz_4326.csv.zip | Backbone. Stable "current release" URL, refreshed monthly; the file fetched was dated 2026-09-30. opendata.swiss terms_open |
| GeoNames postal export | https://download.geonames.org/export/zip/CH.zip | Only for the Bezirk (district) tier and for postal codes swisstopo doesn't list |
Both are the inputs the old builder used.
Parity against the live zip (2026-10-02)
Rows: 5,974 live and 5,972 fresh. 2 rows only in live, 0 only in fresh. 1,377 rows changed, 2,535 cells, all unclassified: latitude 933, longitude 933, address_share_pct 669. No other column changed. PO-box/large-account rows: 276 on both sides.
Cause: swisstopo's monthly release has moved on since the live build (upstream drift).
| Rows | Change | Cause |
|---|---|---|
| 1149 Berolle / Bière (admin_code3 5425) | only in live | The current swisstopo file has no Bière slice of 1149 Berolle; Berolle's share went 99.574 → 100 |
| 9104 Waldstatt / Schwellbrunn (admin_code3 3004) | only in live | The current file has no Schwellbrunn slice; Waldstatt 99.496 → 99.697, Urnäsch 0.302 → 0.303 |
| 669 cells | address_share_pct re-normalised | Same: shares recomputed in the new release |
| 933 rows (e.g. 1008 Prilly, 1012 Lausanne, 1020 Renens VD) | coordinates shifted, e.g. Prilly latitude 46.5367525803778 → 46.536752484570144, longitude 6.604545982129699 → 6.604532947343865 | New swisstopo release. The examples move by about 1 m; the full distribution wasn't measured |
The live CSV matches the old swisstopo values (e.g. Bière 0.426 %), so this is the source changing, not a missing transform. The agent did not tune the producer to hide it.
Decisions
| Decision | By | Basis |
|---|---|---|
| Accept the drift and land ch | User (session 2479b1c9-…, 2026-10-03, AskUserQuestion "ch/fi drift" → "Accept both") | Recorded in the sign-off section of docs/parity/p5.md |
| Fetch swisstopo's stable "current release" URL rather than a dated one | Agent | swisstopo publishes no dated URL; the docstring says so |
| Remove row, distinct-PLZ, split-locality, district-fill and PO-box row counts from README and descriptor; "duplicates 0" → "the producer checks it is unique" | Orchestrator instruction (the README counts rule, user 2026-10-02) | Kept the 26 cantons and 2,112 municipalities |
Replace the README's licence_status.decision sentence with "both licences permit commercial use" | Agent | No such field in the descriptor (same fix as batch 1) |
Transforms carried over
Folded from build_postal_codes_product.py (2026-09-16 rebuild):
- swisstopo locality rows are the backbone. Locality rows split across municipalities stay as one row per municipality, with their address share ("84.138 %" → "84.138").
admin_code1=CH-+ swisstopo's Kantonskürzel (the ISO 3166-2:CH suffix);admin_name1is the ISO canton name.- Liechtenstein rows (blank Kantonskürzel, PLZ 9485–9499) are dropped.
- The district comes from GeoNames, joined on the BFS municipality number (never on names). It is left empty where GeoNames gives a municipality more than one district (leftovers from mergers).
- Postal codes in GeoNames but not in swisstopo (e.g. "Zofingen PF") are added with
code_type = po_box_or_large_account. accuracyandalternative_city_nameare empty.- The old asserts are now
ProducerErrors: 4-digit postal code, non-empty place name, no literalnan, known canton, unique (postal_code, place_name, admin_code3), all 26 cantons present. No row-count asserts.
Open questions
- Resolved (user, 2026-10-05, pc-28e.7.59): gating of recurring swisstopo drift is left to the P8 refresh.yml design (comment on pc-28e.11). Refresh stays disabled until then.
ATTRIBUTION.txtstill has "5,974 rows", the 276 PO-box rows and "93 rows carry no district value";.licensing-notes.mdhas historical counts. Both are for pc-3d2 / pc-hfz.
Provenance
Reconstructed 2026-10-05 from the agent transcript 2479b1c9-c4dd-47e5-be82-34ccf8d49a80/subagents/agent-a88bf7f3ca81e1172.jsonl, the orchestrator session 2479b1c9-… (also checked d33e1b7e-…, 7df0c5be-…, 068ca403-…: no further ch decisions), the commit messages (8947b96, 6c221ca), the bd close reason and docs/parity/p5.md.