Developer ResourcesJune 3, 2026 · 7 min read

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.

Koray Köylü
Koray Köylü

Founder, ibanchecker.cash

javascriptapiibanvalidationcode
Share

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 provided
  • INVALID_CHARS, Contains characters other than letters and digits
  • INVALID_COUNTRY, First two characters aren't a recognized IBAN country code
  • INVALID_LENGTH, Wrong length for the given country
  • INVALID_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.