Every IBAN carries two check digits in positions 3 and 4, computed with the ISO 7064 mod-97-10 algorithm. They exist so that a mistyped IBAN is rejected instantly at the form, instead of becoming a misdirected payment. Here is exactly how the algorithm works, with a worked example and the traps that break first implementations.
The algorithm
To validate an IBAN:
- Rearrange: move the first four characters (country code + check digits) to the end of the string.
- Convert letters to numbers: replace each letter with its position in the alphabet plus 9, so A=10, B=11, … Z=35. Digits stay as they are.
- Interpret the result as one big integer and compute it modulo 97.
- Valid if the remainder is 1.
To generate check digits: set them to 00, do the same rearrangement and conversion, compute the remainder, and use 98 - remainder, left-padded to two digits.
Worked example
Take the published registry example GB29 NWBK 6016 1331 9268 19 (remove spaces first).
- Rearranged:
NWBK60161331926819+GB29. - Letter conversion: N=23, W=32, B=11, K=20, G=16, B=11, so the string becomes
23321120 60161331926819 1611 29(spaces added here only for readability):2332112060161331926819161129. - That 28-digit number modulo 97 equals 1, so the IBAN is valid.
Pseudocode
function validateIban(iban):
s = uppercase(removeSpaces(iban))
if not matchesCountryFormat(s): return false # length + per-country pattern
r = s[4:] + s[0:4] # rearrange
n = ""
for ch in r:
n += isLetter(ch) ? str(ord(ch) - ord('A') + 10) : ch
return mod97(n) == 1
function mod97(digits): # chunked modulo, no bigint needed
rem = 0
for chunk in splitIntoChunksOf(digits, 7):
rem = int(str(rem) + chunk) % 97
return rem
Pitfalls
- Big-integer overflow. The converted number easily exceeds 30 digits; a 64-bit integer silently overflows for most countries. Use the chunked modulo trick above (process 7-9 digits at a time, carrying the remainder) or a bigint type.
- Letter mapping errors. A=10, not 1 and not 65. Off-by-one and raw-ASCII bugs both produce validators that reject every real IBAN.
- Forgetting the rearrangement. Computing mod 97 over the string as written gives garbage.
- Skipping the format check. Mod-97 alone accepts wrong-length strings for the country, or letters where the national format requires digits. Always check length and the country pattern first.
- Lowercase and whitespace. Normalise before validating; users paste
de89 3704...with spaces.
National check digits are a separate layer
Many countries embed an additional checksum inside the BBAN: France’s RIB key, Belgium’s national mod-97 pair, Italy’s CIN letter, Hungary’s two weighted digits, Czechia’s modulo-11 rules. The ISO check digits are computed over these but do not verify them. Full validation for such countries means two independent checks. And even then, remember what checksums cannot tell you: a fully valid IBAN may correspond to no account at all, which is why the EU’s Verification of Payee name-matching exists.