Guide · Node.js · SDK · REST API

CNPJ Validation in Node.js

The Brazilian business registry number — 14 digits, two weighted MOD-11 check digits, and an optional company lookup via BrasilAPI. Here's how to validate it correctly.

1. What is a CNPJ?

CNPJ (Cadastro Nacional da Pessoa Jurídica) is the Brazilian National Registry of Legal Entities. It is the mandatory identification number for every company, association, foundation, or any other legal entity operating in Brazil. The CNPJ is issued and managed by the Receita Federal (Brazilian Federal Revenue Service).

A CNPJ is required for:

  • Opening bank accounts and obtaining credit
  • Issuing invoices (notas fiscais)
  • Signing contracts and participating in public procurement
  • Tax filings and social security obligations
  • Registering with state and municipal authorities
EntityCNPJType
Headquarters11.222.333/0001-81Branch 0001 (matriz)
Branch office11.222.333/0002-62Branch 0002 (filial)
ℹ️Every Brazilian company has at least one CNPJ with branch number 0001, which identifies the headquarters (matriz). Additional branches receive incrementing branch numbers (0002, 0003, etc.) but share the same 8-digit base.

2. CNPJ anatomy

A CNPJ is a 14-digit number. When formatted, it uses the pattern XX.XXX.XXX/XXXX-XX. Internally, it consists of three parts:

Example: 11.222.333/0001-81

11222333Base (8 digits)
0001Branch (4 digits)
81Check (2 digits)
PartPositionsDescription
Base number1–8Uniquely identifies the company. Assigned sequentially by Receita Federal.
Branch number9–120001 = headquarters (matriz). Higher values identify branch offices (filiais).
Check digits13–14Two verification digits computed with a weighted MOD-11 algorithm.
ℹ️The formatted representation uses dots, a slash, and a hyphen as separators: XX.XXX.XXX/XXXX-XX. The API accepts both the formatted and the raw 14-digit form.

3. The check digit algorithm

CNPJ uses two consecutive weighted MOD-11 sums. The first pass computes check digit 1 (position 13) from digits 1–12. The second pass computes check digit 2 (position 14) from digits 1–13 (including the freshly computed check digit 1).

First check digit (position 13)

  1. Multiply each of the first 12 digits by the weight sequence: [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
  2. Sum all products
  3. Calculate remainder = sum mod 11
  4. If the remainder is less than 2, the check digit is 0; otherwise the check digit is 11 − remainder

Second check digit (position 14)

  1. Multiply each of the first 13 digits (including check digit 1) by the weight sequence: [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
  2. Sum all products
  3. Calculate remainder = sum mod 11
  4. Same rule: if the remainder is less than 2, the check digit is 0; otherwise 11 − remainder

Here is a step-by-step worked example for the CNPJ 11222333000181:

PositionDigitWeight (1st)Product
1155
2144
3236
4224
52918
63824
73721
83618
9050
10040
11030
12122

Sum = 5 + 4 + 6 + 4 + 18 + 24 + 21 + 18 + 0 + 0 + 0 + 2 = 102

102 mod 11 = 102 − 9 × 11 = 102 − 99 = 3

The remainder is 3 (not less than 2), so check digit 1 = 11 − 3 = 8.

The second pass uses digits 1–13 (including the 8) with weights [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2], yielding check digit 2 = 1.

Both check digits match the CNPJ 11222333000181 — it is structurally valid.

Here is a JavaScript implementation:

function validateCNPJ(cnpj) {
  // Strip formatting
  const digits = cnpj.replace(/[^\d]/g, '');
  if (digits.length !== 14) return false;

  // Reject known invalid patterns (all same digit)
  if (/^(\d)\1{13}$/.test(digits)) return false;

  const d = digits.split('').map(Number);

  // First check digit (position 13)
  const w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
  const sum1 = w1.reduce((acc, w, i) => acc + d[i] * w, 0);
  const rem1 = sum1 % 11;
  const check1 = rem1 < 2 ? 0 : 11 - rem1;
  if (check1 !== d[12]) return false;

  // Second check digit (position 14)
  const w2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
  const sum2 = w2.reduce((acc, w, i) => acc + d[i] * w, 0);
  const rem2 = sum2 % 11;
  const check2 = rem2 < 2 ? 0 : 11 - rem2;
  return check2 === d[13];
}
⚠️A CNPJ that passes the checksum is not necessarily an active, registered company. The number may belong to a dissolved or suspended entity. Use the ?lookup=true parameter to check the company's status via BrasilAPI.

4. Why manual validation isn't enough

Checksum only catches typos

The MOD-11 algorithm detects transposition errors and single-digit mistakes, but it cannot tell you whether the CNPJ was actually issued by Receita Federal. A number can pass the checksum and still be completely fabricated.

Companies can be inactive or suspended

A CNPJ remains structurally valid even after the company is dissolved, has its registration suspended, or is declared unfit (inapta) by Receita Federal. Only a registry lookup can reveal the current status.

You need company details for compliance

KYC, invoicing, and procurement workflows require more than a boolean. You need the company's legal name (razão social), trade name (nome fantasia), activity code (CNAE), and registration status. The checksum gives you none of this.


5. The right solution: one API call

The IsValid CNPJ API handles the full validation stack in a single GET request:

1

Checksum validation

Two-pass MOD-11 algorithm on all 14 digits

2

Company lookup (optional)

Legal name, trade name, CNAE, status, and address from BrasilAPI

3

Headquarters detection

Identifies whether the CNPJ is a headquarters (0001) or a branch

Get your free API key at isvalid.dev. The free tier includes 100 calls per day with no credit card required.

Full parameter reference and response schema: CNPJ Validation API docs →


6. Node.js code example

Using the @isvalid-dev/sdk package or the built-in fetch API (Node.js 18+):

// cnpj-validator.mjs
import { createClient } from '@isvalid-dev/sdk';

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY });

