Validate National Identification Numbers

National identification numbers, also known as national IDs, are unique identification codes issued by governments to their citizens and residents. These codes are used for various purposes, such as taxation, healthcare, education, travel documentation, and account opening.

If you're building an API or application that accepts national IDs, it's essential to validate them accurately to maintain data quality and prevent fraudulent activities. This article outlines best practices for validating national IDs.


Check the number of digits

Most countries issue national IDs with a fixed number of digits, such as Brazil's CPF numbers with 11 digits or India's Aadhaar numbers with 12 digits. Validate that the input contains the exact number of digits for that country.


Verify the checksum to confirm the ID number is logically correct

Use checksum to verify (such as the Luhn algorithm) to validate the ID number. For example, the last two digits of an Indian Aadhaar number are check digits to validate the first 10 digits. Verify the checksum to confirm the ID number is logically correct.


Ensure the number passes country-specific validation

To conduct a comprehensive national ID validation, it's important to validate all country-specific requirements and thoroughly verify the ID. Many countries have additional validation checks for their IDs, including date ranges, location codes, prefixes, etc. For instance, South Korean citizen numbers issued before 2000 start with 1 or 2, while those issued after 2000 start with 6 or 7.

The following sections provide some country-specific validations.


American Samoa

Below is the National ID validation for American Samoa:

(^\d{3}-\d{2}-\d{4}$)|(^\d{4}$)|(^\d{9}$)

Argentina

Below is the Argentinian National ID validation:

(^\d{2}[.]?\d{3}[.]?\d{3}$)

OR

(^\d{7,8}[-]?\d$)

Australia

Below is the Australian National ID validation:

(^[A-Za-z\d]{8,11}$)

Bangladesh

Below is the National ID validation for Bangladesh:

^\d{13}$

Belgium

Below is the Belgium National Registry Number validation:

^(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})(?P<seq>\d{3})(?P<check>\d)
(?P<year>(?:18|19)\d{2}|20\d{2})$ 

And here is the check digit validation logic:

# Validate check digit 
check_digit = 89 - (year + month*2 + day*3 + int(groups['seq'])*7)
if check_digit % 11 != int(groups['check']):
    print("Invalid ID")
    return        

# Ensure ID is in sorted order
prev_id = id_num[:9] + str(check_digit)  
if prev_id >= id_num:
    print("Invalid ID")
    return 
print("Valid ID")

Brazil

Below is the Brazilian Cadastro de Pessoas Físicas (CPF) validation:

(^\d{3}\.\d{3}\.\d{3}\-\d{2}$)

OR

(^\d{11}$)

Canada

Below is the Canadian National Insurance Number validation:

(^\\d{3}[-\\s]?\\d{3}[-\\s]?\\d{3}$)

Chile

Below is the Rol Único Nacional (RUN) validation for Chile:

((^\d{1,2}[.]\d{3}[.]\d{3}[-][A-Za-z\d]$)

OR

(^\d{7,8}[-]?[A-Za-z\d]$)

China

Below is the the National ID validation for China:

^[a-zA-Z0-9]{18}$

Colombia

Below is the the Colombian National ID validation:

(^[A-Za-z\d]{5,11}$)

Czech Republic

Below is the Czech Republic (Czechia) Rodné číslo National ID validation:

^([0-9]{2}[0-9]{2}(?:[0-5][0-9]|[68][0-9])(?:0[1-9]|[12][0-9]|3[01])|
([0-9]{4}[0-9]{2})[/][0-9]{2,4}$

And here is the checksum validation logic:

# Validate checksum  
checksum = 0
for num in birth_date:
    checksum += int(num)
if (checksum % 11) == 0 or 10:
    multiplier = 1  
elif (checksum % 11) == 1:
    multiplier = 0  
else:
    multiplier = 11 - (checksum % 11)  
expected = multiplier * sum(int(x) for x in national_id[-3:-1]) % 11
if int(national_id[-1]) == expected:
    print("Valid ID")
else:
    print("Invalid ID") 

Denmark

Below is the Danish National ID validation:

(^\d{6}[-]?\d{4}$)

Estonia

Below is the Estonian National ID validation:

(^\d\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])\d{3}\d$)

Finland

Below is the Finnish National ID validation:

(^(?:0[1-9]|[12][0-9]|3[01])(?:0[1-9]|1[0-2])\d{2}(?i:[-+ABCDEFUVWXY])\d{3}(?i)[\dA-FHJ-NPR-Y]$)

France

Below is the French INSEE validation:

^(?<dept_code>[01-8][0-9])(-(?<gender_code>[0-9][0-9])){2}(-(?<year_code>0[1-9]
|[1-9][0-9]))
(-(?<month_code>0[1-9]|1[0-2]))(-(?<day_code>[0-2][1-9]|[03][0-1]|10))
(-(?<serial>[0-9][0-9]))$

