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
- MIT License 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-br-sample
| Field | Type | Description | Constraints | Title |
|---|---|---|---|---|
| country_code | string | ISO 3166-1 alpha-2 country code — always BR | { "pattern": "^BR$", "required": true } | Country Code |
| postal_code | string | Brazilian CEP, format NNNNN-NNN (full 8-digit code) | { "pattern": "^\\d{5}-\\d{3}$", "required": true } | Postal Code |
| place_name | string | Bairro (neighborhood) — the CEP system's sub-municipal delivery unit | Place Name | |
| admin_name1 | string | State or Federal District name (Estado / Distrito Federal) | Administrative Name 1 | |
| admin_code1 | string | ISO 3166-2:BR state code, BR- prefix stripped (e.g. SP, RJ, DF) | Administrative Code 1 | |
| admin_name2 | string | Municipality name (Município) | Administrative Name 2 | |
| admin_code2 | string | IBGE municipality code (7-digit) | Administrative Code 2 | |
| admin_name3 | string | Bairro (neighborhood) — same value as place_name | Administrative Name 3 | |
| admin_code3 | string | Reserved — no bairro-level code exists in any source | Administrative Code 3 | |
| latitude | number | Latitude coordinate (WGS84, decimal degrees) — not available for BR | Latitude | |
| longitude | number | Longitude coordinate (WGS84, decimal degrees) — not available for BR | Longitude | |
| accuracy | string | Coordinate accuracy tier — not available for BR | Accuracy | |
| alternative_city_name | string | Full compound bairro name where the source assigns one CEP to a zone spanning two former neighborhoods (e.g. 'Jardim Monte Cristo/Parque Oziel'); place_name carries only the first component | Alternative City Name |
Download
Download sample CSVAbout
- Last updated
- 12 October 2026
- Total rows
- 100
- Format
- CSV
- File size
- 7.47 kB
- Sources
- SeuAliado/OpenCEP (MIT License) — primary bulk CEP data
- Mendeley Data, "Códigos de CEP do Brasil por UF, CIDADE, BAIRRO E LOGRADOURO" by Ivan Zimmermann (CC BY 4.0) — secondary CEP data, consolidated for coverage
- IBGE Localidades API — official state and municipality reference
- kelvins/municipios-brasileiros (MIT License) — municipality dictionary
- License
- MIT License
About this dataset
Brazil (BR): migration notes
Internal record of the P5 migration. This is a dated record (2026-10-12) and is not shipped in the zip.
- Issue: datopian/postal-codes#41 (pc-28e.7.8, was deferred 2026-10-03)
- Producer:
scripts/produce.py(+test_produce.py),pipeline: producer: custom, refresh disabled - Parity:
unexplainedby the repo's classifier (every difference is in a class it has no name for), signed off by nobody yet. Against the live zip'sbr.csv: 1,233,231 live rows, 1,232,818 fresh; 442 rows differ in all, 0.036% (413 only in live, 29 changed). All 442 are traced below.
Why a new producer, not a port of the legacy chain
The old chain (legacy_source_pipeline/brazil_postal_code_parse.py -> refine_raw_source.py -> run_qa.py -> build_postal_codes_product.py, with scrape_admin.py) was run on today's upstream inputs (2026-10-12) and does not reproduce the live file: 1,207,370 rows against 1,233,231, and 31,882 rows (2.59%) differ. Run from the saved prototype intermediates it was byte-identical to live (checked earlier in the session), so the committed code is not the code that produced the live file. Measured stage by stage:
| Stage | Saved (what built live) | Rerun on fresh inputs | Difference |
|---|---|---|---|
parse -> final_output -> raw.csv | 1,233,231 rows | 1,232,991 | 240 CEPs only in saved; 0 only in fresh |
raw.csv cells, common CEPs | county 29,754 (23,678 now empty); stateName 1,946 (all empty); cityName 298; ibgeCode 3; additionalInfo all 1,232,991 (see below) | ||
Brazil.csv (QA) | 1,233,232 | 1,207,391 | 25,861 only in saved = 25,621 dropped by QA as "county must not be empty" + the 240 |
13-column br.csv vs live | 1,233,231 | 1,207,370 | 25,861 only in live; 6,021 changed (admin_name2 6,017, admin_code2 2,725 now empty, place_name 64) |
Causes, each checked against the inputs:
- The committed parse blanks the municipality for every CEP that is only in the Mendeley list (23,505 live CEPs).
combine_clean_data()moves the Mendeley/ruacep city intostreetNameand blankscityName; the merge keeps the blank becausecombine_firsttreats""as present. The live file has the municipality on all of them. - additionalInfo
CAPITAL/COUNTRYSIDE(520,637 / 712,594 rows in the savedraw.csv) is produced by nothing in the committed code or inputs; the committed parse writes it empty. It is dropped in the 13-column product, so it does not reach the live file. - Municipality spelling. The live
admin_name2equals the IBGE Localidades API name for itsadmin_code2on 1,233,205 of 1,233,231 rows. The legacy parse copies the source's spelling instead ("Santa Bárbara D'Oeste" vs "Santa Bárbara d'Oeste"). run_qa.pycallssys.executablewithout importingsys, so its Frictionless step could not have run as committed.- The old README's claim that the logic was verified SHA256-identical is true only of the saved intermediates, not of a run from upstream.
What the producer does
Rules, per CEP (full list in the produce.py docstring): source is OpenCEP's record, else the first Mendeley row; state from the UF; municipality by IBGE code (OpenCEP carries it; Mendeley rows are placed by UF + folded city name through kelvins, then the IBGE list); admin_name2 is the IBGE API name for the code; place_name is the bairro or the municipality if empty; "A/B" bairros keep "A" and the full name goes to alternative_city_name; control characters are removed from the bairro; ordered by state, municipality, place, CEP. Run in ~35 s from cached inputs.
Inputs (all fetched upstream, data/br.inputs.json records url, time, size, sha256): OpenCEP release 2.0.1 v1.zip (342,302,994 bytes, MIT; it is the only release in the repo, published 2024-07-08), the Mendeley CSV (99,290,030 bytes, CC BY 4.0, last modified 2020-01-03), kelvins municipios.csv (MIT), IBGE Localidades estados and municipios (the API gzips its answers; the producer decompresses). The IBGE DTB report and the ruacep file that the old chain read are not inputs and are removed from the tree (still in git history).
Parity against the live zip (2026-10-12)
| rows | |
|---|---|
| live | 1,233,231 |
| fresh | 1,232,818 |
| only in live | 422 (by the repo's key postal_code, place_name; 413 CEPs are gone, 9 changed their place_name) |
| only in fresh | 9 |
| changed cells | 20 (all admin_name2) |
| deviating rows, by CEP | 442 = 413 + 29, 0.036% of live |
Every other cell, including all 1,232,818 postal_code, admin_name1/code1, admin_code2 and the empty coordinate columns, equals live. max_row_drop is 0.015, the drop is 0.03%.
| Difference | Rows | Cause |
|---|---|---|
| CEP only in live, from the ruacep.com.br scrape | 173 | Mineiros GO 103, Picos PI 35, Ubá MG 15, São Pedro da Aldeia RJ 13, Vinhedo SP 7. In neither OpenCEP nor Mendeley. Licence-driven: see below |
| CEP only in live, in no input at all | 240 | All Extrema MG (37646-xxx / 37647-xxx, the Barreiro district). Not in OpenCEP 2.0.1, Mendeley or ruacep. The live file has 194 CEPs starting 37646 and 129 starting 37647; OpenCEP 2.0.1 has 43 and 40. Consistent with the live build having read an older OpenCEP snapshot for this district, but not provable: only one release exists |
| Extrema rows whose bairro differs | 3 | Live Morro Grande / Barreiro, OpenCEP 2.0.1 now Condomínio Itamaraty / Estância Barreiro: source changed (37647-290, -302, -312) |
| Municipality spelling | 26 | Five municipalities: Arês -> Arez (RN), Açu -> Assú (RN), São Luiz -> São Luiz do Anauá (RR), Barão de Monte Alto -> Barão do Monte Alto (MG), Gracho Cardoso -> Graccho Cardoso (SE). The live file has the older spelling kelvins carries; the producer uses IBGE's current name. 6 of the 26 are also the place_name where the bairro was empty |
| Control character | 0 now | OpenCEP 37644-390 has Á<U+0081>guas; the live file is clean. The producer removes control characters, so this row matches |
Informational: the public sample at postal.datahub.io/br/br.csv is unexplained against the fresh first 100 rows (75 unclassified cells, 14 float_suffix_dropped): it is the old mangled sample.
Verification (2026-10-12)
- Random 100 rows (seed 20261012), each checked against the raw OpenCEP JSON / Mendeley rows / kelvins / IBGE API by a separate script that imports nothing from the producer: 100 of 100 pass (96 OpenCEP, 4 Mendeley). A second 100 drawn only from Mendeley-placed CEPs (the path the legacy chain broke): 100 of 100 pass.
- Municipality coverage: 5,570 municipalities have a row in live and in fresh, the same set. IBGE lists 5,571: the one without a CEP in any source (Boa Esperança do Norte, MT) is not in the product, as before.
scripts/build.py br,build.py --all --check(248 ok), pytest (1,681 passed; 34 are br). Frictionless run inside the unpacked zip (datapackage.yaml+br.csv): VALID.
Licence: the ruacep.com.br scrape
Decision for the owner; nothing here decides it. cep_data.csv (9,324 lines, 9,149 distinct CEPs) came from ruacep.com.br ("todos os direitos reservados", no reuse licence).
- Live rows that depend on it: 173 of 1,233,231 = 0.014%. They exist only because of the scrape.
- 8,951 further live rows have their CEP in the scrape too, but are reproduced identically from OpenCEP and Mendeley (0 differences in any field), so the scrape contributes nothing to them. The "~7,000 rows" in
.licensing-notes.mdand "roughly 7,000" inATTRIBUTION.txtare not what the data shows. - The producer does not use the scrape, and stays inside the 1% bound without it (-173 rows). The price is losing those 173 CEPs; the product loses no municipality.
- The scrape file and the code that read it are deleted from the tree by this change (git history keeps them). The shipped
README.md("a small subset") andATTRIBUTION.txt("roughly 7,000 rows") still carry the caveat; they were left as they are because the owner has not decided whether to keep, reword or drop it, and the live zip still contains the 173 rows until it is republished.
Decisions
| Decision | By | Basis |
|---|---|---|
| Write the producer from the data's own rules instead of porting the legacy chain | Agent | The chain does not reproduce live (2.59% differ); the rules reproduce it to 0.036% |
| Do not use ruacep.com.br | Agent, pending owner | No reuse licence; costs 173 rows |
admin_name2 = IBGE API name by code (26 rows differ from live spelling) | Agent | Matches live on 1,233,205 of 1,233,231 rows; no single source reproduces the other 26 |
| Remove control characters from the bairro | Agent | One row; makes it equal live |
Delete legacy_source_pipeline/, refine_raw_source.py, scrape_admin.py, run_qa.py, build_postal_codes_product.py, scripts/README.md | Agent | Replaced by produce.py; recoverable from history |
Open questions
- Sign-off of the 442 rows (0.036%) as
explained, and of dropping the 173 ruacep rows. - Keep, reword or drop the ruacep caveat in
README.md/ATTRIBUTION.txtonce the 173 rows are gone from the shipped file. - The 240 Extrema CEPs: ask SeuAliado whether an older snapshot exists, or accept the loss.
- Download time and fragility.
fetch_inputsbuffers the 342 MB OpenCEP zip in memory and restarts it on any failure. GitHub's release CDN was very slow on 2026-10-12 (the first download took over an hour); the old parse usedcurl --retry -C -. If it keeps failing on the runner, resumable download belongs inscripts/producers/common.py, not here. - Mendeley's data stops in 2018 and OpenCEP's only release is 2024-07; refresh stays disabled, so nothing will change until someone re-runs it.