Regex for Indian ID & Document Validation

Every major Indian identity document — Aadhaar, PAN, GSTIN, IFSC, passport, phone number, PIN code, driving license — has a fixed, documented format, which makes them some of the most regex-friendly IDs in the world. But they split into two genuinely different problems: some formats are protected by a checksum digit that regex cannot verify, and some are pure format, where a regex match is the whole validation. Confusing the two is the single most common mistake in KYC and signup forms — this guide draws the line clearly, then gives you a tested pattern for each.

Two kinds of Indian ID formatsChecksum-protectedregex confirms shape only• Aadhaar (Verhoeff)• PAN (check letter)• GSTIN (check char)• IFSC (fixed 5th digit)Format-onlyregex IS the validation• Passport• Mobile phone• PIN code• Driving license

All 8 formats at a glance

Vehicle registration numbers get their own dedicated guide since the RTO-series and newer BH-series formats need more room than a table row — everything else is here.

IDLengthChecksum?Pattern
Aadhaar12 digitsYes (Verhoeff)/^[2-9][0-9]{11}$/
PAN10 charsYes (check letter)/^[A-Z]{5}[0-9]{4}[A-Z]$/
GSTIN15 charsYes (check char)/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/
IFSC11 charsNo (fixed 5th char)/^[A-Z]{4}0[A-Z0-9]{6}$/
Phone10–13 charsNo/^(?:\+91|91)?[6-9][0-9]{9}$/
PIN code6 digitsNo/^[1-9][0-9]{5}$/
Passport8 charsNo/^[A-Z][0-9]{7}$/
Driving license15–16 charsNo/^[A-Z]{2}[0-9]{2}[ -]?(19|20)[0-9]{2}[0-9]{7}$/

Checksum-protected IDs

These four all embed a check character — a digit or letter mathematically derived from the rest of the number. Regex confirms the shape is right; it cannot confirm the check character is mathematically valid, so a random string that happens to fit the pattern will still pass.

Aadhaar (12 digits)

UIDAI never issues an Aadhaar number starting with 0 or 1, and the last digit is a Verhoeff-algorithm checksum — a scheme specifically designed to catch transposed and adjacent-digit typos, which is why UIDAI uses it instead of a simple digit sum.

const aadhaarRegex = /^[2-9][0-9]{11}$/;
aadhaarRegex.test('234567890123'); // true — shape is valid
// A regex pass here does NOT mean the number was ever issued.

PAN (10 characters)

The 4th letter isn't random — it encodes the holder type (P for an individual, C for a company, H for a Hindu Undivided Family, and several others), so ABCPD1234E and ABCCD1234E are structurally different kinds of PAN, not just different numbers.

const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
panRegex.test('ABCPD1234E'); // true

GSTIN (15 characters)

A GSTIN is the most structured Indian ID because it isn't really its own identifier — it's a 2-digit state code (01 for Jammu & Kashmir through 38 for Ladakh) wrapped around a full 10-character PAN, followed by an entity count, the fixed letter Z, and a checksum. Example: 27AAPFU0939F1ZV.

const gstinRegex = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/;
gstinRegex.test('27AAPFU0939F1ZV'); // true
gstinRegex.test('27AAPFU0939F1XV'); // false — 14th char must be Z

IFSC (11 characters)

Grouped here for its fixed 5th character rather than a true mathematical checksum: RBI reserves that position as the digit 0 for every bank, so it acts as a structural sanity check even though it isn't derived from the surrounding characters.

const ifscRegex = /^[A-Z]{4}0[A-Z0-9]{6}$/;
ifscRegex.test('HDFC0001234'); // true

Format-only IDs

No embedded checksum in any of these four — a regex match genuinely is a complete structural validation. What still varies is exactly which characters are legal in which position, which is where copy-pasted "close enough" patterns tend to go wrong.

Mobile phone (10–13 characters)

India's Department of Telecommunications only allocates mobile numbers starting with 6, 7, 8, or 9 — a number starting with 5 or below is never a valid Indian mobile number, regardless of length.

const phoneRegex = /^(?:\+91|91)?[6-9][0-9]{9}$/;
phoneRegex.test('+919876543210'); // true
phoneRegex.test('9876543210');    // true
phoneRegex.test('5876543210');    // false — DoT never allocates a leading 5

PIN code (6 digits)

