IBAN validation API
One POST checks an IBAN the way a payment run needs it: the check digits and the country's layout, then the bank behind it, read from the national register with its source and its date.
- 89 IBAN countries
- Bank and BIC with their source
- First calls without a key
What one call checks
POST /v1/iban/validate answers with one JSON object. Each check has its own field, so your code reads exactly what was verified and what was not.
Check digits
checks.iban_checksum
The ISO 13616 mod-97 check on the two digits after the country code. A single mistyped character fails it.
Country structure
checks.iban_structure
The length and the layout of the account part for each of the 89 countries of the IBAN registry. An invalid IBAN is not an HTTP error: the answer is a 200 with valid: false and the reason.
National check digits
checks.national_check_digits
Where a country hides its own key inside the account number: France and Monaco (RIB key), Belgium, Italy and San Marino (CIN), Spain (DC) and the United Kingdom (modulus check). A wrong key shows in this field and never turns valid to false. In Poland, the check digit of the settlement number comes with the bank code, in bank_code_check.check_digit.
The bank and its BIC
bank_code_check · bic.source · as_of
The bank code is looked up in the national register where we read it in full: Germany, Austria, Belgium, Slovakia, Czech Republic, Bulgaria, Switzerland and Liechtenstein. There, a code the register does not hold comes back not_allocated. Elsewhere a partial register or a composite map names the bank, and the answer says it cannot rule a code out. The register and the date of its edition travel with the answer.
SEPA and Verification of Payee
sepa · risk_indicators.vop_coverage
The SEPA schemes that reach the bank (Credit Transfer, Instant, Direct Debit), from the EPC scheme registers when they list it and from the country otherwise, with the basis named. And whether the EPC Verification of Payee register lists the bank as ready.
Bank screening, when you ask for it
POST /v1/iban/compliance
A separate call screens the payee's bank (BIC8) against the OFAC, EU and UN lists, checks the country against FATF and a fixed list of sanctioned jurisdictions, and returns a risk score from 0 to 100. It is informational, and it never screens the payee's name.
What it does not tell you
- Whether the account exists or is open. No register publishes that: only the payee's bank knows.
- Whose name is on the account. That check is Verification of Payee, run by the payee's bank; the API only says whether that bank is listed as ready for it.
- Whether the payee is sanctioned. The optional screening covers the bank and the country, not the person or the company you pay.
- The German account-number methods and the national keys of the countries not named above: they are not checked yet.
Your first call
Copy one of these as it stands. The IBAN is the example the sandbox uses: a valid Swiss IBAN that resolves to a real bank.
The keyless trial serves 25 validations a week on POST /v1/iban/validate, counted for the address the call comes from, ISO week in UTC, reset on Monday 00:00 UTC.
curl -X POST https://api.ibanforge.com/v1/iban/validate \
-H "Content-Type: application/json" \
-d '{"iban":"CH1000230000000012345"}'const res = await fetch("https://api.ibanforge.com/v1/iban/validate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ iban: "CH1000230000000012345" }),
});
const answer = await res.json();
console.log(answer.valid, answer.bank_code_check?.status, answer.bic?.code);import requests
r = requests.post(
"https://api.ibanforge.com/v1/iban/validate",
json={"iban": "CH1000230000000012345"},
timeout=10,
)
answer = r.json()
print(answer["valid"], answer["bank_code_check"]["status"])The answer, as the API gave it
Extract of the answer the API returned for this IBAN on 25 September 2026: the fields that say what was checked, and against which register. The full answer also carries the issuer, the Swiss clearing data, risk indicators and the next step to take. Called with no key, it also carries a trial block that says how many calls are left this week and when the count resets.
{
"iban": "CH1000230000000012345",
"valid": true,
"checks": {
"iban_structure": "pass",
"iban_checksum": "pass",
"bank_code": "pass",
"bic": "pass",
"national_check_digits": "not_checked",
"account_exists": "not_checked",
"payee_name": "not_checked",
"institution_sanctions": "not_checked",
"country_sanctions": "not_checked",
"payee_sanctions": "not_checked"
},
"country": {
"code": "CH",
"name": "Switzerland"
},
"bic": {
"code": "UBSWCHZH80A",
"bank_name": "UBS Switzerland AG",
"city": "Zürich",
"source": "SIX BankMaster (Swiss IID / BC-Nummer register)",
"as_of": "2026-09",
"basis": "national_register",
"authoritative": true
},
"bank_code_check": {
"value": "00230",
"status": "verified",
"register": "SIX BankMaster (Swiss IID / BC-Nummer register)",
"authoritative": true,
"as_of": "2026-09"
},
"sepa": {
"member": true,
"schemes": [
"SCT",
"SDD"
],
"vop_required": false
}
}Past the keyless trial, send the same request with the header Authorization: Bearer ifk_… and your key.
Why a mod-97 check is not enough
The official Swiss example of the IBAN registry, CH93 0076 2011 6238 5295 7, has correct check digits. Its bank code is allocated to no one in the SIX BankMaster, and the API says so:
{
"iban": "CH9300762011623852957",
"valid": true,
"bank_code_check": {
"value": "00762",
"status": "not_in_register",
"reason": "not_allocated",
"match": null,
"register": "SIX BankMaster (Swiss IID / BC-Nummer register)",
"authoritative": true,
"as_of": "2026-09"
}
}Answer of the API to that IBAN, exported on 29 September 2026.
Start free, then pay for what you use
Three free ways in, each with its own allowance. None asks for a card.
The keyless trial
25 validations a week on POST /v1/iban/validate, for the address the call comes from, as a taster. Reset on Monday 00:00 UTC.
A key in one click
25 requests a month, on every endpoint. One empty POST to /v1/keys/generate, or the button below: no e-mail, no card.
200 requests a month
Claim the same key with a 6-digit code sent to an address you read, or give the address when you create it. Same key, same prefix, no card.
When you need more
- Pro: $29 a month for 10,000 requests, reset on the 1st, cancel anytime.
- Credit packs that never expire, by card or in USDC: 1,000 credits for $4, 5,000 for $20, 25,000 for $80.
- x402: pay per call in USDC on Base, no account at all, $0.005 per validation and $0.002 per IBAN in a batch.
Everything around the API
- SandboxThe real API in your browser, with example IBANs from several countries.
- OnboardingFrom the keyless call to a batch of 100 IBANs, every block of the answer by its real name.
- Endpoint referencePOST /v1/iban/validate, field by field, with the error codes.
- OpenAPI 3.1The contract, to generate a client or import into Postman.
- npm: @ibanforge/sdk ↗The TypeScript and JavaScript SDK.
- PyPI: ibanforge ↗The Python SDK, sync and async clients.
- MCP serveribanforge-mcp for Claude, Cursor and other MCP clients, or the hosted endpoint.
- n8n ↗The community node for self-hosted n8n.
Try it on your own IBANs
The sandbox runs the real API. When you are ready, take a key: no e-mail, no card.