IBAN Check Digit Validation: The MOD-97 Algorithm Explained
A step-by-step walkthrough of the ISO 13616 MOD-97-10 algorithm — how it detects transcription errors, why it works, and production-ready JavaScript and Python implementations.
The MOD-97-10 algorithm is the mathematical backbone of every IBAN. It is defined in ISO 13616 and ISO 7064, and it is what allows any bank, payment processor, or piece of software to detect a mistyped digit in an IBAN without ever contacting the account holder's bank. This guide walks through every step of the algorithm with a worked example and production-ready code.
Why Does IBAN Validation Need a Check Algorithm?
An IBAN can contain up to 34 characters. Copy it from a PDF, type it from a paper invoice, or key it into an online banking form — and the chance of a transcription error is real. The check digit system ensures that virtually any single-character error produces a number that fails the algorithm instantly, before the payment is even submitted.
The MOD-97 check catches all adjacent transpositions (swapping two neighbouring digits) and approximately 98% of single-digit substitutions. The remaining 2% that slip through are cases where a wrong digit accidentally produces the same mod-97 remainder — statistically rare and spread across different positions.
The Four-Step MOD-97 Algorithm
Step 1 — Strip Spaces and Uppercase
Remove all spaces from the IBAN and convert to uppercase. IBANs are often written in groups of four for readability, but the algorithm operates on the compact form.
Input: GB29 NWBK 6016 1331 9268 19
Compact: GB29NWBK60161331926819Step 2 — Move the First Four Characters to the End
Take the first four characters (the country code and the two check digits) and append them to the right of the remaining string.
GB29NWBK60161331926819
→ NWBK60161331926819GB29Step 3 — Convert Letters to Numbers
Replace each uppercase letter with its integer equivalent: A = 10, B = 11, C = 12 … Z = 35. Digits stay as-is. Concatenate the result into one long integer string.
N=23, W=32, B=11, K=20
G=16, B=11
NWBK60161331926819GB29
→ 23321120 60161331926819 1611 29
→ 23321120601613319268191611 29Step 4 — Compute the Remainder mod 97
Divide the resulting integer by 97. A valid IBAN always produces a remainder of 1. Any other remainder means the IBAN is invalid.
2332112060161331926819161129 mod 97 = 1 ✓Because the number can be over 30 digits long — too large for a 64-bit integer in many languages — use a chunk-based modulo approach (see code below).
How Check Digits Are Assigned at Account Creation
When a bank creates a new IBAN, it does the same steps but temporarily substitutes 00 for the check digits, then computes 98 − (number mod 97). The result becomes the check digits. This guarantees that when you later run MOD-97 on the completed IBAN, the remainder is exactly 1.
Placeholder: GB00NWBK60161331926819
Rearranged: NWBK60161331926819GB00
Numeric: 232311206016133192681916110 0
Mod 97: 72
Check digits: 98 − 72 = 26 → "26" would be the check digits
(GB29 uses different account data; 29 = 98 − (its rearranged mod 97))JavaScript Implementation
function validateIBAN(iban) {
const cleaned = iban.replace(/\s+/g, "").toUpperCase();
const rearranged = cleaned.slice(4) + cleaned.slice(0, 4);
const numeric = rearranged.replace(/[A-Z]/g, (c) =>
(c.charCodeAt(0) - 55).toString()
);
let remainder = 0;
for (let i = 0; i < numeric.length; i++) {
remainder = (remainder * 10 + parseInt(numeric[i], 10)) % 97;
}
return remainder === 1;
}
console.log(validateIBAN("GB29 NWBK 6016 1331 9268 19")); // true
console.log(validateIBAN("GB00 NWBK 6016 1331 9268 19")); // falseThe chunk loop processes one digit at a time, keeping the running remainder small enough for standard integer arithmetic. This avoids BigInt or arbitrary-precision libraries on most platforms.
Python Implementation
def validate_iban(iban: str) -> bool:
cleaned = iban.replace(" ", "").upper()
rearranged = cleaned[4:] + cleaned[:4]
numeric = "".join(
str(ord(c) - 55) if c.isalpha() else c for c in rearranged
)
return int(numeric) % 97 == 1
print(validate_iban("GB29 NWBK 6016 1331 9268 19")) # True
print(validate_iban("GB29 NWBK 6016 1331 9268 20")) # FalsePython handles arbitrarily large integers natively, so no chunking is needed. The single int(numeric) % 97 call works regardless of IBAN length.
What the Algorithm Does NOT Check
MOD-97 is a structural integrity check, not a reachability check. It tells you the IBAN was formed correctly — not that the account exists, is active, or belongs to the person you think you are paying. A structurally valid IBAN can still be:
- An account that has been closed
- A valid IBAN belonging to a different person (BEC fraud)
- An IBAN from a sanctioned entity that passes format validation
For payment fraud prevention, always validate with the ibanchecker.cash validator, which additionally returns the bank name, BIC, and SEPA status — giving you enough information to verify the receiving institution matches what the payee told you.
Testing Corner Cases
When implementing MOD-97 validation, test these specific inputs:
- Adjacent digit transposition: swap two neighbouring digits in a valid IBAN — must return invalid.
- Single digit substitution: change any one digit by 1 — must return invalid in ~98% of cases.
- Check digits 00 and 01: these are reserved by ISO 13616 and never assigned; a valid implementation should treat them as invalid.
- Alphabetic BBAN: UK, French, and Italian IBANs contain letters in the BBAN — verify your letter-to-number conversion handles every character A–Z.
- Maximum-length IBAN: some countries reach 34 characters; test that your chunk loop handles the full length without integer overflow.
Last updated: June 2026
Validate an IBAN instantly
Free IBAN checker — MOD-97 verification, bank lookup, and SEPA status across 84 countries.
Open IBAN Checker →Related Articles