Guide · Python · SDK · REST API

CPF Validation in Python

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 in Python 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.

PositionsLengthDescription
1–88Registration number (sequential)
91Fiscal region digit (see table below)
101First check digit (weighted MOD-11)
111Second 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 Python implementation of the algorithm:

import re


def validate_cpf(cpf: str) -> bool:
    """Validate a Brazilian CPF number using the two-step MOD-11 checksum.

    Args:
        cpf: A CPF string (formatted or unformatted).

    Returns:
        True if both check digits are valid, False otherwise.
    """
    # Strip formatting characters
    digits = re.sub(r"\D", "", cpf)
    if len(digits) != 11:
        return False

    d = [int(c) for c in digits]

    # First check digit (D1): weights 10..2 over digits 0..8
    sum1 = sum(d[i] * (10 - i) for i in range(9))
    rem1 = sum1 % 11
    d1 = 0 if rem1 < 2 else 11 - rem1
    if d1 != d[9]:
        return False

    # Second check digit (D2): weights 11..2 over digits 0..9
    sum2 = sum(d[i] * (11 - i) for i in range(10))
    rem2 = sum2 % 11
    d2 = 0 if rem2 < 2 else 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. Python code example

Using the isvalid-sdk Python SDK or the requests library. Install with pip install isvalid-sdk or pip install requests.

# cpf_validator.py
import os
from isvalid_sdk import IsValidConfig, create_client

iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"]))

# ── Validate a CPF number ────────────────────────────────────────────────
result = iv.br.cpf("529.982.247-25")

if not result["valid"]:
    print("Invalid CPF")
else:
    print(f"Formatted    : {result['formatted']}")     # "529.982.247-25"
    print(f"Check digits : {result['checkDigits']}")    # "25"

In a web application, you might expose a CPF validation endpoint with Flask:

# app.py (Flask)
import os
from flask import Flask, request, jsonify
from isvalid_sdk import IsValidConfig, create_client

app = Flask(__name__)
iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"]))


@app.post("/verify-cpf")
def verify_cpf():
    data = request.get_json()
    cpf = data.get("cpf")

    if not cpf:
        return jsonify(error="CPF is required"), 400

    try:
        result = iv.br.cpf(cpf)
    except Exception:
        return jsonify(error="CPF validation service unavailable"), 502

    if not result["valid"]:
        return jsonify(error="Invalid CPF number"), 400

    return jsonify(
        valid=True,
        formatted=result["formatted"],
        checkDigits=result["checkDigits"],
    )
The SDK handles authentication, retries, and response parsing for you. For production applications, prefer the SDK over raw HTTP calls.

7. cURL example

Validate a CPF number from the command line:

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
ℹ️The formatted field always returns the canonical XXX.XXX.XXX-XX format, regardless of how the input was submitted. Use this field to display the CPF consistently in your application.

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
result = iv.br.cpf("111.111.111-11")
print(result["valid"])  # False

# The API catches what the math alone cannot
result2 = iv.br.cpf("000.000.000-00")
print(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
r1 = iv.br.cpf("529.982.247-25")  # formatted
r2 = iv.br.cpf("52998224725")      # raw digits
r3 = 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
cpf_result = iv.br.cpf("529.982.247-25")

# Business taxpayer — use the CNPJ endpoint (different weights and length)
# cnpj_result = iv.br.cnpj("11.222.333/0001-81")

(d) Batch validation

When validating multiple CPFs (e.g. importing a CSV of customers), use concurrent.futures to parallelize API calls and avoid sequential bottlenecks:

import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from isvalid_sdk import IsValidConfig, create_client

iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"]))

cpf_numbers = ["529.982.247-25", "111.111.111-11", "305.384.210-47"]

with ThreadPoolExecutor(max_workers=5) as pool:
    futures = {pool.submit(iv.br.cpf, c): c for c in cpf_numbers}

    for future in as_completed(futures):
        cpf = futures[future]
        result = future.result()
        status = "valid" if result["valid"] else "invalid"
        print(f"{cpf}: {status}")

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.