Test IBANs that survive more than a length check: built on the bank codes of real banks, with correct mod-97 check digits and the national extras (RIB key, CIN, 11-test) computed instead of faked, for ten countries, up to a thousand at a time, as plain lines, CSV or JSON.
What an IBAN is, part by part
The International Bank Account Number, defined in ISO 13616 and catalogued per country in the IBAN Registry, wraps each country's older domestic account format in a uniform envelope: two letters of country code, two check digits, then the national part (the BBAN). The most quoted example happens to be German:
| Piece | Value | Meaning |
|---|---|---|
DE | country | ISO 3166 code, decides length and layout of everything after it |
89 | check digits | mod-97 over the whole rearranged number |
37040044 | bank code | the Bankleitzahl; this one is Commerzbank Köln |
0532013000 | account | 10 digits, padded with leading zeros |
That envelope is the same everywhere, but the BBAN inside is not. Germany packs a bank code and an account number, France adds a branch code and its own two-digit RIB key, Italy prepends a single check letter called the CIN, the Netherlands uses four letters for the bank and requires the account to pass a weighted checksum known as the elfproef. Lengths run from 15 characters (Norway) to 31 (Malta), with most SEPA countries in the low twenties. Any tool that treats an IBAN as "two letters plus some digits" produces numbers a serious validator rejects on the second look, which is the reason this generator implements each country's layout individually. The rows above the article show the seams: amber for country code and check digits, green for the bank code, plain for the rest.
Why developers need fake IBANs
Four situations keep coming up. Form validation first: if your checkout or onboarding form validates IBANs, you need inputs that pass, inputs that fail, and inputs that pass mod-97 but stumble over country length, and you need them in quantity. Fixtures second: seed data for integration tests and local databases should look like production data without containing any, and a column of DE00000000000000000000 tells you nothing about how your UI truncates, groups or masks real values. Demos and screenshots third: a sales demo or documentation screenshot with a real customer IBAN in it is an incident, not an oversight.
The fourth reason has teeth: the GDPR. A customer's IBAN identifies the account holder, which makes it personal data, and copying production records into staging systems with weaker access control is the kind of practice data protection officers flag first. Synthetic IBANs that are structurally indistinguishable from real ones remove the whole category of problem. We have seeded a few too many staging environments to be relaxed about this: generate test data that looks maximally real and contains exactly nothing, and the discussion with the DPO takes five minutes instead of five meetings. For the rest of the row, names and addresses to go with the IBAN, there is our test data generator.
How the two check digits work
The two digits after the country code are a mod-97 checksum defined in ISO 7064, and they are the reason a single typo almost never survives a banking form. Checking an IBAN takes three steps. Move the first four characters to the end, so DE89 3704…3000 becomes 3704…3000 DE89. Replace every letter with its position in the alphabet plus 9, so D becomes 13, E becomes 14. Now read the result as one long decimal number and divide by 97: a genuine IBAN leaves remainder 1. Generating runs the same machinery backwards: set the check digits to 00, compute the remainder, subtract it from 98, and pad to two digits.
The classic implementation trap is integer overflow. A rearranged German IBAN is a 24-digit number, and 24 digits do not fit into a 64-bit float or int, so a naive Number(...) % 97 silently computes garbage that still looks plausible. The two clean fixes are BigInt, or the chunked modulo this tool uses: take the remainder of the first 7 digits, prepend it to the next chunk, repeat. Mod-97 catches every single-character error and the overwhelming share of transposed pairs, which is stronger than the Luhn scheme card numbers use (our Luhn checker shows that one digit by digit). What it does not catch: a wrong but self-consistent IBAN. The number can be perfect and the account still nonexistent, and no offline algorithm can tell the difference.
Why real bank codes matter
Most IBAN generators fill the bank code with random digits, and for a plain mod-97 check that is enough. It stops being enough the moment the receiving system looks the code up. Payment providers, banking backends, accounting imports and stricter form libraries resolve the bank code against the national directory (the Bundesbank's Bankleitzahl file in Germany, the OeNB register in Austria, the Banque de France establishment list) to derive the BIC or display the bank name, and an IBAN whose bank code resolves to nothing gets rejected with a confusing "invalid IBAN" even though the checksum is fine.
This generator ships a curated list of real codes per country instead: 17 German Bankleitzahlen from Deutsche Bank Berlin (10070000) to GLS Bank Bochum (43060967), Erste Bank and Raiffeisen for Austria, INGB, RABO and ABNA for the Netherlands, and equivalents for the other six countries. Each row shows which bank its code belongs to, and the CSV output carries the name as a column, so a fixture can assert against it. If you specifically want the failure case, the --random-bank flag switches to random digits; the Polish variant even computes the sort code's own eighth check digit, so the number fails the directory lookup but nothing else.
The same care goes into the national check digits inside the BBAN, which random-digit generators skip entirely: the French RIB key, the Italian CIN letter, the Spanish dígito de control pair, the Belgian mod-97 pair, the Dutch elfproef and the Polish sort-code digit are all computed by their real algorithms here. A validator that checks any of them, and the strict ones do, accepts these IBANs and rejects most competitors' output.
How to use this generator
Pick a country (or SEPA for a random mix), set the count, click any row to copy that IBAN. The batch regenerates on every option change; copy-all and the download button take the whole list. Three output shapes cover the usual destinations: plain lines for pasting into a form or a flat file, --csv for a fixture with country, iban, bank_code, account_number and bank_name columns, and --json for the same records as an array. --paper switches the visible list to the print format in groups of four (DE89 3704 0044…), which is what belongs on anything a human reads; the electronic format without spaces is what belongs in databases and APIs, and CSV and JSON always use it.
The stats under the list stay honest: the length box shows the registry length for the selected country, and the check-digit box re-validates the entire generated batch against mod-97 on every render rather than asserting a hardcoded "valid". Randomness comes from crypto.getRandomValues, and everything runs in this tab, so account-shaped data never touches a server, ours included.
Country formats at a glance
The ten supported countries, with the BBAN layout this tool builds and a sample of the curated bank codes:
| Country | Length | BBAN layout | Example bank code |
|---|---|---|---|
| DE Germany | 22 | 8-digit bank + 10-digit account | 37040044 (Commerzbank Köln) |
| AT Austria | 20 | 5-digit bank + 11-digit account | 20111 (Erste Bank) |
| CH Switzerland | 21 | 5-digit clearing no. + 12-digit account | 00700 (Zürcher Kantonalbank) |
| NL Netherlands | 18 | 4-letter bank + 10-digit account (11-test) | INGB (ING) |
| FR France | 27 | 5-digit bank + 5-digit branch + 11-char account + RIB key | 30004 (BNP Paribas) |
| IT Italy | 27 | CIN letter + 5-digit ABI + 5-digit CAB + 12-char account | 03069 (Intesa Sanpaolo) |
| ES Spain | 24 | 4-digit bank + 4-digit branch + 2 check digits + 10-digit account | 2100 (CaixaBank) |
| BE Belgium | 16 | 3-digit bank + 7-digit account + 2 check digits | 001 (BNP Paribas Fortis) |
| GB United Kingdom | 22 | 4-letter bank + 6-digit sort code + 8-digit account | NWBK 60-16-13 (NatWest) |
| PL Poland | 28 | 8-digit bank/branch no. + 16-digit account | 11402004 (mBank) |
Two details worth knowing when you write assertions against this data. Dutch accounts always pass the 11-test here, because real Dutch banks (outside the former Postbank range) never issue numbers that fail it and strict validators check it. And the UK, despite leaving the EU, remains in SEPA, so GB IBANs still belong in a European payments test suite.
Test IBANs in code
For one-off values this page is faster, but inside a test suite you want IBANs from code:
| Snippet | Notes |
|---|---|
from faker import Faker; Faker('de_DE').iban() | Python; valid mod-97, but the bank code is random digits. |
from schwifty import IBAN; IBAN.generate('DE', bank_code='37040044', account_code='1') | Python; validates against a bundled bank registry and derives the BIC. |
Iban.random(CountryCode.DE) | Java, iban4j; the Builder variant accepts an explicit bankCode. |
import { fakerDE as faker } from '@faker-js/faker'; faker.finance.iban() | JavaScript; formatted or electronic via options. |
Rolling your own is a mod-97 implementation away, and the overflow trap from the check-digit section is where most homegrown versions break: in JavaScript, compute the remainder with BigInt or in 7-digit chunks, never with a plain number. If the suite needs card numbers next to the IBANs, the same logic with Luhn instead of mod-97 lives in our credit card test numbers generator.
Generator or validator?
This page builds IBANs; checking one you already have is the opposite job and has its own tool. The IBAN validator takes any IBAN and names the exact failure: wrong length for the country, an impossible character in a numeric position, or check digits that do not match, instead of a bare "invalid". The pairing is deliberate. When a customer's IBAN is rejected by your form, the validator tells you why; when your form needs to be tested against a hundred valid inputs, the generator makes them. Neither can promise the account exists, and anything that claims otherwise from inside a browser is guessing: existence is only known to the banks involved, at transfer time.
Test IBAN questions
Is it legal to use randomly generated IBANs for testing?
Yes. An IBAN is an account address, not a secret or a protected identifier, and computing a number that satisfies a public checksum breaks no law. The line is crossed by what you do with it: putting a fabricated IBAN on an invoice, into a direct debit mandate or anywhere it is meant to deceive is fraud regardless of how the number was made. Keep generated IBANs inside test systems, fixtures, screenshots and validation checks and you are fine. One practical caution: mark test data clearly, because a structurally valid IBAN in a staging database looks exactly like a real one to the next person who exports it.
How do I generate test IBANs in Python or Java?
Python has two good options: faker gives you Faker("de_DE").iban() with a random bank code, and schwifty builds an IBAN from a real bank code with IBAN.generate("DE", bank_code="37040044", account_code="532013000"), validating against its bundled bank registry and deriving the BIC on top. In Java, iban4j does both jobs: Iban.random(CountryCode.DE) for quick fixtures, or the Builder with an explicit bankCode when a downstream system checks the code against the directory. All of these produce mod-97-valid output; only schwifty and an explicit builder give you existing bank codes.
What happens if you transfer money to a fake IBAN?
If the IBAN fails mod-97, your banking app rejects it before anything is sent, which is the whole point of the check digits. If it is structurally valid but the account does not exist, the receiving bank returns the SEPA transfer with a return code such as AC01 (incorrect account number), and the money lands back in your account after a few days. Since 9 October 2025, euro-area banks additionally have to run Verification of Payee and warn you when the recipient name does not match the account before the transfer even leaves. Still, never point a real payment at generated data; test transfers belong in a sandbox with test money.
Which IBAN should I use for testing SEPA payments?
The best-known test IBAN is DE89 3704 0044 0532 0130 00, built on the real Commerzbank Köln bank code 37040044. It appears in the IBAN standard’s own examples, in countless docs, and Stripe designates it as the IBAN that makes a SEPA Direct Debit succeed in test mode. Its fame is also its weakness: it is so widespread that some systems special-case or blocklist it, and every developer on the team has seen it a hundred times. For fixtures that should look like real data, generate fresh IBANs per country instead and keep the famous one for quick manual form checks.
Do test IBANs work in the Stripe or PayPal sandbox?
In Stripe test mode, any structurally valid IBAN is accepted for SEPA Direct Debit, and Stripe documents magic values on top: DE89370400440532013000 creates a PaymentIntent that succeeds, while designated sibling IBANs trigger specific failures, disputes or delayed outcomes, which is exactly what you want for testing error paths. PayPal’s sandbox works through sandbox accounts rather than raw IBAN entry; where a bank account is linked, generated IBANs pass because the sandbox does not verify account existence. In live mode both providers verify accounts through micro-deposits or the banking network, and generated numbers fail there.
Are randomly generated IBANs GDPR-relevant?
A real customer’s IBAN is personal data under Art. 4 GDPR, since it identifies the account holder. A randomly generated IBAN that never belonged to anyone identifies nobody and falls outside the regulation, the same way Recital 26 treats anonymous data. The realistic risk sits on the other side: copying production IBANs into staging or test databases spreads personal data into systems with weaker access controls, which is precisely what synthetic test IBANs exist to avoid. The chance of randomly hitting an existing account number under a real bank code is tiny but not zero, so treat generated IBANs as display and validation data, never as payment targets.