IBAN Checksums Explained: The MOD-97 Algorithm

IBAN Checksums Explained: The MOD-97 Algorithm

Learn exactly how IBAN checksum digits are calculated with the MOD-97 algorithm, why they catch typos, and how to compute and validate them in code.

Written by Random IBAN Team · Published on 2025-01-15 · Updated on 2026-07-18
#IBAN checksum #modulo 97 #IBAN validation #IBAN algorithm #IBAN control digits #ISO 7064 #check digits

Every International Bank Account Number carries two small digits that do a surprisingly large amount of work. They sit right after the country code — positions three and four — and they are the IBAN's built-in error detector. Get one digit wrong anywhere in the account number and, most of the time, those two checksum digits no longer add up. A payment form can then reject the value before submission; the checksum does not itself control whether a bank processes a payment.

This guide explains how those checksum digits are calculated, why the modulo 97 step is so effective, and how to compute or verify them reliably in your own systems.


What the IBAN Checksum Actually Is

The two digits after the country code are formally called the check digits. They are not random and they are not part of your account number — they are derived from the rest of the IBAN. ISO 13616-1 defines the IBAN structure, and the check digits use the ISO/IEC 7064 MOD97-10 scheme.

The whole point is integrity. When a customer types an IBAN into a payment form, fingers slip: a 3 becomes an 8, two adjacent digits get swapped, a character is dropped. The check digits let any bank — or your own validation code — detect those mistakes instantly, without contacting the destination bank.

Think of it like a parity bit on a much larger scale: a compact fingerprint of the account reference that travels alongside the data it protects.


How the Checksum Calculation Works

The validation routine is deceptively simple. Take a French IBAN such as FR76 3000 6000 0112 3456 7890 189. To check it:

  1. Move the first four characters to the end. FR76 goes to the back, leaving 30006000011234567890189FR76.
  2. Replace every letter with two digits. Each letter maps to a number: A=10, B=11, … Z=35. So F=15 and R=27, turning FR76 into 152776.
  3. Read the result as one big integer and compute integer mod 97.
  4. If the remainder is exactly 1, the value passes the IBAN checksum. Any other remainder means the check fails. A remainder of 1 does not verify the domestic bank code, account assignment, ownership, or reachability.

That is the entire algorithm. The genius is in step 4: the check digits are chosen, when the IBAN is first created, precisely so that this remainder comes out to 1.

Calculating the Check Digits From Scratch

Generating a brand-new IBAN runs the process in reverse:

  1. Assemble the BBAN (the country-specific bank code, branch, and account number).
  2. Append the country code followed by 00 as a placeholder for the check digits.
  3. Convert letters to numbers and compute the big integer.
  4. Calculate 98 - (integer mod 97).
  5. Zero-pad the result to two digits — that is your check digit pair.

For example, calculating the check digits for the German-format BBAN in DE89 3704 0044 0532 0130 00 produces 89. The Random IBAN generator follows the configured character pattern for each supported country and derives check digits so the result passes MOD-97. Curated bank-code lists and domestic checksum rules are used only where implemented; whether a bank code is current, whether an account is assigned, and whether a provider sandbox accepts the value are not verified.


Why Modulo 97 (and Not Something Simpler)?

Designers could have used a basic checksum like "sum all the digits." They chose modulo 97 deliberately, and the choice has real consequences for accuracy.

  • 97 is prime. Using a prime modulus spreads errors evenly and prevents the blind spots that plague non-prime schemes.
  • It catches every single-digit error. Change any one digit and the remainder changes — guaranteed.
  • It catches almost all transpositions. Swapping two adjacent characters (a classic typo) is detected in the overwhelming majority of cases.
  • Under a simple uniform-random-error model, an incorrect value passes with probability 1/97, giving an estimated detection rate of about 98.97%. Real typo patterns can differ, but this is still far higher than a simple sum or a single check digit could achieve.

The trade-off is that you need big-integer arithmetic, because a 34-character IBAN expands to a number with dozens of digits. In practice this is a non-issue: every mainstream language handles it natively or with a one-line library, and there is a piece-wise trick (below) that avoids big integers entirely.


Computing the Checksum in Code

You rarely need a big-integer type. Because of how modular arithmetic works, you can process the IBAN in small chunks and keep a running remainder. Here is the canonical approach in a few languages.

JavaScript:

function isValidIban(iban) {
  const s = iban.replace(/\s+/g, '').toUpperCase();
  // Move first 4 chars to the end
  const rearranged = s.slice(4) + s.slice(0, 4);
  // Convert letters to numbers (A=10 ... Z=35)
  const numeric = rearranged.replace(/[A-Z]/g, ch =>
    (ch.charCodeAt(0) - 55).toString()
  );
  // Piece-wise mod 97 to avoid BigInt
  let remainder = 0;
  for (const digit of numeric) {
    remainder = (remainder * 10 + Number(digit)) % 97;
  }
  return remainder === 1;
}

