IBAN Validation: Complete Developer Guide (2026)
If your app handles international payments, money transfers, or banking integrations, you'll need to validate IBANs. The International Bank Account Number is used across 80+ countries to identify bank accounts for cross-border transfers.
Getting IBAN validation wrong means failed transfers, returned payments, and unhappy users. This guide shows you exactly how the validation works, with code you can drop into any project.
What's Inside an IBAN?
Every IBAN follows the same structure, regardless of country:
Country Check digits Bank code Account number
- Country code (2 letters) — ISO 3166-1 alpha-2 (DE = Germany, GB = UK, FR = France)
- Check digits (2 digits) — computed using MOD-97 to catch typos
- BBAN (Basic Bank Account Number) — the rest, format varies by country
The total length depends on the country. Germany is 22 characters, the UK is 22, France is 27, and Norway is just 15.
IBAN Lengths by Country
| Country | Code | Length | Example |
|---|---|---|---|
| Germany | DE | 22 | DE89 3704 0044 0532 0130 00 |
| United Kingdom | GB | 22 | GB29 NWBK 6016 1331 9268 19 |
| France | FR | 27 | FR76 3000 6000 0112 3456 7890 189 |
| Spain | ES | 24 | ES91 2100 0418 4502 0005 1332 |
| Netherlands | NL | 18 | NL91 ABNA 0417 1643 00 |
| Italy | IT | 27 | IT60 X054 2811 1010 0000 0123 456 |
| Switzerland | CH | 21 | CH93 0076 2011 6238 5295 7 |
| Norway | NO | 15 | NO93 8601 1117 947 |
| Poland | PL | 28 | PL61 1090 1014 0000 0712 1981 2874 |
| Belgium | BE | 16 | BE68 5390 0754 7034 |
The MOD-97 Checksum Algorithm
IBAN validation uses the MOD-97 algorithm defined in ISO 7064. Here's how it works:
- Move the first 4 characters to the end:
DE89...becomes...DE89 - Convert letters to numbers: A=10, B=11, ..., Z=35. So D=13, E=14
- Calculate the remainder when divided by 97
- If the remainder is 1, the IBAN is valid
Walk-Through: DE89370400440532013000
Step 1: Move first 4 to end 370400440532013000DE89 → 370400440532013000131489 (D=13, E=14) Step 2: MOD-97 370400440532013000131489 % 97 = 1 Result: 1 → Valid ✓
The challenge with big numbers: this is a 24+ digit number, which exceeds JavaScript's Number.MAX_SAFE_INTEGER (2^53). You need to process it in chunks.
JavaScript Implementation
function validateIBAN(input) {
// 1. Clean and uppercase
const iban = input.replace(/\s/g, '').toUpperCase();
// 2. Basic format check
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{4,30}$/.test(iban)) {
return { valid: false, reason: 'Invalid IBAN format' };
}
// 3. Check country-specific length
const lengths = {
DE: 22, GB: 22, FR: 27, ES: 24, IT: 27, NL: 18,
BE: 16, AT: 20, CH: 21, SE: 24, NO: 15, DK: 18,
FI: 18, PL: 28, PT: 25, IE: 22, LU: 20, CZ: 24,
// ... more countries
};
const country = iban.substring(0, 2);
if (lengths[country] && iban.length !== lengths[country]) {
return { valid: false, reason: `${country} IBAN must be ${lengths[country]} characters` };
}
// 4. MOD-97 check
// Move first 4 chars to end
const rearranged = iban.substring(4) + iban.substring(0, 4);
// Convert letters to numbers
let numeric = '';
for (const char of rearranged) {
if (char >= 'A' && char <= 'Z') {
numeric += (char.charCodeAt(0) - 55).toString();
} else {
numeric += char;
}
}
// MOD-97 on large number (process in chunks)
let remainder = 0;
for (let i = 0; i < numeric.length; i++) {
remainder = (remainder * 10 + parseInt(numeric[i])) % 97;
}
if (remainder !== 1) {
return { valid: false, reason: 'Checksum failed (MOD-97)' };
}
return {
valid: true,
country,
check_digits: iban.substring(2, 4),
bban: iban.substring(4),
formatted: iban.replace(/(.{4})/g, '$1 ').trim(),
};
}
validateIBAN('DE89 3704 0044 0532 0130 00');
// { valid: true, country: "DE", check_digits: "89",
// bban: "370400440532013000",
// formatted: "DE89 3704 0044 0532 0130 00" }
The key trick is the chunk-based MOD-97 on line 33-36. Instead of converting the entire string to a BigInt, we process digit by digit, taking the modulus at each step. This keeps the number small and works in any JavaScript environment.
Try it free →
Extracting the Bank Code
The BBAN contains the bank code, but its position varies by country:
| Country | Bank Code Position | Example |
|---|---|---|
| Germany (DE) | Characters 5-12 (8 digits) | 37040044 |
| UK (GB) | Characters 5-8 (4 letters) | NWBK |
| France (FR) | Characters 5-9 (5 digits) | 30006 |
| Netherlands (NL) | Characters 5-8 (4 letters) | ABNA |
| Spain (ES) | Characters 5-8 (4 digits) | 2100 |
function extractBankCode(iban, country) {
const bankCodeLengths = {
DE: 8, GB: 4, FR: 5, NL: 4, ES: 4,
IT: 5, BE: 3, AT: 5, CH: 5, SE: 3,
};
const len = bankCodeLengths[country];
if (!len) return null;
return iban.substring(4, 4 + len);
}
Common Mistakes
1. Not handling spaces
IBANs are often displayed with spaces (DE89 3704 0044...) but stored without them. Always strip spaces before validation.
2. Case sensitivity
IBANs can be entered in lowercase. Always convert to uppercase before processing — the letter-to-number conversion depends on it.
3. Skipping the length check
An IBAN that passes MOD-97 but has the wrong length for its country is still invalid. A German IBAN must be exactly 22 characters — no more, no less.
4. Using parseInt on the full number
parseInt("370400440532013000131489") loses precision because it exceeds 2^53. Always use the chunk method or BigInt.
Using the DataCheck API
If you'd rather not maintain country-specific length tables and bank code extraction logic:
const res = await fetch(
'https://datacheck.dev/api/validate?input=DE89370400440532013000&type=iban'
);
const data = await res.json();
// {
// valid: true,
// formatted: "DE89 3704 0044 0532 0130 00",
// country: "DE",
// details: {
// country: "Germany",
// check_digits: "89",
// bban: "370400440532013000",
// bank_code: "37040044"
// }
// }
Test IBANs for Development
| Country | Valid Test IBAN |
|---|---|
| Germany | DE89 3704 0044 0532 0130 00 |
| UK | GB29 NWBK 6016 1331 9268 19 |
| France | FR76 3000 6000 0112 3456 7890 189 |
| Spain | ES91 2100 0418 4502 0005 1332 |
| Netherlands | NL91 ABNA 0417 1643 00 |
| Switzerland | CH93 0076 2011 6238 5295 7 |
| Belgium | BE68 5390 0754 7034 |
| Norway | NO93 8601 1117 947 |
Wrapping Up
IBAN validation comes down to three checks: format (letters + digits), country-specific length, and MOD-97 checksum. The tricky part is the big-number arithmetic, which the chunk-based approach solves cleanly.
For apps that just need to verify IBANs and extract bank codes, the 40-line implementation above works perfectly. For production use with 45+ countries and always-current formats, consider using an API.
Get your free API key →