Germany

Below is the German Personalausweisnummer validation:

^(?:(?!000|474[0-7]|900[0-9])\d{9}|(?!000|474[0-7]|900[0-9])\d{10}(?=\d|X))$

Greece

Below is the Greek AMAT Identity Card Registration Number validation:

^(0[1-5][0-9])((([12][0-9])|(0[0-9]))([0-9]{6}|[0]{1,}[0-9]{5}))([0-9]{1}|[XxAa])$

And here is the check digit validation logic:

id =  # input ID number as string
id_2digits = id[0:2]  # first 2 digits 

# Calculate check digit using modulus 11 for IDs before 2000
if id_2digits <= '31': 
    check_digit = 11 - sum([(int(id[i])*((i%2)+1)) for i in range(0,9)])%11  
else: 

# Calculate check digit using modulus 10 for IDs after 2000        
check_digit = 10 - sum([(int(id[i])*(1 if i%2==0 else 2)) for i in range(0,9)])%10
if check_digit == id[-1]: 
    print("Valid ID")
else: 
    print("Invalid ID") 

Guam

Below is the National ID validation for Guam:

(^\d{3}-\d{2}-\d{4}$)|(^\d{4}$)|(^\d{9}$)

Hong Kong

Below is the National ID validation for Hong Kong:

(^[A-Za-z\d]{9}$)

India

Below is the Indian National ID validation:

(^[A-Za-z]{2}[-]\d{13}$)|(^[A-Za-z]{2}\d{2}[\s]?\d{11}$)

Indonesia

Below is the Indonesian National ID validation:

(^\d{16}$)

Ireland

Below is the Irish National ID validation:

^\d{7}[A-Za-z]{1,2}$

Italy

Below is the Italian Codice Fiscal validation:

^[A-Z]{6}[0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{2}[A-Z][0-9LMNPQRSTUV]{3}[A-Z]$

Japan

Below is the Japanese National ID validation:

(^\d{12}$)

Malaysia

Below is the Malaysian National ID validation:

(^\d{12}$)

Mexico

Below are the Mexican CURP and Mexican RFC validations:

(^[A-Za-z\d]{18}$)

Netherlands

Below is the Dutch (Netherlands) Burgerservicenummer or BSN validation:

^(?!123[0-9]{6})(?!.*(000)$)(?=[0-9]{9}(?![0-9]{2})(?: (?!20)[0-9]{2})
{3}[0-9]{3})[0-9]{9}|[0-9]{2} [0-9]{7}[0-9]{3}(?(1)(?![0-9X])|(?(2)|
[0-9X]))(3[0-1]|[1-2][0-9]0|[1-9][0-9])^(0[1-9]|[1-4][0-9]|5[0-2])[0-9]{2}$

And here is the check digit validation logic:

def luhn_checksum(bsn):
    """Calculate Luhn checksum for Dutch BSN."""
    sum = 0
    length = len(bsn)
    parity = length % 2        
    
    for i in range(length - 1, -1, -1):
        digit = int(bsn[i])
        
        if i % 2 == parity:
            digit *= 2
            
        if digit > 9:
            digit -= 9
            
        sum += digit
        
    return sum * 9 % 11

def validate_bsn(bsn):
    """Validate Dutch BSN using Luhn checksum."""
    checksum = luhn_checksum(bsn)
    if checksum == int(bsn[-1]):
        print("Valid ID")
    else:
        print("Invalid ID")

New Zealand

Below is the National ID validation for New Zealand:

^[A-Za-z0-9]+$

Nigeria

Below is the Nigerian National ID validation:

(^\d{11}$)|(^\d{8}[-\s]?0001$)

Northern Mariana Islands

Below is the National ID validation for Northern Mariana Islands:

(^\d{3}-\d{2}-\d{4}$)|(^\d{4}$)|(^\d{9}$)

Norway

Below is the Norwegian National ID validation:

(^(?:0[1-9]|[12][0-9]|3[01])(?:0[1-9]|1[0-2])\d{2}\d{5}$)

Peru

Below is the Peruvian National ID validation:

^\d{8}$

Philippines

Below is the National ID validation for Philippines:

^\d{10}$

Poland

Below is the Polish PESEL validation:

(^\\d{11}$)

Portugal

Below is the Portuguese Bilhete de Identidade (BI) National ID validation:

^([2-8][0-9A-Z]{2}|[3-8][0-9]{2})[0-9]{8}[0-9]{1}$

And here is the check digit validation logic:

# Extract components
region_code = id[0:2]
check_sum_digits = id[2:10]
check_digit = id[10:11]   

# Double check Luhn checksum
checksum1 = calculate_luhn_checksum(check_sum_digits)
checksum2 = calculate_luhn_checksum(check_sum_digits[::-1])
if checksum1 != checksum2:
    print("Invalid ID")
    return False       

# Validate check digit 
control_digit = checksum1 % 10
if check_digit != str(control_digit):
    print("Invalid ID")
    return False

# Validate region code
valid_region_codes = [list of codes]
if region_code not in valid_region_codes:
    print("Invalid ID")
    return False               
else: 
print("Valid ID")
return True  

Puerto Rico

Below is the National ID validation for Puerto Rico:

(^\d{3}-\d{2}-\d{4}$)|(^\d{4}$)|(^\d{9}$)

Romania

Below is the Romanian CNP validation:

^(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[0-1])(0[1-9]|[1-8][0-9])
(([1-9][0-9]{2})|([1-9][0-9])|(0[1-9]))((8|9|[2-3][0-9])($|[^0-9]))$
(0[1-9]|1[0-9]|2[0-9]|3[0-5]|4[0-8])[0-9]{3}$  

Singapore

Below is the Singaporean National Registration Identity Card (NRIC) validation:

^[A-Za-z]\d{7}[A-Za-z]$

OR

^[A-Za-z0-9]{9}$

South Korea

Below is the National ID validation for South Korea:

(^\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12][0-9]|3[01])[-]?\d{7}$)

