IBAN Regex Patterns That Actually Work (And Why Simple Regex Isn't Enough)
Country-specific regex patterns for DE, GB, FR, NL plus a generic IBAN regex — and why regex is only step 1 of validation, not the full solution.
Technical Support, ibanchecker.cash
IBAN regex patterns are useful as a first-pass filter, they can quickly reject strings that are obviously not IBANs. But regex alone cannot validate an IBAN. The ISO 13616 check digit algorithm (MOD-97) requires integer arithmetic that no regular expression can perform. This guide covers the patterns that actually work, where they fall short, and how to combine them correctly with a check digit calculation.
Why a Simple Regex Is Never Enough
Consider this string: DE89370400440532013001. It is 22 characters, starts with a valid country code, and contains only alphanumeric characters. Every regex you could write would accept it. But it is invalid, the last digit has been changed from 0 to 1, which breaks the MOD-97 check.
The check digits at positions 3–4 of every IBAN encode a mathematical commitment to the rest of the string. Verifying them requires computing int(rearrangedString) % 97 and confirming the result is 1. No regex engine evaluates arithmetic, this is a fundamental limitation of the formalism, not a practical workaround.
Regex is step 1 of IBAN validation, not the full solution. Use it to reject malformed strings early and cheaply. Then compute MOD-97 on the survivors.
Generic IBAN Regex (All Countries)
A generic pattern that matches any structurally plausible IBAN across all 92 supported countries:
// JavaScript
const IBAN_GENERIC = /^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/;
# Python
import re
IBAN_GENERIC = re.compile(r'^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$')This pattern requires:
- Exactly 2 uppercase letters (country code)
- Exactly 2 digits (check digits)
- 11 to 30 alphanumeric characters (BBAN, varies by country)
Total length range: 15 (Norway, NO15) to 34 (Saint Lucia, LC32). The generic regex captures this range. It will also accept structurally plausible strings with invalid country codes or wrong lengths, country-specific patterns tighten this.
Always strip whitespace and convert to uppercase before applying any regex:
// JavaScript
function preprocess(raw) {
return raw.replace(/[s-]/g, '').toUpperCase();
}
# Python
def preprocess(raw: str) -> str:
return raw.replace(' ', '').replace('-', '').upper()Country-Specific Regex Patterns
Country-specific patterns constrain the total length and, in some cases, the internal structure. The BBAN format (the national portion after the check digits) varies by country.
Germany (DE), 22 Characters
German IBANs contain only digits in the BBAN. The Bankleitzahl (8 digits) and account number (10 digits) are all numeric.
// JavaScript
const DE_IBAN = /^DE[0-9]{20}$/;
# Python
DE_IBAN = re.compile(r'^DE[0-9]{20}$')
// Valid: DE89370400440532013000
// Invalid: DE89370400440532013X00 (letter in BBAN)United Kingdom (GB), 22 Characters
GB IBANs have a 4-letter bank code followed by a 6-digit sort code and 8-digit account number, all defined structure.
// JavaScript
const GB_IBAN = /^GB[0-9]{2}[A-Z]{4}[0-9]{14}$/;
# Python
GB_IBAN = re.compile(r'^GB[0-9]{2}[A-Z]{4}[0-9]{14}$')
// Valid: GB29NWBK60161331926819
// Invalid: GB29NW1K60161331926819 (digit in bank code)France (FR), 27 Characters
French IBANs embed a 5-digit bank code, 5-digit branch code, 11-character account number, and 2-digit national check digits. The account characters can be alphanumeric.
// JavaScript
const FR_IBAN = /^FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/;
# Python
FR_IBAN = re.compile(r'^FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$')
// Valid: FR7630006000011234567890189Netherlands (NL), 18 Characters
Dutch IBANs have a 4-letter bank code followed by exactly 10 digits.
// JavaScript
const NL_IBAN = /^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/;
# Python
NL_IBAN = re.compile(r'^NL[0-9]{2}[A-Z]{4}[0-9]{10}$')
// Valid: NL91ABNA0417164300Multi-Country Dispatch Pattern
In JavaScript, you can build a lookup table of country-specific patterns and fall back to the generic pattern for countries not explicitly listed:
const COUNTRY_PATTERNS = {
DE: /^DE[0-9]{20}$/,
GB: /^GB[0-9]{2}[A-Z]{4}[0-9]{14}$/,
FR: /^FR[0-9]{2}[0-9]{5}[0-9]{5}[A-Z0-9]{11}[0-9]{2}$/,
NL: /^NL[0-9]{2}[A-Z]{4}[0-9]{10}$/,
AT: /^AT[0-9]{18}$/,
CH: /^CH[0-9]{19}$/,
ES: /^ES[0-9]{22}$/,
IT: /^IT[0-9]{2}[A-Z][0-9]{10}[A-Z0-9]{12}$/,
BE: /^BE[0-9]{14}$/,
PL: /^PL[0-9]{26}$/,
};
const IBAN_GENERIC = /^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/;
function matchesStructure(iban) {
const country = iban.slice(0, 2);
const pattern = COUNTRY_PATTERNS[country] ?? IBAN_GENERIC;
return pattern.test(iban);
}Regex as Step 1: The Full Validation Pipeline
Here is the complete two-step pipeline in JavaScript. Regex gates the MOD-97 computation , no point running the arithmetic on obvious garbage.
const IBAN_LENGTHS = {
AD: 24, AE: 23, AL: 28, AT: 20, AZ: 28,
BA: 20, BE: 16, BG: 22, BH: 22, BR: 29,
BY: 28, CH: 21, CR: 22, CY: 28, CZ: 24,
DE: 22, DK: 18, DO: 28, EE: 20, EG: 29,
ES: 24, FI: 18, FO: 18, FR: 27, GB: 22,
GE: 22, GI: 23, GL: 18, GR: 27, GT: 28,
HR: 21, HU: 28, IE: 22, IL: 23, IQ: 23,
IS: 26, IT: 27, JO: 30, KW: 30, KZ: 20,
LB: 28, LC: 32, LI: 21, LT: 20, LU: 20,
LV: 21, LY: 25, MC: 27, MD: 24, ME: 22,
MK: 19, MR: 27, MT: 31, MU: 30, NL: 18,
NO: 15, PK: 24, PL: 28, PS: 29, PT: 25,
QA: 29, RO: 24, RS: 22, SA: 24, SC: 31,
SE: 24, SI: 19, SK: 24, SM: 27, ST: 25,
SV: 28, TL: 23, TN: 24, TR: 26, UA: 29,
VA: 22, VG: 24, XK: 20,
};
function validateIban(raw) {
const iban = raw.replace(/[s-]/g, '').toUpperCase();
// Step 1a: format check
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(iban)) {
return { valid: false, error: 'INVALID_FORMAT' };
}
// Step 1b: country + length check
const country = iban.slice(0, 2);
const expected = IBAN_LENGTHS[country];
if (!expected) return { valid: false, error: 'UNKNOWN_COUNTRY' };
if (iban.length !== expected) return { valid: false, error: 'WRONG_LENGTH' };
// Step 2: MOD-97 check digit (cannot be done with regex)
const rearranged = iban.slice(4) + iban.slice(0, 4);
const numeric = rearranged.replace(/[A-Z]/g, c => String(c.charCodeAt(0) - 55));
// BigInt required, the number is too large for Number
if (BigInt(numeric) % 97n !== 1n) {
return { valid: false, error: 'INVALID_CHECK_DIGITS' };
}
return { valid: true };
}Notice the BigInt requirement for the MOD-97 step. The numeric string for a 32-character IBAN (Saint Lucia) can be 50+ digits, well beyond JavaScript's safe integer range. Always use BigInt in JavaScript, or Python's native arbitrary-precision integers.
What Regex Cannot Do
To be explicit about the limits of pattern matching for IBAN validation:
- Cannot verify check digits. This is the primary limitation, a regex that accepts all valid IBANs also accepts most invalid ones with a single character changed.
- Cannot verify bank existence. An IBAN can pass MOD-97 and reference a bank code that does not exist. Bank data lookup requires a BIC database.
- Cannot verify account existence. No client-side technique can confirm that the account is open and funded, that requires the bank's systems.
For full validation including bank metadata, use the ibanchecker.cash API, it performs all three layers: format, MOD-97, and BIC database lookup, and returns the bank name and BIC alongside the validity result.
Sources & References
- SWIFT IBAN Registry — Official IBAN country format specifications
- ISO 13616-1:2020 — International standard for IBAN structure
- ISO 7064 — MOD-97-10 check digit algorithm standard
Last updated: June 2026
Integrate IBAN validation via API
REST API with 1,000 free validations/month. JSON response with bank name, SEPA status, and BBAN breakdown.
How to Validate IBANs in JavaScript (API & Manual)
Two approaches: call the ibanchecker.cash API for full bank data, or implement MOD-97 validation yourself. Includes work...
IBAN Validation in Python: Complete Implementation with Regex and Check Digits
Two production-ready approaches: call the ibanchecker.cash API for bank metadata, or implement the MOD-97 algorithm your...
Building an IBAN Validation API: REST Endpoint Design and Edge Cases
Architect-level guide to IBAN API design — GET vs POST tradeoffs, error code standards, normalization contracts, KV cach...
Top IBAN Validation Libraries for Node.js, Python, PHP, and Go in 2026
Comparison of ibantools, python-stdnum, php-iban, and go-iban — features, MOD-97 support, BIC lookup, TypeScript support...