CEP
Código de Endereçamento Postal, the 8-digit Brazilian postal code managed by Correios.
Validate
- JavaScript library
- Python library10 cases fail
- Go library9 cases fail
- Ruby library10 cases fail
- Rust library9 cases fail
- .NET library1 case fails
- Erlang library10 cases fail
Validates a CEP: exactly 8 digits.
cepmay be a string or a number. A CEP that starts with0must 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:
-20040020and2004002.1are invalid.isValidandgetStateread the number1310100as 7 digits and reject it, whilegetAddressInfoandformatwithpadleft-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.
| Parameter | Type | Required |
|---|---|---|
cep | string | number | yes |
| returns | boolean |
Check if a CEP (brazilian postal code) is valid.
- Accepts a
stringor anumber. A CEP that starts with0has 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.
getAddressInfoByCepandformatCepwithpad: trueare more lenient with numbers: they left-pad a number to 8 digits (1310100is01310-100), whileisValidCepandgetStateByCepread1310100as 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)Try it with JavaScript isValidCep
Shared test cases (30) and the result in each library cep.isValid
Format
- JavaScript library
- Python library13 cases fail
- Go library8 cases fail
- Ruby library13 cases fail
- Rust library10 cases fail
- .NET library2 cases fail
- Erlang library13 cases fail
Formats a CEP as 00000-000.
options.padleft-pads the value with zeros to 8 digits first. Otherwise, a CEP that starts with0and 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 (
010010is01001-0). - An empty value, or one without digits, returns an empty string even with
pad. Until 2.4.0padreturned 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.
| Parameter | Type | Required |
|---|---|---|
value | string | number | yes |
options | FormatCepOptions | no |
options.pad | boolean | no |
| returns | string |
Format a CEP (brazilian postal code).
- Options (
FormatCepOptions):padleft-pads the value with zeros to 8 digits before masking (defaultfalse). An empty value, or one without digits, gives''even withpad. - A CEP that starts with
0given as a number loses that zero: pass a string or usepad. 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)Try it with JavaScript formatCep
Shared test cases (27) and the result in each library cep.format
Parse
- JavaScript library
- Python library
- Go library
- Ruby library2 cases fail
- Rust library
- .NET library
- Erlang library
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.
nulland other values that are neither a string nor a number return an empty string.
| Parameter | Type | Required |
|---|---|---|
value | string | number | yes |
| returns | string |
Remove CEP formatting, keep only digits, and cap the result to 8 digits.
import { parseCep } from '@brazilian-utils/brazilian-utils';
parseCep('92500-000'); // 92500000Try it with JavaScript parseCep
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.getStatenever returnsnullfor it. 00000-000to00999-999and78900-000to78999-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.
| Parameter | Type | Required |
|---|---|---|
| returns | string |
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'Try it with JavaScript generateCep
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
cephas 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, so123was looked up as00000-123. - The result carries
cep(8 digits, no mask),state(two-letter code),city,neighborhoodandstreet.neighborhoodandstreetare empty when the CEP covers a whole city. options.providerspicks which providers to query. The function retries transient network failures per provider.options.timeoutMslimits 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(anAbortSignalin JavaScript) cancels the lookup, and the call fails with the signal's reason, asfetchdoes. 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
1310100for01310-100means the same CEP and is accepted, while1310101is another CEP and is not), and its state, when it names one, must be the state that owns the CEP range (seecep.getState). Otherwise that provider counts as not knowing the CEP. BrasilAPI, for instance, answered99999-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.providersacceptsviacep,brasilapiandwidenet, 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.widenetis deprecated and left out of the default list: its endpoint now redirects tows.apicep.com, which is usually unavailable, so it only adds a failing provider to the race.
| Parameter | Type | Required |
|---|---|---|
cep | string | number | yes |
options | GetAddressInfoByCepOptions | no |
options.providers | CepProvider[] | no |
options.signal | AbortSignal | no |
options.timeoutMs | number | no |
| returns | Promise<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 tows.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 withGetAddressInfoByCepServiceError.signal(AbortSignal) cancels the lookup; the call rejects withsignal.reason, the same asfetch.
- Accepts a string or a number. A string has any non-digit characters stripped (
'CEP 01310-100'is01310100) 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 from1000000(01000-000, the lowest CEP the Correios assign) up. A smaller, negative or fractional number is rejected withGetAddressInfoByCepValidationErrorbefore any request is made. - Retries transient network failures per provider.
- Rejects with
GetAddressInfoByCepValidationErrorwhen the CEP is invalid,providersnames no known provider ortimeoutMsis not a positive finite number, withGetAddressInfoByCepNotFoundErrorwhen every provider failed and at least one reported the CEP as unknown, and withGetAddressInfoByCepServiceErrorwhen 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
1310100for01310-100means the same CEP), must be the CEP, and its state, when it names one, must be the state that owns the CEP range (seegetStateByCep). Otherwise that provider counts as not knowing the CEP. BrasilAPI, for instance, answered99999-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 onecatchcovers 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 });Try it with JavaScript getAddressInfoByCep
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).
paramscarries 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,dddandsiafi. 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.federalUnitis the two-letter state code, for exampleSP. It must be a string and a known UF.params.cityandparams.streetare the city and the street name (or part of it).cityandstreetmust 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
paramsthat is not an object (omitted,null, a string) and afederalUnitthat 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.
| Parameter | Type | Required |
|---|---|---|
params | GetCepInfoByAddressParams | yes |
params.federalUnit | string | yes |
params.city | string | yes |
params.street | string | yes |
| returns | Promise<CepAddressInfo[]> |
Fetch the CEPs of an address from ViaCEP. Resolves to an array of CepAddressInfo.
- The argument (
GetCepInfoByAddressParams) carriesfederalUnit,cityandstreet.federalUnitmay be lowercase;cityandstreetare 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
GetCepInfoByAddressValidationErrorwhen 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), withGetCepInfoByAddressNotFoundErrorwhen no address matches, and withGetCepInfoByAddressErrorwhen ViaCEP answers with an HTTP error status. - Retries transient network failures, as
getAddressInfoByCepdoes. - 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'
// }
// ]Try it with JavaScript getCepInfoByAddress
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.
valueis read ascep.isValidreads it: 8 digits, as a string or a number, with spaces, dots, hyphens and slashes ignored.- A number must be a non-negative integer:
-20040020and2004002.5returnnull. A CEP that starts with0must be passed as a string. - The result is the same object as
state.getByIbgeCodeandstate.listreturn (a new copy each call). - Returns
nullfor 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-000to00999-999, and78900-000to78999-999. In the second one, MT ends at78899-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-000returns SP although no city uses10xxx. - The table is the answer of the Correios "Busca Faixa de CEP" search when only the state is given.
| UF | CEP |
|---|---|
| SP | 01000-000 to 19999-999 |
| RJ | 20000-000 to 28999-999 |
| ES | 29000-000 to 29999-999 |
| MG | 30000-000 to 39999-999 |
| BA | 40000-000 to 48999-999 |
| SE | 49000-000 to 49999-999 |
| PE | 50000-000 to 56999-999 |
| AL | 57000-000 to 57999-999 |
| PB | 58000-000 to 58999-999 |
| RN | 59000-000 to 59999-999 |
| CE | 60000-000 to 63999-999 |
| PI | 64000-000 to 64999-999 |
| MA | 65000-000 to 65999-999 |
| PA | 66000-000 to 68899-999 |
| AP | 68900-000 to 68999-999 |
| AM | 69000-000 to 69299-999 and 69400-000 to 69899-999 |
| RR | 69300-000 to 69399-999 |
| AC | 69900-000 to 69999-999 |
| DF | 70000-000 to 72799-999 and 73000-000 to 73699-999 |
| GO | 72800-000 to 72999-999 and 73700-000 to 76799-999 |
| RO | 76800-000 to 76999-999 |
| TO | 77000-000 to 77999-999 |
| MT | 78000-000 to 78899-999 |
| MS | 79000-000 to 79999-999 |
| PR | 80000-000 to 87999-999 |
| SC | 88000-000 to 89999-999 |
| RS | 90000-000 to 99999-999 |
| Parameter | Type | Required |
|---|---|---|
value | string | number | yes |
| returns | State | 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
isValidCepaccepts: 8 digits, as a string or a number, with spaces, dots, hyphens and slashes ignored. A CEP that starts with0has to be a string, and a negative or fractional number is rejected.getAddressInfoByCepandformatCepwithpad: trueleft-pad numbers instead (1310100is01310-100). - Amazonas, Distrito Federal and Goiás have two ranges each, and no state range covers
00000-000to00999-999nor78900-000to78999-999. - A range is the block the state owns, not a promise that every CEP in it is in use:
10000-000sits unused inside the São Paulo range and still answers São Paulo. - Returns
nullfor an invalid CEP or one outside every range. Exports theStatetype.
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'); // nullSource: Correios, Busca Faixa de CEP
Code: brazilian-utils/javascriptTry it with JavaScript getStateByCep
Shared test cases (39) and the result in each library cep.getState
Guides
- Address from a CEPA form that looks the CEP up and fills the street, neighborhood, city and state, with Brazilian Utils in React, Angular, Vue and plain JavaScript.
- Document fieldA field that masks and validates a CPF, CNPJ, CEP or phone number as you type, with Brazilian Utils in React, Angular, Vue and plain JavaScript.
- Schema librariesThe validators of Brazilian Utils inside a Zod, Valibot or ArkType schema, or as a Standard Schema of their own.
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
- The input must contain exactly
8digits.
Algorithm
- Check that the input contains exactly
8characters. - Check that all characters are digits.
- 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-000falls in the SP block although no city uses10xxx. - Two blocks belong to no state:
00000-000to00999-999and78900-000to78999-999. MT ends at78899-999. - AM, DF and GO have two blocks each.
72800-000to72999-999, between the two DF blocks, belongs to GO. - The full table is in the description of
cep.getState. cep.generatedraws 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 exactly8characters) - Invalid:
123456789(must contain exactly8characters) - Invalid:
abcdefgh(must contain only digits)
Official sources
- Lei nº 6.538 de 22 de junho de 1978
- Tudo sobre CEP, Correios
- Guia de Endereçamento, Correios
- Busca Faixa de CEP, Correios
- Localidades alvo, Correios
- github.com/BrasilAPI/BrasilAPI/…/[cep].js
- gist.github.com/tamnil/792a6a66f6df9fc028041587cfca0c3d
See also States (UF), Municipalities
Last updated on
