CEP

Código de Endereçamento Postal, the 8-digit Brazilian postal code managed by Correios.

  • Parity matrix

Validate

Validates a CEP: exactly 8 digits.

  • cep may be a string or a number. A CEP that starts with 0 must be a string.
  • Spaces, dots, hyphens and slashes are ignored, wherever they appear. Any other character makes the value invalid.
  • A number is read only when it is a safe non-negative integer: -20040020 and 2004002.1 are invalid. isValid and getState read the number 1310100 as 7 digits and reject it, while getAddressInfo and format with pad left-pad it.

Pending decision

The reference (JS) ignores spaces, dots and hyphens (01310-200 is valid). The other libraries accept digits only. See the open decision in docs/findings.md.

ParameterTypeRequired
cepstring | numberyes
returnsboolean

Check if a CEP (brazilian postal code) is valid.

  • Accepts a string or a number. A CEP that starts with 0 has to be a string, since a number cannot keep the leading zero, and a number is only read when it is a non-negative safe integer.
  • Spaces, dots, hyphens and slashes are ignored. Any other character makes the value invalid.
  • getAddressInfoByCep and formatCep with pad: true are more lenient with numbers: they left-pad a number to 8 digits (1310100 is 01310-100), while isValidCep and getStateByCep read 1310100 as 7 digits and reject it.
import { isValidCep } from '@brazilian-utils/brazilian-utils';

isValidCep('01310100'); // true
isValidCep('92500-000'); // true (hyphen between groups)
isValidCep('92.500-000'); // true (dot and hyphen)
isValidCep('013 10 100'); // true (spaces anywhere between the digits)
isValidCep(20040020); // true (number input)
isValidCep(-20040020); // false (not a non-negative safe integer)
isValidCep('9250000A'); // false (letters are rejected)
isValidCep('12345'); // false (invalid length)
Code: brazilian-utils/javascript
Try it with JavaScript isValidCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (30) and the result in each library cep.isValid

Format

Formats a CEP as 00000-000.

  • options.pad left-pads the value with zeros to 8 digits first. Otherwise, a CEP that starts with 0 and comes as a number loses that zero.
  • Every character that is not a digit is removed, and digits past the 8th are dropped. An incomplete value is masked only as far as it goes (010010 is 01001-0).
  • An empty value, or one without digits, returns an empty string even with pad. Until 2.4.0 pad returned the whole zero mask (00000-000).
  • A number is read only when it is a safe non-negative integer. Any other number (negative, fractional, non-finite) returns an empty string, with or without pad.

Pending decision

The reference (JS) formats only the characters an incomplete value has. It returns an empty string for empty or invalid input. The other libraries return null. See the open decision in docs/findings.md.

ParameterTypeRequired
valuestring | numberyes
optionsFormatCepOptionsno
options.padbooleanno
returnsstring

Format a CEP (brazilian postal code).

  • Options (FormatCepOptions): pad left-pads the value with zeros to 8 digits before masking (default false). An empty value, or one without digits, gives '' even with pad.
  • A CEP that starts with 0 given as a number loses that zero: pass a string or use pad. A number is only read when it is a non-negative safe integer; any other number returns ''.
import { formatCep } from '@brazilian-utils/brazilian-utils';

formatCep('92500000'); // 92500-000
formatCep('9250000', { pad: true }); // 09250-000
formatCep(-92500000); // '' (not a non-negative safe integer)
Code: brazilian-utils/javascript
Try it with JavaScript formatCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (27) and the result in each library cep.format

Parse

Removes CEP formatting and keeps only digits, capped at 8 digits.

  • Non-digit characters are removed, and the result is capped at 8 digits.
  • A number is read only when it is a safe non-negative integer. Any other number (negative, fractional, non-finite, past the safe range) returns an empty string. null and other values that are neither a string nor a number return an empty string.
ParameterTypeRequired
valuestring | numberyes
returnsstring

Remove CEP formatting, keep only digits, and cap the result to 8 digits.

import { parseCep } from '@brazilian-utils/brazilian-utils';