// ── Basic validation ────────────────────────────────────────────────────────
const result = await iv.br.cnpj('11.222.333/0001-81');

if (!result.valid) {
  console.log('Invalid CNPJ');
} else {
  console.log('Formatted:', result.formatted);    // "11.222.333/0001-81"
  console.log('HQ?      :', result.isHeadquarters); // true
}

// ── With company lookup ─────────────────────────────────────────────────────
const detailed = await iv.br.cnpj('11222333000181', { lookup: true });

if (detailed.valid && detailed.found) {
  console.log('Company  :', detailed.razaoSocial);
  console.log('Trade    :', detailed.nomeFantasia);
  console.log('Status   :', detailed.situacao);
  console.log('CNAE     :', detailed.cnaeFiscal, detailed.cnaeDescricao);
  console.log('State    :', detailed.uf);
}
The lookup parameter is optional. Use it when you need company details like legal name, status, or CNAE code. Omit it for fast checksum-only validation.

7. cURL examples

Basic CNPJ validation:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/br/cnpj?value=11.222.333/0001-81"

With company lookup via BrasilAPI:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/br/cnpj?value=11222333000181&lookup=true"

8. Understanding the response

Valid CNPJ (basic)

{
  "valid": true,
  "formatted": "11.222.333/0001-81",
  "base": "11222333",
  "branch": "0001",
  "checkDigits": "81",
  "isHeadquarters": true
}

Valid CNPJ (with lookup)

{
  "valid": true,
  "formatted": "11.222.333/0001-81",
  "base": "11222333",
  "branch": "0001",
  "checkDigits": "81",
  "isHeadquarters": true,
  "found": true,
  "razaoSocial": "EMPRESA EXEMPLO LTDA",
  "nomeFantasia": "EXEMPLO",
  "situacao": "Ativa",
  "dataInicioAtividade": "2010-05-15",
  "cnaeFiscal": "6201501",
  "cnaeDescricao": "Desenvolvimento de programas de computador sob encomenda",
  "naturezaJuridica": "206-2 - Sociedade Empresaria Limitada",
  "porte": "ME",
  "capitalSocial": 50000,
  "uf": "SP",
  "municipio": "SAO PAULO",
  "cep": "01310100",
  "dataSource": "brasilapi"
}

Invalid CNPJ

