Guide · Node.js · SDK · REST API

CPF Validation in Node.js

The Brazilian individual taxpayer identification number — 11 digits with two check digits computed via weighted MOD-11 sums. Here's how to validate it correctly and handle the edge cases that trip up manual implementations.

1. What is a CPF?

CPF (Cadastro de Pessoas Fisicas) is the Brazilian individual taxpayer identification number. It is issued by the Receita Federal (the Brazilian Federal Revenue Service) and assigned to every Brazilian citizen and resident foreigner who needs to interact with the tax system. The CPF is an 11-digit number that serves as the primary identifier across virtually every public and private system in Brazil:

  • Banking and financial services — required for opening accounts, applying for credit cards, and processing loans
  • Tax filings — used as the individual taxpayer ID for annual income tax declarations
  • Healthcare — identifies patients in the SUS (Sistema Unico de Saude) public health system
  • E-commerce — required for purchases, invoice generation (nota fiscal), and marketplace registration
  • Government services — needed for voter registration, passport applications, and social programs like Bolsa Familia
  • Employment — mandatory for formal employment contracts and payroll processing

Unlike some national IDs, the CPF does not encode personal data like date of birth or gender. It is a registration number with two check digits that guard against transcription errors. However, the 9th digit does encode the fiscal region where the CPF was originally issued.

ℹ️The CPF was introduced in 1965 and is maintained by the Receita Federal. Every individual registered in the Brazilian tax system has a unique 11-digit CPF that remains unchanged for life.

2. CPF anatomy

A CPF number is always exactly 11 digits. When formatted, it follows the pattern XXX.XXX.XXX-XX, where dots separate groups of three digits and a hyphen precedes the two check digits.

PositionsDigitsMeaning
1–8XXXXXXXXRegistration number (sequential)
9RFiscal region digit (see table below)
10D1First check digit (weighted MOD-11)
11D2Second check digit (weighted MOD-11)

For example, the CPF 529.982.247-25 breaks down as: registration number 52998224, fiscal region 7 (Rio de Janeiro and Espirito Santo), first check digit 2, second check digit 5.

Fiscal region codes

The 9th digit indicates the fiscal region where the CPF was originally registered. This does not change if the person moves to a different state:

DigitRegion
0Rio Grande do Sul
1Distrito Federal, Goias, Mato Grosso, Mato Grosso do Sul, Tocantins
2Amazonas, Para, Roraima, Amapa, Acre, Rondonia
3Ceara, Maranhao, Piaui
4Paraiba, Pernambuco, Alagoas, Rio Grande do Norte
5Bahia, Sergipe
6Minas Gerais
7Rio de Janeiro, Espirito Santo
8Sao Paulo (capital and metropolitan area)
9Parana, Santa Catarina
ℹ️The formatted CPF uses the pattern XXX.XXX.XXX-XX. The API accepts both formatted and unformatted input and always returns the canonical formatted version in the response.

3. The check digit algorithm

The CPF uses two weighted MOD-11 sums to compute its check digits. The first check digit is computed from the first 9 digits, and the second check digit is computed from all 10 digits (the original 9 plus the first check digit).

First check digit (D1)

  1. Take the first 9 digits of the CPF
  2. Multiply each digit by descending weights: 10, 9, 8, 7, 6, 5, 4, 3, 2
  3. Sum all products
  4. Compute the remainder: remainder = sum mod 11
  5. If remainder < 2, the check digit is 0; otherwise, the check digit is 11 − remainder

Second check digit (D2)

  1. Take the first 9 digits plus the first check digit (10 digits total)
  2. Multiply each digit by descending weights: 11, 10, 9, 8, 7, 6, 5, 4, 3, 2
  3. Sum all products
  4. Compute the remainder: remainder = sum mod 11
  5. If remainder < 2, the check digit is 0; otherwise, the check digit is 11 − remainder

Step-by-step example