parseCep('92500-000'); // 92500000
Code: brazilian-utils/javascript
Try it with JavaScript parseCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (20) and the result in each library cep.parse

Generate

Generates a random CEP: 8 digits, unformatted.

  • A CEP has no check digit, so every 8-digit string is structurally valid.
  • The CEP is drawn inside the ranges the Correios assign to the states, each CEP with the same chance. It always belongs to a state, so cep.getState never returns null for it.
  • 00000-000 to 00999-999 and 78900-000 to 78999-999, which no state owns, are never generated. Until 2.4.0 any 8-digit string could come out, about 1 in 90 of them in one of those two ranges.
  • A range is the block a state owns, not a promise that every CEP in it is in use, so the CEP generated may not be the CEP of a real address.
ParameterTypeRequired
returnsstring

Generate a random CEP. A CEP has no check digit, so the CEP is drawn inside the ranges the Correios assign to the states, each CEP with the same chance. It always belongs to a state, so getStateByCep never answers null for it; 00000-000 to 00999-999 and 78900-000 to 78999-999, which no state owns, are never generated. A range is the block a state owns, not a promise that every CEP in it is in use, so the CEP may not be the CEP of a real address.

import { generateCep } from '@brazilian-utils/brazilian-utils';

generateCep(); // '92500000'
Code: brazilian-utils/javascript
Try it with JavaScript generateCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (1) and the result in each library cep.generate

Get address info

Fetches the address of a CEP from several providers at once and returns the first successful answer (network call).

  • A string cep has every character that is not a digit removed ("CEP 01310-100" works). Exactly 8 digits must remain.
  • A number must be a safe non-negative integer. It is left-padded with zeros to 8 digits only from 1000000 (01000-000, the lowest CEP the Correios assign). A smaller number is rejected. 2.4.0 padded any number, so 123 was looked up as 00000-123.
  • The result carries cep (8 digits, no mask), state (two-letter code), city, neighborhood and street. neighborhood and street are empty when the CEP covers a whole city.
  • options.providers picks which providers to query. The function retries transient network failures per provider.
  • options.timeoutMs limits the whole lookup, retries included. When it runs out, the call fails with a service error. It must be a positive finite number.
  • options.signal (an AbortSignal in JavaScript) cancels the lookup, and the call fails with the signal's reason, as fetch does. A signal that is already aborted fails before any request. Without these options the lookup has no time limit.
  • Fails with an error. The error tells apart an invalid CEP or option, a CEP that no provider knows, and a service failure.
  • A provider being down is not "not found". BrasilAPI answers 404 both for an unknown CEP and when its own backends fail. Its 404 counts as not found only when no other provider failed to answer (a network error or an HTTP error status). Next to such a failure, the call fails with a service error. A lone BrasilAPI 404, or a BrasilAPI 404 together with ViaCEP's erro: true, is still not found. 2.4.0 let the 404 win, so an outage could be reported as an unknown CEP.
  • An address is only accepted when it agrees with the CEP asked for: its digits, left-padded with zeros to 8 digits, must be the CEP (a provider that answers 1310100 for 01310-100 means the same CEP and is accepted, while 1310101 is another CEP and is not), and its state, when it names one, must be the state that owns the CEP range (see cep.getState). Otherwise that provider counts as not knowing the CEP. BrasilAPI, for instance, answered 99999-999, a Rio Grande do Sul CEP, with a city of Paraná. Until 2.4.0 such an answer was returned.
  • Once the lookup settles, the requests of the providers that lost the race are aborted.
  • options.providers accepts viacep, brasilapi and widenet, raced in the order given (default ["viacep", "brasilapi"]). A name that is not a known provider is ignored. A list with no known provider is an invalid option. widenet is deprecated and left out of the default list: its endpoint now redirects to ws.apicep.com, which is usually unavailable, so it only adds a failing provider to the race.
This function calls an external service.
ParameterTypeRequired
cepstring | numberyes
optionsGetAddressInfoByCepOptionsno
options.providersCepProvider[]no
options.signalAbortSignalno
options.timeoutMsnumberno
returnsPromise<AddressInfo>