South Africa

Below is the National ID validation for South Africa:

(^\d{13}$)

Spain

Below is the Spanish DNI validation:

(^\\d{8}[-\\s]?[A-Za-z]$)

Sweden

Below is the Swiss (Sweden) Personnummer National ID validation:

(^\\d{10}$) 

Thailand

Below is the National ID validation for Thailand:

(^\d{13}$)

Turkey

Below is the Turkish National ID validation:

(^\d{11}$)|(^\d{13}$)

Ukraine

Below is the Ukrainian National ID validation:

^(?:[12][01]|[321-7])[0-9]{9}$/&&id<99000000&&(id>10100000||id<21000000)
&&luhnChecksumIsValid(id)
&&((id>49999999&&id[2]<3)||regionCodes.includes(id.slice(0,3)))
&&((id[0]==1&&(id>10100000&&id<21000000))||((id<20000000||id>35000000)
&&birthYearValid(id[0])))

United Kingdom

Below are the United Kingdom National Insurance Number, National Health Service Number, and Community Health Index validations:

(^[A-Za-z]{2}\d{6}[A-Za-z]$)

United States

Below are the United States Social Security Number (SSN), Individual Taxpayer Identification Number (ITIN), and Employer Identification Number (EIN) validations:

^
(?!219099999|078051120|111221111|409522002|229229229|101010101|111222333|267941100|
111100000|111222222|010101010|232323232|001010101|234567891|221221221|121121212|
333221111|721074426|261634666|454545454|111223344|333224444|001100001|234234234|
123121231|267941000|111100111|001010011|456789123|252525252|555121212|485520189|
212111111|031101169|555667777|111221212|101101010|321321321|583299230|123123456|
111222111|110011001|333445555|111444444|123345678|123445678|555665555|500507000|
524027657|721071426|112223333|112345678|111121234|111225555|111229999|222221111|
111221234|123321123|111122222|111221122|172321176|555443333|010101011|112121212|
485345454|456123789|545454545|111223334|220786897|100101001|123546789|090090909|
215643925|532801066|212561547|452541111|500222000|123121111|555123456|458026124|
543098765|011010101|123465789|111441111|123121212|239448855|111224444|111223456|
456456456|101010100|270349290|465846552|110011111|456789012|595125274|111123456|
555121234|123121233|065687510|112222222|555551111)
(?!000|666|9\d\d)\d{3}[- ]?(?!00)\d{2}[- ]?(?!0000)\d{4}$

Vietnam

Below is the Vietnamese National ID validation:

^\d{9,12}$

Virgin Islands

Below is the National ID validation for Virgin Islands:

(^\d{3}-\d{2}-\d{4}$)|(^\d{4}$)|(^\d{9}$)

Check against government ID databases

Consider utilizing validation APIs government agencies offer to verify an ID against their records if available. This is the most reliable way to confirm the authenticity of a national ID number, and we highly recommend integrating with official validation APIs if they are available for your target countries.


Keep up-to-date with changes to ID formats

Governments may periodically update national ID formats, algorithms, and validation rules. It's important to stay informed about any announcements related to ID format changes and update your validators accordingly. Failure to keep up-to-date may result in incorrectly accepting or rejecting valid national IDs.