Let's validate 529.982.247-25 (digits: 5 2 9 9 8 2 2 4 7 2 5):

First check digit (D1) — weights 10 down to 2:

PositionDigitWeightProduct
151050
22918
39872
49763
58648
62510
7248
84312
97214

Sum = 50 + 18 + 72 + 63 + 48 + 10 + 8 + 12 + 14 = 295

Remainder = 295 mod 11 = 9

Since 9 ≥ 2: D1 = 11 − 9 = 2

Second check digit (D2) — weights 11 down to 2, including D1:

Digits: 5, 2, 9, 9, 8, 2, 2, 4, 7, 2 (D1)

Weights: 11, 10, 9, 8, 7, 6, 5, 4, 3, 2

Sum = 55 + 20 + 81 + 72 + 56 + 12 + 10 + 16 + 21 + 4 = 347

Remainder = 347 mod 11 = 6

Since 6 ≥ 2: D2 = 11 − 6 = 5

The computed check digits are 2 and 5, which match the last two digits of 529.982.247-25. This CPF passes the checksum.

Here is a JavaScript implementation of the algorithm:

function validateCpfChecksum(cpf) {
  const d = cpf.replace(/\D/g, '').split('').map(Number);
  if (d.length !== 11) return false;

  // First check digit (D1): weights 10..2 over digits 0..8
  const sum1 = d.slice(0, 9).reduce((acc, v, i) => acc + v * (10 - i), 0);
  const rem1 = sum1 % 11;
  const d1 = rem1 < 2 ? 0 : 11 - rem1;
  if (d1 !== d[9]) return false;

  // Second check digit (D2): weights 11..2 over digits 0..9
  const sum2 = d.slice(0, 10).reduce((acc, v, i) => acc + v * (11 - i), 0);
  const rem2 = sum2 % 11;
  const d2 = rem2 < 2 ? 0 : 11 - rem2;
  return d2 === d[10];
}
⚠️This checksum function only verifies the two check digits. It does not reject all-same-digit numbers like 111.111.111-11, which pass the math but are not valid CPFs. A complete validator must handle these special cases separately.

4. Why manual validation isn't enough

The all-same-digits trap

Numbers where all 11 digits are identical — 000.000.000-00, 111.111.111-11, 222.222.222-22, all the way through 999.999.999-99 — all pass the MOD-11 checksum algorithm. The weighted sums produce remainders that happen to generate matching check digits. But none of these are real CPFs. A naive validator that only checks the math will accept all 10 of these invalid inputs.

Checksum does not verify existence

The MOD-11 checksum only catches accidental transcription errors (transposed digits, single-digit typos). It does not confirm that the CPF was ever actually issued by the Receita Federal. Anyone can generate an 11-digit string that passes the checksum formula — that does not make it a valid taxpayer ID.

Formatting variations

CPFs appear in different formats depending on context: with dots and hyphen (529.982.247-25), with just a hyphen (529982247-25), or as raw digits (52998224725). Your validator needs to normalize the input before applying the checksum. Stripping non-digits sounds simple, but edge cases (letters mixed in, wrong length after stripping) require careful handling.

CPF vs CNPJ confusion

Users sometimes enter a CNPJ (14-digit business tax ID) in a CPF field, or vice versa. Both use MOD-11 check digits but with different lengths and weight patterns. A validator that only checks the checksum without verifying the digit count can silently accept the wrong document type.


5. The right solution: one API call

The IsValid CPF API handles the complete validation pipeline in a single GET request:

1

Format normalization

Accepts dots, hyphens, spaces, or raw digits and normalizes to the canonical format

2

All-same-digits rejection

Rejects 000.000.000-00 through 999.999.999-99 even though they pass the checksum

3

Check digit verification

Two-step weighted MOD-11 validation with correct remainder logic

4

Formatted output

Returns the canonical XXX.XXX.XXX-XX format and extracted check digits

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: CPF Validation API docs →


6. Node.js code example

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

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

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