Fetch the address of a CEP from several providers at once and resolve to the first successful answer. The result is an AddressInfo: cep, state, city, neighborhood and street.

  • Options (GetAddressInfoByCepOptions):
    • providers (CepProvider[]) lists the providers to race (default ['viacep', 'brasilapi']). 'widenet' is deprecated, left out of the default list and usually unavailable: its endpoint now redirects to ws.apicep.com, which answered 502 when last checked, so it only adds a failing provider to the race.
    • timeoutMs (number) bounds the whole lookup, retries included (default: no limit). When it runs out, every request is aborted and the call rejects with GetAddressInfoByCepServiceError.
    • signal (AbortSignal) cancels the lookup; the call rejects with signal.reason, the same as fetch.
  • Accepts a string or a number. A string has any non-digit characters stripped ('CEP 01310-100' is 01310100) and has to leave 8 digits. A number is left-padded with zeros to 8 digits, since it cannot carry the leading zero of a São Paulo CEP, but only from 1000000 (01000-000, the lowest CEP the Correios assign) up. A smaller, negative or fractional number is rejected with GetAddressInfoByCepValidationError before any request is made.
  • Retries transient network failures per provider.
  • Rejects with GetAddressInfoByCepValidationError when the CEP is invalid, providers names no known provider or timeoutMs is not a positive finite number, with GetAddressInfoByCepNotFoundError when every provider failed and at least one reported the CEP as unknown, and with GetAddressInfoByCepServiceError when every provider failed for another reason.
  • BrasilAPI answers 404 both for an unknown CEP and when the services behind it are down, so its 404 only counts as "unknown CEP" when no other provider failed to answer.
  • An address is only accepted when it agrees with the CEP asked for: its digits, left-padded with zeros to 8 (a provider that answers 1310100 for 01310-100 means the same CEP), must be the CEP, and its state, when it names one, must be the state that owns the CEP range (see getStateByCep). Otherwise that provider counts as not knowing the CEP. BrasilAPI, for instance, answered 99999-999, a Rio Grande do Sul CEP, with a city of Paraná.
  • Once the lookup settles, the requests of the providers that lost the race are aborted.
  • All three extend GetAddressInfoByCepError, so one catch covers them.
import { getAddressInfoByCep, GetAddressInfoByCepNotFoundError } from '@brazilian-utils/brazilian-utils';

// Using the default providers (['viacep', 'brasilapi'])
const address = await getAddressInfoByCep('01310100');
// { cep: '01310100', state: 'SP', city: 'São Paulo', neighborhood: 'Bela Vista', street: 'Avenida Paulista' }

// Using a specific provider, and telling an unknown CEP from a failure
try {
  await getAddressInfoByCep('01310-100', { providers: ['brasilapi'] });
} catch (error) {
  if (error instanceof GetAddressInfoByCepNotFoundError) {
    // no provider knows the CEP
  }
}

// Using number input (will be padded automatically)
const addressFromNumber = await getAddressInfoByCep(1310100);

// Giving up after 5 seconds
const addressWithinFiveSeconds = await getAddressInfoByCep('01310100', { timeoutMs: 5000 });
Code: brazilian-utils/javascript
Try it with JavaScript getAddressInfoByCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (0) and the result in each library cep.getAddressInfo

This function has no shared test cases yet.

Get info by address

