How to Validate IBANs in JavaScript (API & Manual)
Two approaches: call the ibanchecker.cash API for full bank data, or implement MOD-97 validation yourself. Includes working code for both.
Founder, ibanchecker.cash
IBAN validation is a common requirement for any application that handles European or international payments. There are two main approaches: call a validation API (which returns bank name, BIC, and structured data), or implement the MOD-97 check yourself (fast, offline, no network dependency). This guide covers both.
Approach 1: Using the ibanchecker.cash API
The API approach is recommended when you need full bank data, name, BIC, branch, SEPA status, not just a pass/fail result. The API handles all 92 IBAN countries and returns a consistent JSON response.
Single IBAN Validation
const response = await fetch('https://ibanchecker.cash/api/v1/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({ iban: 'DE89370400440532013000' }),
});
const data = await response.json();
if (data.valid) {
console.log(data.bank_name); // "Deutsche Bank"
console.log(data.bic); // "DEUTDEDBXXX"
console.log(data.country); // "DE"
console.log(data.transfer_type); // "SEPA+SWIFT"
} else {
console.error(data.error); // Human-readable error message
console.error(data.error_code); // "INVALID_CHECK_DIGITS" etc.
}Bulk Validation (up to 100 IBANs)
const response = await fetch('https://ibanchecker.cash/api/v1/validate/bulk', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
ibans: [
'GB29NWBK60161331926819',
'DE89370400440532013000',
'FR7614508059952116425957022',
],
}),
});
const { count, valid_count, invalid_count, results } = await response.json();
console.log(`${valid_count} valid, ${invalid_count} invalid out of ${count}`);Response Structure
// Valid IBAN response
{
"valid": true,
"iban": "DE89370400440532013000",
"formatted": "DE89 3704 0044 0532 0130 00",
"check_digits": "89",
"country": "DE",
"country_name": "Germany",
"bank_name": "Deutsche Bank",
"bic": "DEUTDEDBXXX",
"currency": "EUR",
"transfer_type": "SEPA+SWIFT",
"sepa": true
}
// Invalid IBAN response
{
"valid": false,
"iban": "DE89370400440532013001",
"error": "Check digit verification failed.",
"error_code": "INVALID_CHECK_DIGITS"
}Rate Limits
Every validation request needs an API key. The free key covers 100 requests a month; for production volumes, see the pricing page for plans starting at 50,000 requests a month.
Approach 2: Client-Side MOD-97 Validation
If you only need format validation (no bank data), implementing MOD-97 in-browser or in Node.js is trivial and has zero latency. This is ideal for real-time input validation as a user types.
function validateIBAN(raw) {
// Normalize
const iban = raw.replace(/\s/g, '').toUpperCase();
// Basic character check
if (!/^[A-Z0-9]+$/.test(iban)) return { valid: false, error: 'Invalid characters' };
if (iban.length < 15 || iban.length > 34) return { valid: false, error: 'Invalid length' };
// MOD-97 check
const rearranged = iban.slice(4) + iban.slice(0, 4);
const numeric = rearranged.split('').map(c =>
c >= 'A' ? String(c.charCodeAt(0) - 55) : c
).join('');
let remainder = 0;
for (let i = 0; i < numeric.length; i += 7) {
remainder = parseInt(remainder + numeric.slice(i, i + 7), 10) % 97;
}
if (remainder !== 1) return { valid: false, error: 'Check digit mismatch' };
return { valid: true, iban, formatted: iban.replace(/(.{4})/g, '$1 ').trim() };
}
// Usage
validateIBAN('GB29 NWBK 6016 1331 9268 19');
// { valid: true, iban: 'GB29NWBK60161331926819', formatted: 'GB29 NWBK 6016 1331 9268 19' }React Hook Example
import { useState, useCallback } from 'react';
function useIBANValidation() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(false);
const validate = useCallback(async (iban) => {
if (!iban.trim()) return;
setLoading(true);
try {
const res = await fetch('/api/validate-iban', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ iban }),
});
setResult(await res.json());
} finally {
setLoading(false);
}
}, []);
return { result, loading, validate };
}The hook calls a route on your own server, which forwards the request to the API with your key. An API key placed in browser code is visible to every visitor of your site.
Error Codes Reference
EMPTY, No IBAN providedINVALID_CHARS, Contains characters other than letters and digitsINVALID_COUNTRY, First two characters aren't a recognized IBAN country codeINVALID_LENGTH, Wrong length for the given countryINVALID_CHECK_DIGITS, MOD-97 check failed (transcription error)
Best Practices
- Strip spaces and uppercase before validation, users copy IBANs from bank statements in various formats.
- Use client-side MOD-97 for real-time feedback as users type, then confirm with the API on form submission.
- Cache API results for repeated validations of the same IBAN within a session.
- Never store unvalidated IBANs, validate at the point of entry, before any payment processing.
Sources & References
Integrate IBAN validation via API
REST API with 100 free validations a month. JSON response with bank name, SEPA status, and BBAN breakdown.
Introducing Northern Cyprus UBAN Support: Validator and Bank Directory
A validator for the 28-character Northern Cyprus UBAN, plus a directory of all 23 registered banks. What UBAN is, and wh...
One Client, Four Registries: PyPI, npm, Packagist and MCP All Disagree
Why OIDC cannot do npm's first publish, why Packagist needs no workflow, and the PyPI name check that returns 200 for na...
npm trust github Fails with 400 Bad Request: The Missing Permission Flag
A bare 400 with no error body, caused by a 20 May 2026 registry rule. The exact npm version boundary, the one flag that ...
IBAN Validation in Python: Complete Implementation with Regex and Check Digits
Two production-ready approaches: call the API for bank metadata, or implement MOD-97 yourself. Includes working code and...