CNPJ alfanumérico: o que mudou e como validar no seu sistema
Desde 31 de julho de 2026 a Receita Federal emite CNPJ com letras. O número continua com 14 posições, mas as 12 primeiras podem ter letras maiúsculas, e os dois dígitos verificadores continuam numéricos. Os CNPJs já existentes não mudam. Quem guarda CNPJ como número, valida com uma expressão que só aceita dígitos ou calcula o dígito verificador com parseInt vai rejeitar empresas novas.
Teste um CNPJ agora
O validador abaixo roda só no seu navegador: nada sai desta página. Ele mostra o cálculo dos dígitos verificadores, útil para conferir a sua implementação.
Exemplos:
O que a Receita mudou
A mudança foi instituída pela Instrução Normativa RFB nº 2.229, de 15 de outubro de 2024 (publicada no Diário Oficial da União em 16/10/2024), que alterou a IN RFB nº 2.119/2022.
| Posições | Parte | Antes | Agora |
|---|---|---|---|
| 1 a 8 | Raiz (identifica a empresa) | só dígitos | letras maiúsculas e dígitos |
| 9 a 12 | Ordem (matriz 0001, filiais) | só dígitos | letras maiúsculas e dígitos |
| 13 e 14 | Dígitos verificadores | dígitos | continuam só dígitos |
- Só inscrições novas recebem o formato. Nenhum CNPJ existente foi trocado, e a emissão alfanumérica é gradual.
- A máscara continua a mesma:
12.ABC.345/01DE-35. - Letras são sempre maiúsculas. Normalize a entrada (
12abc...vira12ABC...) antes de validar e de gravar.
Na carga de dados abertos de setembro de 2026, a primeira com CNPJs emitidos depois da mudança, havia 1 estabelecimento com CNPJ alfanumérico entre 73.366.147. A tendência é crescer a cada mês, porque toda abertura nova passa a poder receber letras.
Como calcular o dígito verificador
O algoritmo é o mesmo módulo 11 de sempre. A única novidade é como cada caractere vira número: valor = código ASCII − 48. Para os dígitos nada muda ('0' = 0 … '9' = 9). As letras vão de A = 17 a Z = 42.
| Caractere | 0–9 | A | B | C | D | E | … | Z |
|---|---|---|---|---|---|---|---|---|
| Valor | 0–9 | 17 | 18 | 19 | 20 | 21 | … | 42 |
- Primeiro dígito: multiplique os 12 primeiros valores pelos pesos
5 4 3 2 9 8 7 6 5 4 3 2e some. - Divida a soma por 11. Se o resto for 0 ou 1, o dígito é 0; senão, é 11 − resto.
- Segundo dígito: repita com os 13 valores (os 12 mais o primeiro dígito) e os pesos
6 5 4 3 2 9 8 7 6 5 4 3 2.
Exemplo oficial, 12.ABC.345/01DE:
| 1 | 2 | A | B | C | 3 | 4 | 5 | 0 | 1 | D | E | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Valor | 1 | 2 | 17 | 18 | 19 | 3 | 4 | 5 | 0 | 1 | 20 | 21 |
| Peso | 5 | 4 | 3 | 2 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
| Produto | 5 | 8 | 51 | 36 | 171 | 24 | 28 | 30 | 0 | 4 | 60 | 42 |
- Soma = 459; 459 ÷ 11 deixa resto 8; primeiro dígito = 11 − 8 = 3.
- Com o 3 no fim e os pesos de 6 a 2, a soma é 424; resto 6; segundo dígito = 11 − 6 = 5.
- CNPJ completo: 12.ABC.345/01DE-35.
A mesma conta funciona para os CNPJs antigos, só numéricos: não é preciso manter duas implementações.
Código de validação
Todas as versões abaixo foram executadas com os mesmos casos de teste (numérico válido, alfanumérico válido, com e sem máscara, minúsculas, dígito errado e formato errado) antes de publicar: Node.js 18 e 24, Python 3.12, PHP 8.3 e .NET 8.
const PESOS = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
export function cnpjValido(cnpj) {
const c = String(cnpj).replace(/[^0-9A-Za-z]/g, '').toUpperCase();
if (!/^[A-Z0-9]{12}[0-9]{2}$/.test(c)) return false;
const v = [...c].map((ch) => ch.charCodeAt(0) - 48);
const dv = (n) => {
let soma = 0;
for (let i = 0; i < n; i++) soma += v[i] * PESOS[i + 13 - n];
const resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
};
return v[12] === dv(12) && v[13] === dv(13);
}import re
PESOS = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
def cnpj_valido(cnpj: str) -> bool:
c = re.sub(r"[^0-9A-Za-z]", "", cnpj).upper()
if not re.fullmatch(r"[A-Z0-9]{12}[0-9]{2}", c):
return False
v = [ord(ch) - 48 for ch in c]
def dv(n: int) -> int:
resto = sum(v[i] * PESOS[i + 13 - n] for i in range(n)) % 11
return 0 if resto < 2 else 11 - resto
return v[12] == dv(12) and v[13] == dv(13)function cnpj_valido(string $cnpj): bool
{
$c = strtoupper(preg_replace('/[^0-9A-Za-z]/', '', $cnpj));
if (!preg_match('/^[A-Z0-9]{12}[0-9]{2}$/', $c)) {
return false;
}
$pesos = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
$v = array_map(fn ($ch) => ord($ch) - 48, str_split($c));
$dv = function (int $n) use ($v, $pesos): int {
$soma = 0;
for ($i = 0; $i < $n; $i++) {
$soma += $v[$i] * $pesos[$i + 13 - $n];
}
$resto = $soma % 11;
return $resto < 2 ? 0 : 11 - $resto;
};
return $v[12] === $dv(12) && $v[13] === $dv(13);
}using System.Text.RegularExpressions;
public static partial class Cnpj
{
static readonly int[] Pesos = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
[GeneratedRegex("[^0-9A-Za-z]")] private static partial Regex NaoAlfanumerico();
[GeneratedRegex("^[A-Z0-9]{12}[0-9]{2}$")] private static partial Regex Formato();
public static bool Valido(string cnpj)
{
var c = NaoAlfanumerico().Replace(cnpj, "").ToUpperInvariant();
if (!Formato().IsMatch(c)) return false;
var v = c.Select(ch => ch - 48).ToArray();
int Dv(int n)
{
var soma = 0;
for (var i = 0; i < n; i++) soma += v[i] * Pesos[i + 13 - n];
var resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
return v[12] == Dv(12) && v[13] == Dv(13);
}
}O truque do índice PESOS[i + 13 - n] usa uma só lista de pesos para os dois dígitos: com n = 12 começa no segundo peso (5) e com n = 13 no primeiro (6).
Checklist de adequação
- Banco de dados. Coluna de CNPJ como texto de 14 posições (
CHAR(14)ouVARCHAR(14)), nuncaINTouBIGINT. Migre os dados existentes com zeros à esquerda (00000000000191, não191). - Formulários. Aceite letras no campo, converta para maiúsculas e mantenha a máscara
00.000.000/0000-00com posições alfanuméricas. Teclado numérico no celular deixa de servir. - Validação. Troque expressões como
^\d{14}$por^[A-Z0-9]{12}\d{2}$e use o cálculo acima. - Comparação e ordenação. Compare como texto. Ordenação numérica e conversões para número quebram.
- Planilhas. Excel interpreta coluna numérica e come zeros à esquerda; importe a coluna como texto.
- Integrações. Confira com cada parceiro (bancos, emissores de nota, ERPs) se o campo de CNPJ já aceita letras. Documentos fiscais eletrônicos tiveram notas técnicas próprias de adequação.
- Testes. Inclua o exemplo oficial
12.ABC.345/01DE-35na sua suíte, junto de um CNPJ numérico real e de um com dígito errado.
Como a apipj trata o CNPJ alfanumérico
GET /v1/valida/{cnpj}valida formato e dígitos, numérico ou alfanumérico. É aberto, não exige conta e não consome cota:
curl -s https://apipj.com.br/v1/valida/12.ABC.345%2F01DE-35{"cnpj":"12ABC34501DE35","valido":true,"formatado":"12.ABC.345/01DE-35","alfanumerico":true,"erro":null}GET /v1/cnpj/{cnpj}aceita os dois formatos, com ou sem máscara e em minúsculas, e devolve os dados cadastrais quando o CNPJ está na carga do mês. Veja a API de CNPJ e a documentação.
Perguntas frequentes
Meu CNPJ vai mudar?
Não. CNPJs já emitidos continuam iguais. Só inscrições novas podem receber letras.
Os dígitos verificadores podem ter letras?
Não. As posições 13 e 14 continuam só numéricas, calculadas pelo módulo 11 descrito acima.
Preciso de uma função para o CNPJ antigo e outra para o novo?
Não. Com valor = código ASCII − 48, os dígitos continuam valendo 0 a 9, então a mesma função valida os dois.
Um dígito verificador válido prova que a empresa existe?
Não. Prova só que o número está bem formado. Para saber se a inscrição existe e está ativa, consulte os dados cadastrais.