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 International 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-rs-sample
| Field | Type | Description | Constraints | Title |
|---|---|---|---|---|
| country_code | string | ISO 3166-1 alpha-2 -- always RS | Country Code | |
| postal_code | string | Serbian postal code (5 digits) | { "pattern": "^\\d{5}$" } | Postal Code |
| place_name | string | Locality/place name as GeoNames' postal export records it | Place Name | |
| admin_name1 | string | District (okrug) -- 25/25 covered, resolved via a coordinate-based join against GeoNames' own gazetteer, see ATTRIBUTION.txt | Administrative Name 1 | |
| admin_code1 | string | ISO 3166-2:RS district code | Administrative Code 1 | |
| admin_name2 | string | "Vojvodina" for the 7 Vojvodina districts; empty for the other 18 (incl. Belgrade), which have no parent province in Serbia's own ISO structure | Administrative Name 2 | |
| admin_code2 | string | ISO 3166-2:RS-VO where applicable, else empty | Administrative Code 2 | |
| admin_name3 | string | Not populated -- no sellable source reaches opština (municipality) level | Administrative Name 3 | |
| admin_code3 | string | Not populated | Administrative Code 3 | |
| latitude | number | GeoNames postal-export latitude (WGS84) | Latitude | |
| longitude | number | GeoNames postal-export longitude (WGS84) | Longitude | |
| accuracy | string | AREA throughout -- coarse city/town-level point, shared across all codes in that place | 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
- 6.15 kB
About this dataset
Serbia (RS): 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:
explained(gate: sign-off) [row_order×1], covered by the standing sign-off - Producer:
custom, refresh disabled
Recovered builder
build_postal_codes_product.py (dated 2026-08-31) was found uncommitted in the working copy, together with its two saved GeoNames inputs. 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 rs.csv byte for byte (sha256 ed43ff7656…). The producer was then checked the same way: its transform() on those saved inputs gives the live data, differing only in line endings, and in row order inside shared postal codes.
Sources
| Input | URL | Notes |
|---|---|---|
| GeoNames postal export | https://download.geonames.org/export/zip/RS.zip | CC BY 4.0. Admin fields are empty in this file |
| GeoNames gazetteer dump | https://download.geonames.org/export/dump/RS.zip | Same publisher and licence. Gives each populated place its district (admin1 SE/VO, admin2 0-24) |
Parity against the live zip (2026-10-11)
Rows: 1,155 live and 1,155 fresh, none only on one side, 0 changed cells. Order differs only inside postal codes that have several rows (GeoNames' tie order), the row_order class pre-approved on 2026-10-02.
GeoNames' files haven't changed for Serbia since the live build, so the only difference is the row order and CRLF → LF.
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 | 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 |
Transforms carried over
- District (okrug): each postal row is matched to a gazetteer place by name (or asciiname); with several candidates the nearest by haversine distance wins, if within 50 km. Otherwise the nearest populated place of any name within 100 km. A row with none is a
ProducerError. - District name and ISO code from a fixed table; "Morava" is corrected to "Moravica" (
RS-17). The 7 Vojvodina districts also getadmin_name2"Vojvodina" andRS-VO. - Coordinates rounded to 5 decimals,
accuracyAREA. Checks (5-digit codes, unique(postal_code, place_name), nonan, all 25 districts) areProducerErrors.
Open questions
- The public sample at
postal.datahub.io/rs/rs.csvis an older build than the zip (no district,accuracy4; 0 of its 100 rows match the zip's first 100). 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 rs (reads the live zip, writes nothing) and the Frictionless validation of the built zip.