Python:

def is_valid_iban(iban: str) -> bool:
    s = "".join(iban.split()).upper()
    rearranged = s[4:] + s[:4]
    numeric = "".join(
        str(ord(c) - 55) if c.isalpha() else c
        for c in rearranged
    )
    return int(numeric) % 97 == 1   # Python ints are arbitrary-precision

PHP:

function isValidIban(string $iban): bool {
    $s = strtoupper(preg_replace('/\s+/', '', $iban));
    $rearranged = substr($s, 4) . substr($s, 0, 4);
    $numeric = '';
    foreach (str_split($rearranged) as $ch) {
        $numeric .= ctype_alpha($ch) ? (string)(ord($ch) - 55) : $ch;
    }
    // bcmod handles the large integer safely
    return bcmod($numeric, '97') === '1';
}

All three follow the same four steps. The only real difference is how each language handles the large number — Python uses native big integers, PHP leans on bcmod, and JavaScript sidesteps the problem with the running-remainder loop.


Validate Early, Validate Twice

Catching a bad IBAN at the right moment saves everyone pain. A few practices that consistently pay off:

  • Validate client-side first. Run the MOD-97 check in the browser as the user types or on blur, so they fix the typo immediately instead of after submitting.
  • Re-validate on the server. Never trust the client alone — repeat the check on the backend to guard against tampering and scripted requests.
  • Normalise before you check. Strip spaces, uppercase everything, and reject characters outside A–Z and 0–9 before running the algorithm.
  • Separate format errors from checksum errors in your messaging. "This IBAN is too short for Germany" is far more useful to a customer than a generic "invalid IBAN."

Remember the limit of the checksum: it confirms that the value is internally consistent under MOD-97, not that the account exists. A checksum-valid IBAN can still point to a closed or non-existent account. For that, you need account verification with the bank or a dedicated service.


Generating Checksum-Valid IBAN Fixtures for Tests

Hard-coding a single IBAN into your test suite is fragile, and using a real one is a compliance risk. Instead, generate checksum-valid synthetic IBANs on demand.

When you create numbers with Random IBAN, every value is built with the MOD-97 method specified for IBAN check digits by ISO/IEC 7064 MOD97-10 and the IBAN structure defined by ISO 13616-1, so it passes that checksum layer. A library that also checks current bank directories, provider-specific rules, or unsupported domestic checks may still reject it. Use the values to:

  • Unit-test your validation functions with known-good and known-bad inputs.
  • QA bulk imports by exporting up to 100 IBANs to CSV, JSON or TXT from the web generator.
  • Demo payment-form journeys with synthetic input that is blocked from live payment rails.

Pair this with the IBAN validator to confirm your own implementation agrees with the reference algorithm, and re-check your fixtures whenever a country updates its IBAN format. For deeper background on the standard itself, see our guide on what an IBAN number is.


FAQ

Why are there always exactly two check digits?

The IBAN standard fixes the check digit field at two digits for every country, regardless of total length. Two digits give 100 possible values, which is enough to satisfy the modulo-97 requirement (remainders range from 0 to 96) while keeping the format compact and uniform worldwide.

Can a wrong IBAN still pass the MOD-97 check?

Yes, although it is unlikely. Under a simple uniform-random-error model, an incorrect value passes with probability 1/97, so the estimated detection rate is about 98.97%. Real typo patterns can differ, and a small fraction of multi-character errors can still produce a remainder of 1. The check guarantees structure, not that the account is real or correct.

What is the difference between the check digits and the national check digits?

The two IBAN check digits (positions 3–4) protect the entire IBAN using MOD-97. Some countries — Spain, France, Italy and others — also embed their own national check digits inside the BBAN to protect the domestic account number. They are separate mechanisms that operate at different layers.

Do I need a big-integer library to validate an IBAN?

Not necessarily. The piece-wise remainder method shown above processes the number in small steps and keeps a running value below 97, so plain integer math is enough. You only need big integers if you compute int(numeric) % 97 in one shot, as the Python example does.

Why does converting letters use A=10 instead of A=1?

Each letter expands to a two-digit number from 10 to 35. Starting at 10 (rather than 1) guarantees every letter maps to exactly two digits, which keeps the positional arithmetic consistent. If A were 1, letters would produce a mix of one- and two-digit values and the calculation would break.

How do I generate an IBAN with valid check digits for testing?

Use the Random IBAN generator: pick a country, and it fills the configured BBAN pattern and computes the check digits automatically. Every result passes MOD-97, but it is not checked for bank-code currency, account assignment, or provider-sandbox compatibility. Keep generated values isolated from live payment systems because an accidental match is possible.

Try Our IBAN Tools

Put your knowledge into practice with our free tools.