The first digit is the postal zone (1 through 9 — there is no zone 0), the second is the sub-zone, the third is the sorting district, and the last three identify the specific post office.

const pinRegex = /^[1-9][0-9]{5}$/;
pinRegex.test('110001'); // true — New Delhi GPO

Passport (8 characters)

The simplest format on this list — one uppercase letter, seven digits, nothing more.

const passportRegex = /^[A-Z][0-9]{7}$/;
passportRegex.test('A1234567'); // true

Driving license (15–16 characters)

The only ID here without one nationwide format — the state code (MH, DL, KA, and so on), RTO code, and separator vary enough by issuing state that this pattern should be treated as "matches the common case," not a guarantee.

const dlRegex = /^[A-Z]{2}[0-9]{2}[ -]?(19|20)[0-9]{2}[0-9]{7}$/;
dlRegex.test('MH1220230012345'); // true

One validator for all eight

Instead of eight separate copy-pasted functions, a single dispatcher keeps every pattern in one place — easier to update if a format changes, and it forces you to name the ID type explicitly at every call site instead of guessing from context.

const INDIAN_ID_PATTERNS = {
  aadhaar: /^[2-9][0-9]{11}$/,
  pan: /^[A-Z]{5}[0-9]{4}[A-Z]$/,
  gstin: /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/,
  ifsc: /^[A-Z]{4}0[A-Z0-9]{6}$/,
  phone: /^(?:\+91|91)?[6-9][0-9]{9}$/,
  pinCode: /^[1-9][0-9]{5}$/,
  passport: /^[A-Z][0-9]{7}$/,
  drivingLicense: /^[A-Z]{2}[0-9]{2}[ -]?(19|20)[0-9]{2}[0-9]{7}$/,
};

function isValidIndianId(type, value) {
  const pattern = INDIAN_ID_PATTERNS[type];
  if (!pattern) throw new Error(`Unknown ID type: ${type}`);
  return pattern.test(String(value).trim().toUpperCase());
}

isValidIndianId('pan', 'abcpd1234e');      // true
isValidIndianId('gstin', '27AAPFU0939F1ZV'); // true
isValidIndianId('aadhaar', '123456789012'); // false — starts with 1

Common Mistakes

  • Treating a regex pass as proof the ID is real. For Aadhaar, PAN, GSTIN, and IFSC, a regex match is a necessary but not sufficient check — the checksum (or fixed digit) still needs separate verification if it matters for your use case.
  • Forgetting to normalize case before testing. All eight formats use uppercase letters; users routinely type PAN or IFSC in lowercase, so .toUpperCase() before testing avoids false negatives.
  • Not trimming whitespace. Copy-pasted Aadhaar numbers often carry the UIDAI-style spacing (2345 6789 0123) — strip spaces before testing, don't bake them into the regex unless you specifically want to accept that format.
  • Assuming one driving license format covers every state. The pattern here matches the common case; some states use different separators or field orders, so treat a non-match as "needs manual review," not "definitely invalid."
  • Skipping the DoT leading-digit rule for phone numbers. ^[0-9]{10}$ alone accepts numbers starting with 0–5, which no telecom operator has ever issued as a mobile number.

Frequently Asked Questions

Can regex fully validate an Aadhaar, PAN, or GSTIN number?

No. Regex confirms the shape — length, and which characters are allowed where — but Aadhaar, PAN, and GSTIN all end in a checksum computed by an algorithm regex cannot run. A fabricated number that happens to fit the pattern will still pass. Passport, phone, and PIN code have no checksum, so for those, regex format validation is the whole job.

What is the regex pattern for a GSTIN?

^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$ — a 2-digit state code, a full 10-character PAN embedded inside it, an entity number, the fixed letter Z, and a checksum character.

How do I validate an Indian phone number with regex?

^(?:\+91|91)?[6-9][0-9]{9}$ — an optional +91 or 91 prefix, then 10 digits starting with 6, 7, 8, or 9, the only leading digits DoT allocates for mobile numbers.

Does a valid-looking GSTIN or PAN mean the business or person is real?

No. Even checksum validation only confirms the number is well-formed, not that it was actually issued or is still active. For anything transaction-critical, confirm status against the official GST Portal or a PAN verification API instead of trusting format validation alone.

Need a pattern for a custom ID or compliance field?

Describe the format in plain English and get a tested regex instantly with the free Dev Brains AI Regex Generator — no signup required.

Related articles