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
- Creative Commons Attribution 4.0 — GeoNames (postal codes, place names, administrative names, coordinates, alternative names) 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-pt-sample
| Field | Type | Description | Constraints | Title |
|---|---|---|---|---|
| country_code | string | ISO 3166-1 alpha-2 country code — always PT | { "pattern": "^PT$", "required": true } | Country Code |
| postal_code | string | Portuguese postal code, formatted 'NNNN-NNN' | { "pattern": "^\\d{4}-\\d{3}$", "required": true } | Postal Code |
| place_name | string | Place / locality / street segment name for the postal code | Place Name | |
| admin_name1 | string | District (Distrito) or autonomous region name | Administrative Name 1 | |
| admin_code1 | string | ISO 3166-2:PT code, prefix stripped (01-18, 20, 30) | Administrative Code 1 | |
| admin_name2 | string | Municipality (Concelho) name | Administrative Name 2 | |
| admin_code2 | string | Municipality code (distrito+concelho, 4 digits) | Administrative Code 2 | |
| admin_name3 | string | Parish / locality (Freguesia) name | Administrative Name 3 | |
| admin_code3 | string | Official CAOP DICOFRE parish code: the 4-digit municipality code plus 2 characters (digits, or letters where a municipality has more than 99 parishes, e.g. 0302FA). Empty where neither the parish name nor the coordinate resolves it — see README | Administrative Code 3 | |
| latitude | number | Latitude, decimal degrees (WGS84) | Latitude | |
| longitude | number | Longitude, decimal degrees (WGS84) | Longitude | |
| accuracy | string | GeoNames accuracy of latitude/longitude. GeoNames documents 1=estimated, 4=geonameid, 6=centroid of addresses or shape; other values (e.g. 3) occur upstream without documentation. Empty where GeoNames gives none. | Accuracy | |
| alternative_city_name | string | Alternative or historic name for the place where known, from the GeoNames gazetteer | Alternative City Name |
Download
Download sample CSVAbout
- Last updated
- 6 October 2026
- Total rows
- 100
- Format
- CSV
- File size
- 8.33 kB
About this dataset
Portugal (PT): 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.47 (closed 2026-10-05)
- Commit:
dbdaa34(worktree commit6f28f9e), landed in batch 6 - Parity verdict:
unexplained(the gate blocks on any unclassified cell); accepted as upstream drift - Producer:
custom, refresh disabled
Sources
| Input | URL | Notes |
|---|---|---|
| GeoNames postal export | https://download.geonames.org/export/zip/PT.zip | |
| GeoNames gazetteer dump | https://download.geonames.org/export/dump/PT.zip | Used for alternative_city_name; replaces the legacy R2 copy |
CAOP freguesia table (core.json) | https://aldeiasportuguesas.pt/dados/ → /data/aldeias-portuguesas-vYYYY.N/ | Republication of DGT CAOP 2025 + INE Censos 2021 (CC BY-SA 4.0). This is the old caop_freguesias.json |
CAOP freguesia polygons (geo.geojson) | same versioned directory | Same DICOFRE keys |
- No CAOP URL was recorded anywhere in the old scripts. The agent found both files on the Aldeias Portuguesas download page.
- The directory is versioned. The producer reads
/dados/, takes the highest version that links both files (v2026.2 at migration time, the CAOP 2025 data live was built from), and records the resolved URLs inpt.inputs.json.--no-fetchreuses them.
Parity against the live zip (2026-10-03)
Rows: 206,942 live and 206,942 fresh, none only on one side. 18 changed cells, all in alternative_city_name. Every admin code and name matches live, including admin_code3 / admin_name3. Row order differs only within a postal code (GeoNames' tie order), which is covered by the row_order rule.
Cause: edits to the GeoNames dump (upstream drift).
| Rows | Change | Cause |
|---|---|---|
| 17 × "Ponte Nova" (e.g. 3105-449, 3720-479, 2350-466, 3250-037) | Ponte da Cavada Velha → empty | New dump entry geonameid 13712883 (BDG, no alternate names, modified 2026-09-25) has the same asciiname and comes after 7116987; the rule is "last matching entry wins" |
| 3440-306 Franca | empty → Franca | Dump entry 8011428 now reads "Franca,França" (modified 2026-04-14), newer than the legacy R2 snapshot the old script copied from |
Decisions
| Decision | By | Basis |
|---|---|---|
| Accept the 18 alt-name cells | Standing rule (upstream drift traced to source, user, 2026-10-03) | Same kind as pl's 32 cells; recorded in the sign-off section of docs/parity/p5.md |
Rebuild alternative_city_name from the GeoNames dump rather than copying the legacy R2 pt/pt.csv | D1 (pl precedent) | The old recover_alt_names.py read the live R2 object, which isn't a real source |
| Don't add an "alt name repeats place name" filter | Agent: a faithful port | pt's old script didn't have one, so the Franca echo stays |
| Keep the Aldeias Portuguesas republication; don't switch to DGT's own CAOP downloads | Agent: a faithful port; surfaced to the user | The old builder used it, and switching would be a source switch |
admin_code2 that disagrees with CAOP now stops the build | Agent | The old script only reported it |
Accept alphanumeric DICOFRE codes (\d{4}[0-9A-Z]{2}) | Agent: bug found during the port | Barcelos and Guimarães pass 99 parishes and continue with letters (0302FA, 0308FE); live has 751 such rows. The admin_code3 description, which said "6 digits", was corrected |
Transforms carried over
Folded from fetch_source.py → build_base.py → enrich_caop.py → spatial_join_freguesia.py → recover_alt_names.py → integrity_checker.py:
- GeoNames codes and names copied verbatim as text (no pandas float coercion).
admin_code1→ ISO 3166-2:PT from the district/region name (GeoNames' number isn't ISO), with the canonicaladmin_name1. An unknown name is aProducerError.admin_code3by CAOP name match: exact, then with the trailing bracketed part dropped, then an unambiguous pre-2013 union constituent. Azores and Madeira are keyed onsource_region. With no match, GeoNames' unofficial code is cleared. (GeoNames' freguesia code is its own alphabetical numbering, not DICOFRE; e.g. Agadão is GeoNames010101but DICOFRE010128.)- Then point-in-polygon (shapely STRtree,
buffer(0)repair) for rows still uncoded, only inside the row's own concelho and only when exactly one polygon contains the point.admin_name3is set to the official CAOP name. - The integrity checks are now
ProducerErrors. The primary key is (postal_code, place_name). Rows keep GeoNames file order.
Open questions
- Resolved (user, 2026-10-05, pc-28e.7.59): CAOP via Aldeias Portuguesas is accepted long term.
.licensing-notes.mdstill says "14,538 rows", which is a stale historical count. It was left alone; it belongs to the pc-hfz sweep.
Provenance
Reconstructed 2026-10-05 from the agent transcript 7df0c5be-a523-4080-87f9-6045a6a41515/subagents/agent-a0e0a20077716a524.jsonl, the orchestrator session 7df0c5be-…, the commit message and the bd close reason.