Python functions for working with CNPJ, CEI, CPF, PIS/PASEP, CEP, and município numbers, which identify firms, people, and places in Brazil.
pip install brazilnum
Requires Python 3.9 or newer.
Validate a CNPJ number for a firm, in this case Telefônica Brasil:
>>> from brazilnum.cnpj import validate_cnpj
>>> validate_cnpj('02.558.157/0001-62')
True
>>> validate_cnpj('02.558.157/0001-55')
False
As of 07/2026, CNPJ can also be issued in a new alphanumeric format:
>>> validate_cnpj('XP.B30.AW3/0001-84')
True
>>> validate_cnpj('xp.b30.aw3/0001-84')
True
Note: because CNPJ can contain letters, stray letters in the input make an identifier invalid as of version 0.9.0:
>>> validate_cnpj('CNPJ: 02.558.157/0001-62')
False
Integer input that is too short due to missing zeros is auto-corrected:
>>> validate_cnpj(2558157000162)
True
>>> validate_cnpj(2558157000162, autopad=False)
False
Missing values (None or NaN) are treated as invalid identifiers, so
validation works on a stream of identifiers where some are missing:
>>> validate_cnpj(None)
False
>>> validate_cnpj(float('nan'))
False
Any other input type (e.g. a float or a list) raises a TypeError, since
that usually signals a problem in the data pipeline that is better
surfaced early than hidden by a False return. The formatting, padding,
and parsing functions raise TypeError for missing values too, because
there is no sensible way to format or parse a missing identifier. This
behavior is shared by all validation functions in the package as of
version 0.10.0.
Validate a CEI number, used for businesses that do not require a CNPJ:
>>> from brazilnum.cei import validate_cei
>>> validate_cei('11.583.00249/85')
True
>>> validate_cei('11.583.00249/84')
False
>>> validate_cei(115830024985)
True
Validate CPF and PIS/PASEP numbers for individuals:
>>> from brazilnum.pis import validate_pis
>>> validate_pis('125.6124.131-0')
True
>>> validate_pis('111.6124.131-0')
False
>>> from brazilnum.cpf import validate_cpf
>>> validate_cpf('968.811.342-58')
True
>>> validate_cpf('327.861.067-97')
False
Validation functions work with integer and unformatted input:
>>> validate_pis(12561241310)
True
>>> validate_cpf(96881134258)
True
>>> validate_pis('12561241310')
True
>>> validate_cpf('32786106797')
False
Use the format function when displaying identifiers:
>>> from brazilnum.cnpj import format_cnpj
>>> format_cnpj('02558157000162')
'02.558.157/0001-62'
>>> format_cnpj('XPB30AW3000184')
'XP.B30.AW3/0001-84'
>>> from brazilnum.cei import format_cei
>>> format_cei('115830024985')
'11.583.00249/85'
>>> from brazilnum.pis import format_pis
>>> format_pis('12561241310')
'125.6124.131-0'
>>> from brazilnum.cpf import format_cpf
>>> format_cpf('96881134258')
'968.811.342-58'
There is a helper function to remove formatting from identifiers; it always returns a string:
>>> from brazilnum.util import clean_id
>>> clean_id('02.558.157/0001-62')
'02558157000162'
>>> clean_id(115830024985)
'115830024985'
If your data source stores identifiers as integers and leading zeros are missing, you can pad and validate them in one step:
>>> from brazilnum.cnpj import pad_cnpj
>>> pad_cnpj(2558157000162, validate=True)
('02558157000162', True)
>>> pad_cnpj(2558157000155, validate=True)
('02558157000155', False)
>>> pad_cnpj('PB3AW3W000133')
'0PB3AW3W000133'
>>> from brazilnum.cei import pad_cei
>>> pad_cei(115830024985, validate=True)
('115830024985', True)
You can skip the validation step:
>>> pad_cnpj(2558157000155, validate=False)
'02558157000155'
Padding works the same way for PIS/PASEP and CPF numbers:
>>> from brazilnum.pis import pad_pis
>>> pad_pis(12561241310, validate=True)
('12561241310', True)
>>> pad_pis(11161241310, validate=True)
('11161241310', False)
>>> from brazilnum.cpf import pad_cpf
>>> pad_cpf(4193675866, validate=True)
('04193675866', True)
>>> pad_cpf(4193675867, validate=True)
('04193675867', False)
The first 8 digits of CNPJs identify a firm, and the following 4 digits
identify a specific business establishment owned by that firm. Headquarters is
often establishment 0001. The cnpj_from_firm_id function will create a
full CNPJ from the first 8 digits and a given establishment number:
>>> from brazilnum.cnpj import cnpj_from_firm_id
>>> cnpj_from_firm_id('02.558.157')
'02558157000162'
>>> cnpj_from_firm_id('02.558.157', establishment='0002')
'02558157000243'
>>> cnpj_from_firm_id('02.558.157', establishment='0002', formatted=True)
'02.558.157/0002-43'
This also works with the alphanumeric CNPJ format:
>>> cnpj_from_firm_id('XPB30AW3')
'XPB30AW3000184'
CNPJ can be parsed into firm, establishment, and check digit components:
>>> from brazilnum.cnpj import parse_cnpj
>>> parse_cnpj('02.558.157/0001-62')
CNPJ(cnpj='02.558.157/0001-62', firm='02.558.157', establishment='0001', check='62', valid=True)
>>> parse_cnpj('02.558.157/0001-62', formatted=False)
CNPJ(cnpj=2558157000162, firm=2558157, establishment=1, check=(6, 2), valid=True)
Alphanumeric CNPJs are parsed the same way. Since letters can't be
represented as Python ints, formatted=False returns strings for the
identifier components; the check digits are always numeric, so they are
returned as integers in both cases:
>>> parse_cnpj('XPB30AW3000184')
CNPJ(cnpj='XP.B30.AW3/0001-84', firm='XP.B30.AW3', establishment='0001', check='84', valid=True)
>>> parse_cnpj('XPB30AW3000184', formatted=False)
CNPJ(cnpj='XPB30AW3000184', firm='XPB30AW3', establishment='0001', check=(8, 4), valid=True)
Note: as alphanumeric CNPJs become more common, a future release may
switch parse_cnpj(..., formatted=False) to returning strings for all
CNPJs, so avoid relying on the integer representation in new code.
Códigos de Endereçamentos Postais (zip codes) can be formatted and parsed:
>>> from brazilnum.cep import format_cep, parse_cep
>>> format_cep(13165000)
'13165-000'
>>> format_cep(1002010)
'01002-010'
>>> format_cep(73080)
'73080-000'
>>> parse_cep('01255-080', numeric=True)
CEP(cep=1255080, region=0, subregion=1, sector=12, subsector=125, division=1255, suffix=80)
>>> parse_cep('01255-080', numeric=False)
CEP(cep='01255-080', region='0', subregion='01', sector='012', subsector='0125', division='01255', suffix='080')
Correios has more information about the structure of CEP.
Validation of IBGE município (municipal) identifiers is also possible:
>>> from brazilnum.muni import validate_muni
>>> validate_muni(3550308) # São Paulo
True
>>> validate_muni(4305871) # Coronel Barros (see note below)
True
Note that 9 true codes do not follow the correct verification pattern. ENCAT has a technical note about this issue. The program correctly handles special codes like Coronel Barros, RS (see above).
If you need a list of municípios with names and coordinates, see poliquin/br-localidades. If you need historical and current codes with names, see paulofreitas/dtb-ibge.
If you need random CNPJ for database testing, use the random_cnpj function,
which can return either unformatted or formatted identifiers:
from brazilnum.cnpj import random_cnpj
random_cnpj() # for a random, formatted CNPJ
random_cnpj(False) # for a random, unformatted CNPJ
random_cnpj(alphanumeric=True) # for a random alphanumeric CNPJ
Use random_cei for random CEI identifiers:
from brazilnum.cei import random_cei
random_cei()
The same thing exists for PIS/PASEP and CPF identifiers:
from brazilnum.pis import random_pis
random_pis()
from brazilnum.cpf import random_cpf
random_cpf()
If you're interested in the check digits, there are functions for calculating them that return integers:
>>> from brazilnum.cnpj import cnpj_check_digits
>>> cnpj_check_digits('02.558.157/0001-62')
(6, 2)
>>> from brazilnum.cei import cei_check_digit
>>> cei_check_digit('11.583.00249/85')
5
>>> from brazilnum.cpf import cpf_check_digits
>>> cpf_check_digits('041.936.758-66')
(6, 6)
>>> from brazilnum.pis import pis_check_digit
>>> pis_check_digit('125.6124.131-0')
0
CNPJ check digits are calculated from the first 12 digits:
>>> cnpj_check_digits('025581570001')
(6, 2)
>>> cnpj_check_digits('XPB30AW30001')
(8, 4)
The CEI check digit is calculated from the first 11 digits:
>>> cei_check_digit('11583002498')
5
CPF check digits are calculated from the first 9 digits:
>>> cpf_check_digits('041936758')
(6, 6)
The PIS/PASEP check digit is calculated from the first 10 digits:
>>> pis_check_digit('1256124131')
0