Looks up every CEP of a street on the ViaCEP service (network call).

  • params carries the state (federative unit, read ignoring case and surrounding whitespace), the city and the street. Before the query, the function trims the city and the street and removes their accents.
  • Each result is the ViaCEP record unchanged, under ViaCEP's own field names: cep (masked, 00000-000), logradouro, complemento, unidade, bairro, localidade, uf, estado, regiao, ibge, gia, ddd and siafi. A field the service adds later is passed through too.
  • The function retries transient network failures.
  • Fails with an error. The error tells apart a missing or invalid state/city/street, an address with no match, and an HTTP error from the service.
  • A request that cannot be performed at all (no connection, for example) fails with the underlying network error, not with one of these errors.
  • params.federalUnit is the two-letter state code, for example SP. It must be a string and a known UF. params.city and params.street are the city and the street name (or part of it).
  • city and street must be strings with at least 3 characters once trimmed and stripped of accents, the minimum ViaCEP accepts. A blank value, a value that is not a string, or one under 3 characters fails with the validation error before any request.
  • A params that is not an object (omitted, null, a string) and a federalUnit that is not a string fail with the validation error too.
  • ViaCEP caps the list at 50 addresses, so a short street name that matches more streets returns only the first 50.
This function calls an external service.
ParameterTypeRequired
paramsGetCepInfoByAddressParamsyes
params.federalUnitstringyes
params.citystringyes
params.streetstringyes
returnsPromise<CepAddressInfo[]>

Fetch the CEPs of an address from ViaCEP. Resolves to an array of CepAddressInfo.

  • The argument (GetCepInfoByAddressParams) carries federalUnit, city and street. federalUnit may be lowercase; city and street are trimmed and stripped of accents before the query, and each must be a string of at least 3 characters after that, the minimum ViaCEP accepts.
  • Rejects with GetCepInfoByAddressValidationError when the UF, city or street is missing or invalid (a blank value, a value that is not a string, or a city or street under 3 characters, all rejected before any request), with GetCepInfoByAddressNotFoundError when no address matches, and with GetCepInfoByAddressError when ViaCEP answers with an HTTP error status.
  • Retries transient network failures, as getAddressInfoByCep does.
  • Each item carries the ViaCEP payload unchanged, under ViaCEP's own field names.
  • ViaCEP caps the list at 50 addresses, so a short street name that matches more streets returns only the first 50.
import { getCepInfoByAddress } from '@brazilian-utils/brazilian-utils';

const ceps = await getCepInfoByAddress({
  federalUnit: 'MG',
  city: 'Ouro Preto',
  street: 'Rua Direita'
});

// [
//   {
//     cep: '35411-152',
//     logradouro: 'Rua Direita',
//     complemento: '',
//     unidade: '',
//     bairro: 'Riacho (Amarantina)',
//     localidade: 'Ouro Preto',
//     uf: 'MG',
//     estado: 'Minas Gerais',
//     regiao: 'Sudeste',
//     ibge: '3146107',
//     gia: '',
//     ddd: '31',
//     siafi: '4921'
//   }
// ]
Code: brazilian-utils/javascript
Try it with JavaScript getCepInfoByAddress
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (0) and the result in each library cep.getInfoByAddress

This function has no shared test cases yet.

Get state

Returns the state that owns the CEP range a CEP falls in. It runs offline: the answer comes from a table of ranges, not from a network call.

  • value is read as cep.isValid reads it: 8 digits, as a string or a number, with spaces, dots, hyphens and slashes ignored.
  • A number must be a non-negative integer: -20040020 and 2004002.5 return null. A CEP that starts with 0 must be passed as a string.
  • The result is the same object as state.getByIbgeCode and state.list return (a new copy each call).
  • Returns null for an invalid CEP, a value that is neither a string nor a number, and a CEP outside every range.
  • Two blocks belong to no state: 00000-000 to 00999-999, and 78900-000 to 78999-999. In the second one, MT ends at 78899-999.
  • A range is the block assigned to a state. It does not mean every CEP inside it is in use. SP is one range, so 10000-000 returns SP although no city uses 10xxx.
  • The table is the answer of the Correios "Busca Faixa de CEP" search when only the state is given.
