Mexican CURP Validator

Validate Mexican CURP identifiers in bulk — structure, date, state code and the RENAPO check digit.

CURPs are validated in your browser — never sent anywhere

Check the structure, date, state and RENAPO check digit — one CURP or a whole list

Example loaded — edit it or clear it

Total
Valid
Invalid

What this validator checks — and what it cannot confirm

Paste one CURP or a full column of them (one per line) and see, for each row, whether the 18-character structure is right, the date is real, the state code is one RENAPO issues, the sex letter is H or M, the generation marker matches the century, and — the piece a naked eye cannot see — whether the final check digit computes correctly from the previous seventeen. The tool marks each line ✓ or ✗, gives the specific reason for a rejection, and totals the batch: pass the whole list, or a single value, into the box.

What it deliberately does not do is confirm that the CURP is registered with RENAPO. That check requires an authenticated query to a government service, and any browser page that claims to do it either does not, or is quietly proxying your data to a third party. A well-formed CURP with a correct check digit still might not correspond to a real registered person — the honest scope of this tool is catching typos and clearly invalid inputs before you push a batch to a system that will reject them.

The mechanism is worth spelling out because it explains the failure modes. A CURP encodes name letters (four at the start, three consonants at positions 14–16), a birthdate as YYMMDD, an H/M sex marker, a two-letter state code from a fixed RENAPO catalog, a 17th generation character (a digit 0–9 for people born 1900–1999, a letter A–Z for 2000 onwards), and a check digit computed by weighting each of the first 17 characters against a base-37 alphabet, summing, taking mod 10 and subtracting from 10. The check digit catches every single-character typo and most transpositions — which is exactly the class of errors CURPs pick up when data moves between systems.

Two subtleties worth naming. First, RENAPO substitutes an X into the second letter when the first four would spell one of a documented list of vulgar Spanish words (BUEI, CACA, MAME, PUTO and about eighty others) — that substitution is silent, and the resulting CURP is fully valid; the check-digit math still works because the X went in before the digit was computed. Second, the 17th character is a generation marker, not a random pad: it disambiguates the century behind the two-digit year. A CURP with a digit in position 17 belongs to someone born in the 20th century; a letter means the 21st. This tool infers the century from that character and displays the full birthdate in the result line.

Everything runs in your browser: the input never leaves the tab, and there is no upload, no queue and no daily cap. This is a text-processing job — a regex, a lookup table and a sum — and it does not need a server. That matters when the input is a CSV of HR data or a customer list, because a validator that ships your identifiers to an unknown backend is a compliance problem you did not sign up for.

Anatomy of a CURP, character by character

Every CURP is exactly 18 characters and every position means something. Once you can read the layout, most invalid inputs jump out before the tool even runs.

PositionWhat it isRule
1First letter of paternal surnameUppercase letter (or Ñ)
2First internal vowel of paternal surnameA E I O U — or X after the vulgar-word swap
3First letter of maternal surnameUppercase letter, or X if the person has no maternal surname
4First letter of first (given) nameUppercase letter
5–10BirthdateYYMMDD, year in two digits
11SexH (male) or M (female)
12–13State of birthTwo-letter code from RENAPO's catalog; NE for people born abroad
14First internal consonant of paternal surnameUppercase consonant
15First internal consonant of maternal surnameUppercase consonant
16First internal consonant of first nameUppercase consonant
17Generation markerDigit 0–9 for people born 1900–1999; letter A–Z for 2000 onwards
18Check digitComputed from the first 17 characters (algorithm below)

How the check digit is computed

The 18th character is RENAPO's built-in typo detector. Each of the first 17 characters is mapped to a value in a base-37 alphabet, multiplied by a weight, summed, and reduced.

ALPHABET = "0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"

for each i in 0..16:
    value    = index of curp[i] in ALPHABET
    weight   = 18 - (i + 1)
    sum     += value * weight

result      = sum mod 10
check_digit = (10 - result) mod 10
Position 1 gets weight 17; position 17 gets weight 1. The final mod-10 handles the case where result is 0.