{ "valid": false }
FieldTypeDescription
validbooleanWhether the CNPJ passed format and checksum validation
formattedstringCNPJ in XX.XXX.XXX/XXXX-XX format
basestring8-digit company identifier
branchstring4-digit branch number (0001 = headquarters)
checkDigitsstringThe two verification digits
isHeadquartersbooleanTrue when the branch number is 0001
foundbooleanWhether the company was found in BrasilAPI (only with lookup=true)
razaoSocialstringLegal name of the company
nomeFantasiastringTrade name / brand name
situacaostringRegistration status (e.g. "Ativa", "Baixada", "Inapta")
dataInicioAtividadestringDate when business activity started (YYYY-MM-DD)
cnaeFiscalstringPrimary economic activity code (CNAE)
cnaeDescricaostringHuman-readable description of the CNAE code
naturezaJuridicastringLegal nature (e.g. "206-2 - Sociedade Empresaria Limitada")
portestringCompany size classification (ME, EPP, etc.)
capitalSocialnumberRegistered share capital in BRL
ufstringTwo-letter state code (e.g. "SP", "RJ")
municipiostringCity / municipality name
cepstringPostal code (8 digits, no hyphen)
dataSourcestringData provider identifier ("brasilapi")

9. Edge cases to handle

(a) Branch vs headquarters

A CNPJ with branch number 0001 identifies the headquarters (matriz). Any other branch number (0002, 0003, etc.) identifies a branch office (filial). The isHeadquarters field tells you which one it is. For KYC or supplier onboarding, you may want to require the headquarters CNPJ.

const result = await validateCNPJ('11222333000262');

if (result.valid && !result.isHeadquarters) {
  console.log('This is a branch office (filial)');
  console.log('Branch number:', result.branch); // "0002"
  // Consider requesting the headquarters CNPJ instead
}

(b) Inactive companies

A CNPJ that passes checksum validation may belong to a company with status "Baixada" (deregistered) or "Inapta" (unfit). Always check the situacao field in the lookup response for compliance workflows.

const result = await validateCNPJ('11222333000181', { lookup: true });

if (result.valid && result.found) {
  if (result.situacao !== 'Ativa') {
    console.log('Company is not active:', result.situacao);
    // "Baixada" = deregistered, "Inapta" = unfit, "Suspensa" = suspended
  } else {
    console.log('Company is active');
  }
}

(c) CNPJ formatting

The API accepts both formatted (11.222.333/0001-81) and raw (11222333000181) input. The response always includes the formatted field for display. Store the raw 14-digit string in your database and format only for display.

// Both produce the same result:
const a = await validateCNPJ('11.222.333/0001-81');
const b = await validateCNPJ('11222333000181');

// Always store the raw form
const cnpjForDb = result.formatted.replace(/[^\d]/g, '');

// Use the formatted version for display
console.log('Display:', result.formatted); // "11.222.333/0001-81"

(d) MEI vs ME vs EPP

The porte field in the lookup response indicates the company size classification. MEI (Microempreendedor Individual) is a sole proprietor with revenue up to R$81,000/year. ME (Microempresa) has revenue up to R$360,000/year. EPP (Empresa de Pequeno Porte) goes up to R$4,800,000/year. This matters for tax bracket calculations and supplier eligibility.

const result = await validateCNPJ('11222333000181', { lookup: true });

if (result.valid && result.found) {
  switch (result.porte) {
    case 'MEI':
      console.log('Sole proprietor (MEI)');
      break;
    case 'ME':
      console.log('Micro enterprise');
      break;
    case 'EPP':
      console.log('Small enterprise');
      break;
    default:
      console.log('Company size:', result.porte);
  }
}

Summary

Do not rely on checksum alone — it cannot verify the company is registered or active
Do not store CNPJ as an integer — leading zeros will be lost
Do not skip the situacao check — deregistered companies still pass validation
Validate both check digits with the two-pass MOD-11 algorithm
Use lookup=true for KYC flows — returns legal name, status, CNAE, and address
Check isHeadquarters to distinguish matriz from filial

See also

Validate CNPJ numbers instantly

Free tier includes 100 API calls per day. No credit card required. Checksum validation and optional company lookup from BrasilAPI included.