UFCEP
SP01000-000 to 19999-999
RJ20000-000 to 28999-999
ES29000-000 to 29999-999
MG30000-000 to 39999-999
BA40000-000 to 48999-999
SE49000-000 to 49999-999
PE50000-000 to 56999-999
AL57000-000 to 57999-999
PB58000-000 to 58999-999
RN59000-000 to 59999-999
CE60000-000 to 63999-999
PI64000-000 to 64999-999
MA65000-000 to 65999-999
PA66000-000 to 68899-999
AP68900-000 to 68999-999
AM69000-000 to 69299-999 and 69400-000 to 69899-999
RR69300-000 to 69399-999
AC69900-000 to 69999-999
DF70000-000 to 72799-999 and 73000-000 to 73699-999
GO72800-000 to 72999-999 and 73700-000 to 76799-999
RO76800-000 to 76999-999
TO77000-000 to 77999-999
MT78000-000 to 78899-999
MS79000-000 to 79999-999
PR80000-000 to 87999-999
SC88000-000 to 89999-999
RS90000-000 to 99999-999
ParameterTypeRequired
valuestring | numberyes
returnsState | null

Get the Brazilian state a CEP belongs to, from the CEP ranges the Correios assign to each state (the "Faixa de CEP" of each UF).

  • It runs offline: no CEP API is called, so the answer says which state owns the range, not whether the CEP is in use.
  • Accepts what isValidCep accepts: 8 digits, as a string or a number, with spaces, dots, hyphens and slashes ignored. A CEP that starts with 0 has to be a string, and a negative or fractional number is rejected. getAddressInfoByCep and formatCep with pad: true left-pad numbers instead (1310100 is 01310-100).
  • Amazonas, Distrito Federal and Goiás have two ranges each, and no state range covers 00000-000 to 00999-999 nor 78900-000 to 78999-999.
  • A range is the block the state owns, not a promise that every CEP in it is in use: 10000-000 sits unused inside the São Paulo range and still answers São Paulo.
  • Returns null for an invalid CEP or one outside every range. Exports the State type.
import { getStateByCep } from '@brazilian-utils/brazilian-utils';

getStateByCep('01310-100');
// { code: 'SP', name: 'São Paulo', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 35 }

getStateByCep(20040020);
// { code: 'RJ', name: 'Rio de Janeiro', regionCode: 'SE', regionName: 'Sudeste', ibgeCode: 33 }

getStateByCep('69300-000')?.code; // 'RR'
getStateByCep('72800-000')?.code; // 'GO'
getStateByCep('00999-999'); // null
getStateByCep('12345'); // null

Source: Correios, Busca Faixa de CEP

Code: brazilian-utils/javascript
Try it with JavaScript getStateByCep
The inputs start with the first shared case. Change one to see the new result.

Runs @brazilian-utils/brazilian-utils 2.5.0 in your browser.

Shared test cases (39) and the result in each library cep.getState

Guides

Specification

Summary

The CEP is a numeric code of eight digits. The postal service assigns these codes to localities, streets, postal units, services, public agencies, companies and buildings. The codes guide and speed up the routing, processing and delivery of mail items.

Validation rules

  1. The input must contain exactly 8 digits.

Algorithm

  1. Check that the input contains exactly 8 characters.
  2. Check that all characters are digits.
  3. If both conditions are true, return valid. If not, return invalid.

Regex

  • Unformatted CEP: ^\d{8}$
  • Formatted CEP: ^\d{5}-\d{3}$

State ranges

The Correios assign each state one or more blocks of CEPs. cep.getState reads the state from these blocks, offline.

  • A block belongs to a state, but not every CEP inside it is in use. 10000-000 falls in the SP block although no city uses 10xxx.
  • Two blocks belong to no state: 00000-000 to 00999-999 and 78900-000 to 78999-999. MT ends at 78899-999.
  • AM, DF and GO have two blocks each. 72800-000 to 72999-999, between the two DF blocks, belongs to GO.
  • The full table is in the description of cep.getState.
  • cep.generate draws only inside these blocks, so the CEP it generates always belongs to a state.

Examples

  • Valid: 01310200
  • 01310-200: pending decision. The reference (JS) accepts it, the other libraries do not.
  • Invalid: 12345 (must contain exactly 8 characters)
  • Invalid: 123456789 (must contain exactly 8 characters)
  • Invalid: abcdefgh (must contain only digits)

Official sources

See also States (UF), Municipalities

Last updated on

On this page