Worked example on GOFC560731HDFRRR09 (a commonly cited RENAPO training CURP): summing the 17 weighted values gives 1831, mod 10 = 1, and (10 − 1) mod 10 = 9 — which is the check digit at position 18.

Validating CURPs in Excel or Google Sheets

For a first-pass structure check across a column, this REGEX matches every well-formed CURP. It does NOT validate the check digit — that requires a helper function — but it catches the majority of typos in one formula.

Google Sheets (A2 = CURP):
=REGEXMATCH(UPPER(A2), "^[A-ZÑ]{4}\d{6}[HM][A-Z]{2}[A-ZÑ]{3}[0-9A-Z]\d$")

Excel 365 (A2 = CURP):
=IF(ISNUMBER(SEARCH("?", A2)), NOT(ISERROR(FIND("^", ""))), FALSE)  // use a proper regex helper, e.g. via Office Scripts
Google Sheets has native REGEXMATCH; Excel 365 needs a small LAMBDA or Office Scripts helper.

For a full-fidelity check including the digit, build a small user-defined function in Google Apps Script or Office Scripts that implements the algorithm above. Or paste the column into the batch box on this page and copy the results back.

Validating CURPs in SQL

A first-pass structural check as a single WHERE clause. Use the appropriate regex operator for your engine — the pattern itself is the same.

-- PostgreSQL / Snowflake (case-insensitive regex)
SELECT curp
FROM   customers
WHERE  curp !~* '^[A-ZÑ]{4}[0-9]{6}[HM][A-Z]{2}[A-ZÑ]{3}[0-9A-Z][0-9]$';

-- MySQL / MariaDB
SELECT curp
FROM   customers
WHERE  UPPER(curp) NOT REGEXP '^[A-ZÑ]{4}[0-9]{6}[HM][A-Z]{2}[A-ZÑ]{3}[0-9A-Z][0-9]$';

-- SQL Server needs LIKE with underscores or a CLR regex; the LIKE pattern below only catches length + digits
-- SELECT curp FROM customers WHERE curp NOT LIKE '____[0-9][0-9][0-9][0-9][0-9][0-9][HM]________';
Structural validation. The check digit needs a stored function; the algorithm above is short enough to translate directly.

FAQ

What does the CURP check digit actually catch?

Any single-character error and almost every two-character transposition — the two classes of typo that dominate when identifiers move between systems. The math weights each of the 17 preceding characters by its position (17 down to 1), against a base-37 alphabet (0–9, then A through Z with Ñ inserted), sums them, takes mod 10 and subtracts from 10. Change one character and the sum changes; swap two adjacent characters and the sum changes too. That is why RENAPO uses it as the last-line defense against clerical errors.

Does a valid CURP mean the person is really registered?

No — and any tool that claims that from a browser is either wrong or quietly sending your data somewhere. This validator proves the CURP is well-formed and internally consistent (structure, date, state, sex, generation marker, check digit). Confirming the CURP exists in the national registry needs an authenticated request to RENAPO's official service; that is intentionally not something a static web page can do. Use this to catch the fake-looking ones; use RENAPO's own verification service to confirm the real ones.

Why do some CURPs contain X in the second letter?

RENAPO silently swaps an X into the second character when the first four letters would spell one of about eighty vulgar words — BUEI, CACA, MAME, PUTO and their variants. The substitution happens before the check digit is computed, so the resulting CURP is completely valid and the check digit still works. Individuals do not choose it; the assignment is automatic at issuance. If you look at a real CURP list you will see the pattern occasionally, and it is genuine, not a typo.

How is CURP different from RFC?

They come from different agencies and encode different things. CURP is issued by RENAPO for civil identification and every person born or residing legally in México has one; it is 18 characters. RFC is issued by SAT for tax purposes; individuals get a 13-character RFC and companies get a 12-character one. The first ten characters of an individual's CURP and RFC come from the same rules and often match, but the trailing characters (state + consonants + generation vs homoclave + check digit) diverge. This tool validates CURP; the RFC validator on this site handles the SAT algorithm.

Related tools