// ── Validate a CPF number ─────────────────────────────────────────────────
const result = await iv.br.cpf('529.982.247-25');

if (!result.valid) {
  console.log('Invalid CPF');
} else {
  console.log('Formatted   :', result.formatted);    // "529.982.247-25"
  console.log('Check digits:', result.checkDigits);   // "25"
}
The SDK uses the country-specific namespace iv.br.cpf(). All Brazilian identifiers (CPF, CNPJ) are grouped under iv.br.

7. cURL example

Validate a CPF number directly from the terminal:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/br/cpf?value=529.982.247-25"

A successful response includes the formatted CPF and its check digits:

{
  "valid": true,
  "formatted": "529.982.247-25",
  "checkDigits": "25"
}

8. Understanding the response

Valid CPF

{
  "valid": true,
  "formatted": "529.982.247-25",
  "checkDigits": "25"
}

Invalid CPF

{ "valid": false }
FieldTypeDescription
validbooleanWhether the CPF passed format validation, all-same-digits rejection, and both MOD-11 check digit verifications
formattedstringThe CPF in canonical format (XXX.XXX.XXX-XX). Only present when valid: true
checkDigitsstringThe two check digits as a two-character string (e.g. "25"). Only present when valid: true

9. Edge cases

(a) All-same-digits rejection

All 10 numbers with identical digits (000.000.000-00 through 999.999.999-99) produce valid check digits when run through the MOD-11 algorithm. This is a well-known quirk of the formula. The IsValid API explicitly rejects all of these, returning { "valid": false }:

// All-same-digits — passes checksum but is NOT a valid CPF
const result = await iv.br.cpf('111.111.111-11');
console.log(result.valid); // false

// The API catches what the math alone cannot
const result2 = await iv.br.cpf('000.000.000-00');
console.log(result2.valid); // false

(b) Formatting normalization

The API accepts CPFs in any common format — with dots and hyphen, with just a hyphen, with spaces, or as raw digits. It strips non-digit characters before validation and returns the canonical formatted version:

// All of these are equivalent — the API normalizes the input
const r1 = await iv.br.cpf('529.982.247-25');  // formatted
const r2 = await iv.br.cpf('52998224725');      // raw digits
const r3 = await iv.br.cpf('529982247-25');     // partial formatting

// All return: { valid: true, formatted: "529.982.247-25", checkDigits: "25" }

(c) CPF vs CNPJ

CPF is the individual taxpayer ID (11 digits), while CNPJ is the business taxpayer ID (14 digits). Both use MOD-11 check digits but with different lengths and weight sequences. If a user submits a 14-digit CNPJ to the CPF endpoint, the API returns invalid because the length does not match. Use the appropriate endpoint for each document type:

// Individual taxpayer — use the CPF endpoint
const cpfResult = await iv.br.cpf('529.982.247-25');

// Business taxpayer — use the CNPJ endpoint (different weights and length)
// const cnpjResult = await iv.br.cnpj('11.222.333/0001-81');

10. Summary

The CPF is the backbone of individual identification in Brazil. Eleven digits, two check digits, and a deceptively simple MOD-11 algorithm — but the all-same-digits trap, formatting variations, and CPF/CNPJ confusion add layers of complexity that are easy to get wrong. The IsValid API handles all of it in a single call and returns structured, ready-to-use data.

Do not accept all-same-digit numbers — they pass the checksum but are not real CPFs
Do not confuse CPF (11 digits, individual) with CNPJ (14 digits, business)
Do not assume a valid checksum means the CPF exists — checksum only catches typos
Use the API to validate format, reject all-same-digits, and verify both check digits
Accept any formatting — the API normalizes dots, hyphens, and spaces automatically
Use the formatted response field to display a consistent canonical format to users

See also

Validate CPF numbers instantly

Free tier includes 100 API calls per day. No credit card required. Format normalization, all-same-digits rejection, and MOD-11 check digit verification included.