@br-geo-kit/ibge
v1.0.0
Published
Brazilian states, municipalities, IBGE codes, area codes and CEP ranges as a typed offline dataset
Maintainers
Readme
@br-geo-kit/ibge
Brazilian states, municipalities, IBGE codes, geographic regions, area codes and CEP blocks — offline, versioned, and traceable to the source.
pnpm add @br-geo-kit/ibgeimport { ibge } from '@br-geo-kit/ibge'
ibge.municipalities('SP') // 645 records, for a dropdown
ibge.municipality('3550308') // { name: 'São Paulo', ddd: '11', ... }
ibge.search('santa rita', { uf: 'PB' })
ibge.stateOfCep('01310-100') // 'SP' — a range lookup, not a request
ibge.stateOfDdd('11') // 'SP'
ibge.capitalOf('TO') // PalmasNo network, no API key, no rate limit. Most of what applications call the IBGE's API for at runtime is a table that changes about once a decade.
What is in it
| Table | Records | | --- | --- | | States | 27 | | Municipalities | 5 571 | | Geographic regions (immediate + intermediate) | 643 | | Area codes | 67 | | CEP blocks | 30 |
Generated by a pipeline that downloads from the IBGE's Localidades API, validates, and emits deterministic JSON. Every version carries its source, its checksums, and what it does not know.
Three levels of "is this CEP valid", each cheaper than the next
isWellFormedCep('01310100') // shape only. No data, no I/O.
ibge.isCepPlausible('01310100') // falls in a block allocated to a state
await cep.lookup('01310100') // exists, and here it isstateOfCep is a binary search over 30 ranges. It rejects a CEP in an
unallocated block before you spend a request on it, and it tells a form
which state to preselect while the lookup is still in flight.
Two blocks belong to nobody: everything below 01000-000, and the whole
of 789xxxxx — Rondônia's original block, vacated when the state was
renumbered into 768–769.
It is a range lookup, not an existence check. 19999999 is inside São
Paulo's block and is assigned to nobody.
What it admits it does not know
136 of 5 571 municipalities have no area code — 97.56% covered.
The IBGE does not publish area codes; they are joined by exact normalized name within a state. The misses are almost all municipalities renamed since the source list was compiled. A nearest-name match would fill most of them and would occasionally be wrong, silently, on a field applications use to build phone numbers.
ibge.metadata.coverage['municipalities.ddd'] // 136Three states have no single time zone. AM, PA and PE each span two, and resolving which side a municipality falls on needs geometry this dataset does not carry.
ibge.timezoneOf('SP') // 'America/Sao_Paulo'
ibge.timezoneOf('PE') // null
ibge.timezonesOf('PE') // ['America/Recife', 'America/Noronha']Declaring a gap is the point. A dataset that quietly ships one is indistinguishable from a complete one, until somebody's form breaks.
Provenance
ibge.metadata.version // '2026'
ibge.metadata.source // 'IBGE — API de Localidades; ...'
ibge.metadata.dataChecksum // reproducible across runsPin a version when reproducibility matters:
const dataset = ibge.load('2026')Asking for a version the installed package does not ship throws rather than falling back to the newest.
In a browser
The default entry reads from disk and is Node-only. In a browser, supply the data yourself:
import { createIbge, IBGE_TABLE_NAMES } from '@br-geo-kit/ibge/web'
const ibge = createIbge({ metadata, tables })The whole dataset is 730 KB raw, about 80 KB gzipped, and the raw files
are published under @br-geo-kit/ibge/data/* so a build step can put
them on your CDN. The same validation and freezing run on both paths.
See browser and React.
Frozen
Every table is deep-frozen on load. The dataset is a process-wide
singleton, so without it one consumer calling .sort() corrupts it for
every other. To sort, copy first:
const ordered = [...ibge.municipalities('SP')].sort(byName)Licence
MIT for the code.
The territorial data comes from the IBGE, a Brazilian federal agency, and is public; it is redistributed here unmodified in content and with attribution on every dataset version. The IBGE publishes no SPDX licence identifier for this endpoint, so this package does not assert one — if your compliance process needs a definite answer, get it from the IBGE.
Area codes are joined from BrasilAPI (MIT). The CEP block allocation is transcribed from the Correios' published Guia Postal; the DNE street database itself is not redistributed.
Full provenance, and what is and is not verified, in docs/licensing.md.
