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.
In this guide
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.
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.
| Positions | Digits | Meaning |
|---|---|---|
| 1–8 | XXXXXXXX | Registration number (sequential) |
| 9 | R | Fiscal region digit (see table below) |
| 10 | D1 | First check digit (weighted MOD-11) |
| 11 | D2 | Second 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:
| Digit | Region |
|---|---|
| 0 | Rio Grande do Sul |
| 1 | Distrito Federal, Goias, Mato Grosso, Mato Grosso do Sul, Tocantins |
| 2 | Amazonas, Para, Roraima, Amapa, Acre, Rondonia |
| 3 | Ceara, Maranhao, Piaui |
| 4 | Paraiba, Pernambuco, Alagoas, Rio Grande do Norte |
| 5 | Bahia, Sergipe |
| 6 | Minas Gerais |
| 7 | Rio de Janeiro, Espirito Santo |
| 8 | Sao Paulo (capital and metropolitan area) |
| 9 | Parana, Santa Catarina |
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)
- Take the first 9 digits of the CPF
- Multiply each digit by descending weights:
10, 9, 8, 7, 6, 5, 4, 3, 2 - Sum all products
- Compute the remainder:
remainder = sum mod 11 - If remainder < 2, the check digit is 0; otherwise, the check digit is 11 − remainder
Second check digit (D2)
- Take the first 9 digits plus the first check digit (10 digits total)
- Multiply each digit by descending weights:
11, 10, 9, 8, 7, 6, 5, 4, 3, 2 - Sum all products
- Compute the remainder:
remainder = sum mod 11 - 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:
| Position | Digit | Weight | Product |
|---|---|---|---|
| 1 | 5 | 10 | 50 |
| 2 | 2 | 9 | 18 |
| 3 | 9 | 8 | 72 |
| 4 | 9 | 7 | 63 |
| 5 | 8 | 6 | 48 |
| 6 | 2 | 5 | 10 |
| 7 | 2 | 4 | 8 |
| 8 | 4 | 3 | 12 |
| 9 | 7 | 2 | 14 |
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]; }
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:
Format normalization
Accepts dots, hyphens, spaces, or raw digits and normalizes to the canonical format
All-same-digits rejection
Rejects 000.000.000-00 through 999.999.999-99 even though they pass the checksum
Check digit verification
Two-step weighted MOD-11 validation with correct remainder logic
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" }
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 }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the CPF passed format validation, all-same-digits rejection, and both MOD-11 check digit verifications |
| formatted | string | The CPF in canonical format (XXX.XXX.XXX-XX). Only present when valid: true |
| checkDigits | string | The 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.
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.