1001Ferramentas
๐Ÿ™๏ธValidators

IBGE Municipality Code Validator

Validate IBGE municipality code format (7 digits: UF + 5 inner). Checks if first 2 are a valid UF.

โ€”

IBGE municipality code validator: algorithm, check digit and three levels of validation

This page focuses specifically on the validation algorithm of the 7-digit IBGE municipality code โ€” how to confirm that a given string can be a valid IBGE code without hitting an external API, and how to escalate to semantic and live checks when needed. The full taxonomy of municipalities, UF prefixes and use cases is covered in our sibling tool; here we drill into the math and the engineering trade-offs.

The IBGE code identifies each of the 5,570 Brazilian municipalities (2022 Census) plus the Distrito Federal. Its structure is fixed: 7 numeric digits, where the first 2 are the UF prefix, digits 3 to 6 are the sequential micro-code and the 7th is a check digit (DV) historically computed by IBGE itself.

Validation level 1: format check (regex)

The cheapest check โ€” purely structural, no network, no table. Reject anything that is not exactly seven digits with a UF prefix in the allowed set:

const FORMAT = /^\d{7}$/;
const VALID_UF = new Set([
  '11','12','13','14','15','16','17',
  '21','22','23','24','25','26','27','28','29',
  '31','32','33','35',
  '41','42','43',
  '50','51','52','53'
]);
function isFormatValid(code) {
  if (!FORMAT.test(code)) return false;
  return VALID_UF.has(code.slice(0, 2));
}

This catches typos like 6-digit codes (CEP confused with IBGE), leading zeros stripped by JSON serialization and invalid UF prefixes like 34 or 44 (which do not exist โ€” the numbering left deliberate gaps).

Validation level 2: check-digit (DV) verification

The 7th digit is a verification digit computed from the first six. The historical IBGE DV algorithm uses Luhn-style weighting:

function ibgeCheckDigit(seis) {
  // seis = first 6 digits as string
  const weights = [1, 2, 1, 2, 1, 2];
  let sum = 0;
  for (let i = 0; i < 6; i++) {
    let prod = parseInt(seis[i], 10) * weights[i];
    if (prod > 9) prod = Math.floor(prod / 10) + (prod % 10);
    sum += prod;
  }
  const dv = (10 - (sum % 10)) % 10;
  return dv;
}
function isDVValid(code) {
  if (code.length !== 7) return false;
  const expected = ibgeCheckDigit(code.slice(0, 6));
  return expected === parseInt(code[6], 10);
}

Example with Sao Paulo (3550308): first 6 = 355030, products = 3,10,5,0,3,0, summing digits of any >9 (10 โ†’ 1), sum = 3+1+5+0+3+0 = 12, dv = (10 - 12 % 10) % 10 = 8. Confirmed.

Validation level 3: existence (IBGE API)

Even a format-valid + DV-valid code can refer to a municipality that was never created. The authoritative check is a live call to the IBGE REST API:

async function existsInIBGE(code) {
  const r = await fetch(
    `https://servicodados.ibge.gov.br/api/v1/localidades/municipios/${code}`
  );
  if (r.status === 200) {
    const j = await r.json();
    return { ok: true, nome: j.nome, uf: j.microrregiao.mesorregiao.UF.sigla };
  }
  return { ok: false };
}

The endpoint is free, returns JSON, requires no API key and is rate-limited to a few thousand requests per hour per IP. BrasilAPI wraps the same data at brasilapi.com.br/api/ibge/municipios/v1/{UF} with extra caching and CORS open by default โ€” preferred for browser-side calls in checkouts.

Mandatory uses where validation matters

  • NF-e: field cMun (operation municipality), cMunFG (taxable event), cMunDescarga (unloading) โ€” Sefaz rejects the XML if any IBGE code is missing or invalid.
  • eSocial: layout S-1010 (Estabelecimento) and S-2200 (Worker Admission) both require codMunic.
  • IRRF, FGTS, RAIS, CAGED, DIRF: all federal labor-and-tax reports use IBGE code for geo segmentation.
  • SUS DATASUS: hospital, procedure and epidemiology datasets keyed by IBGE code.
  • Georeference: joining IBGE code with state/region tables for census data, MapBiomas and INPE deforestation alerts.

Anti-patterns and pegadinhas

  • Hardcoded list goes stale: new municipalities are rare but happen (Mojui dos Campos/PA in 2013, Pinto Bandeira/RS in 2013). A hardcoded JSON shipped two years ago will miss them โ€” prefer the API or refresh the embedded list on every release.
  • Old NPM libs: many libraries (brasileiro, br-cidades) were last updated in 2018-2020 and miss recent municipalities. Audit before shipping.
  • Leading zeros: codes from Acre (12xxxxx) and Amazonas (13xxxxx) often arrive without leading character padding from spreadsheets โ€” always treat as string.
  • Confusion with CEP: CEP has 8 digits and changes per street; IBGE has 7 digits and is per municipality. Never use one to derive the other without a proper crosswalk table.
  • Distrito Federal exception: DF has a single IBGE code (5300108) covering all of Brasilia โ€” there are no sub-municipal codes for administrative regions.

FAQ

Is the IBGE API really free? Yes โ€” the endpoint at servicodados.ibge.gov.br is operated by IBGE itself, requires no registration or API key, and is rate-limited to a few thousand requests per hour per IP. For higher volumes, mirror the table locally and refresh weekly.

Is the check digit mandatory? Yes โ€” the 7th digit is part of the code. Most validators accept codes without DV computation and rely on table lookup only, but the algorithmic DV check is a useful pre-filter to reject obvious typos before any network call.

Does the code change over time? No. Once assigned, the IBGE code is vitalicio for that municipality. Even if the city is renamed, dismembered or merged, the original code never gets reassigned to another municipality โ€” guaranteeing referential integrity in historical datasets back to the 1970s.

Can I validate offline? Yes โ€” combining the regex (level 1) and the DV algorithm (level 2) catches more than 95% of malformed codes without any network call. Only true existence requires the API or a local copy of the IBGE table (a 5,570-row CSV ~120 KB).

Related Tools