---
title: "Validate a single IBAN"
method: POST
path: "/v1/iban/validate"
tags: ["IBAN"]
---

# Validate a single IBAN

`POST /v1/iban/validate`

Validates an IBAN and returns parsed components including country, check digits, BBAN, and optional BIC lookup. Costs 0.005 USDC via x402.

## Request body

- object
  - `iban` string, required — IBAN to validate (spaces allowed, will be normalized)

## Response `200`

Validation result

- IBANValidationResult
  - `iban` string, required — The IBAN as provided (normalized)
  - `valid` boolean, required
  - `country` object
    - `code` string, required
    - `name` string, required
  - `check_digits` string
  - `bban` object
    - `bank_code` string, required
    - `branch_code` string
    - `account_number` string, required
  - `bic` object, nullable
    - `code` string, required
    - `bank_name` string, nullable, required
    - `city` string, nullable, required
  - `formatted` string — IBAN formatted in groups of 4
  - `clearing` object, nullable — Swiss clearing enrichment from the SIX BankMaster directory — present for CH and LI IBANs only, and included at no extra cost in the 0.005 USDC validation. Full rail participation, not just a name lookup.
    - `iid` string — Zero-padded 5-digit IID / BC-Nummer
    - `name` string
    - `type` 'bank' | 'cantonal_bank' | 'postfinance' | 'raiffeisen' | 'central_bank' | 'foreign_participant'
    - `town` string
    - `sic` boolean — SIC (Swiss Interbank Clearing) participation
    - `instant_payments_chf` boolean — Instant Payments CHF participation
    - `eurosic` boolean — euroSIC participation
    - `qr_iid` string, nullable — QR-IID allocation for QR-bill reference, null when the institution has none
  - `error` 'invalid_format' | 'unsupported_country' | 'wrong_length' | 'checksum_failed'
  - `error_detail` string
  - `cost_usdc` number, required
  - `processing_ms` number
  - `sepa` object — SEPA compliance details. Only present when the IBAN is valid and the country participates in SEPA.
    - `member` boolean, required — Whether the IBAN country is a SEPA member
    - `schemes` string[], required — SEPA schemes the institution supports (SCT = Credit Transfer, SDD = Direct Debit, SCT_INST = Instant Credit Transfer)
    - `vop_required` boolean, required — Whether Verification of Payee (VoP) is required under EU Instant Payments Regulation for this institution
  - `issuer` object — Issuer classification for the institution behind the IBAN. Useful for vIBAN detection and KYC enrichment. Only present when the IBAN is valid and the BIC is resolved.
    - `type` 'bank' | 'digital_bank' | 'emi' | 'payment_institution' | 'null', nullable, required — Type of financial institution (bank = traditional bank, digital_bank = neobank/challenger, emi = Electronic Money Institution, payment_institution = licensed PI). Null when we hold no support for a type: falling back to bank would be an assertion, and a payee pre-flight must not be handed one.
    - `name` string, required — Name of the institution holding this BIC
    - `classification` 'curated' | 'default', required — Whether the type was established or assumed. curated = the BIC8 is in the issuer set, so this is an identification. default = nothing is on file and 'bank' is the fallback, which covers 42,195 of 43,199 distinct BIC8 (97.7%, recounted 29/07/2026; the count drifts at every monthly refresh). When sizing exposure to virtual IBANs, count only curated.
    - `iban_issuer` 'confirmed' | 'not_listed' — Whether the country's own list of IBAN-issuing providers names the holder of this bank code. Present only where such a list exists, today NL. confirmed = the identifier belongs to a provider that issues IBANs. not_listed = it resolves to a BIC, but the holder is not among the known issuers, so the account may not exist: measured 29/07/2026, only 90 of our 815 Dutch codes are on that list and the rest resolve to corporate treasuries that hold a Dutch BIC for their own SWIFT traffic. NOT a denial, because the Dutch list is explicitly not exhaustive, which is also why NL keeps bank_code_check.authoritative false.
  - `risk_indicators` object — AML/CFT risk indicators derived from the IBAN structure, issuer type, and country. Designed for compliance pre-screening and fraud prevention workflows. Only present when the IBAN is valid.
    - `issuer_type` 'bank' | 'digital_bank' | 'emi' | 'payment_institution' | 'null', nullable, required — Type of the issuing institution (mirrors issuer.type for convenience). Null when the bank code resolved no institution — it used to default to "bank", which typed an institution that had not been found. Read bank_code_check to tell an unresolved code from a genuine bank.
    - `country_risk` 'standard' | 'elevated' | 'high', required — Country-level risk classification based on FATF grey/black lists and EU high-risk third countries
    - `test_bic` boolean, required — Whether the resolved BIC is a test/sandbox code (position 8 = 0)
    - `sepa_reachable` boolean, required — Whether SEPA Credit Transfers reach this COUNTRY. Derived from the country, not from the account: it stays true on an IBAN whose bank code resolved nothing. See sepa_reachable_scope.
    - `sepa_reachable_scope` 'country', required — The scope sepa_reachable holds at. Present so the field cannot be read as an account-level assertion.
    - `vop_coverage` boolean, required — Whether the institution is covered by Verification of Payee, reducing payee impersonation risk
  - `bank_code_check` object — Separate verdict on the BBAN bank code. `valid` answers ISO 13616 (structure + mod-97) and says nothing about whether the bank code identifies an institution; this field answers that, and states how much weight the answer carries. Present only when the IBAN is valid.
    - `value` string, required — The bank code that was actually checked. Normally identical to bban.bank_code. It differs in Finland, where the monetary institution code is 1 to 4 characters depending on its leading digits while bban.bank_code stays the fixed positional slice: a Nordea IBAN carries bban.bank_code "123" and value "1". When they differ, this field is the one the verdict is about.
    - `status` 'verified' | 'not_in_register' | 'unavailable', required — verified: resolves to an institution we can name. not_in_register: it does not, in reference data we do hold for this country — actionable as non-existence ONLY when authoritative is true. unavailable: we hold no reference data for this country, so no opinion.
    - `match` 'register' | 'prefix' | 'null', nullable, required — register: exact key in the reference set, deterministic. prefix: the bic8 LIKE fallback, reachable only in the 30 countries whose bank code may open on a letter (a BIC8 always does) — check candidates.
    - `register` string, nullable, required — Name of the reference set consulted.
    - `authoritative` boolean, required — True only where that reference set is the national register: today CH and LI against the SIX BankMaster, DE against the Bundesbank Bankleitzahlendatei, FI against the Finance Finland monetary institution list, AT against the Oesterreichische Nationalbank SEPA-Zahlungsverkehrs-Verzeichnis, and BE against the Banque nationale de Belgique bank identification codes. This is the flag to branch on: everywhere else an absence is evidence of absence from our data, not of non-existence. One asymmetry worth knowing: CH, LI, DE, AT and BE allocate codes to individual institutions, while FI allocates prefixes to banking groups, so a Finnish verified confirms the group and its BIC rather than one specific bank. The negative direction carries full weight in all six.
    - `candidates` integer — BIC8 the prefix search matched. Present only for match=prefix. Greater than 1 means the returned BIC is one of several and may belong to a different institution than the account does.
    - `retired` boolean — Present and true when an authoritative register marks the code for deletion: the institution is being retired. The code WAS allocated, so this is a verified result, not a denial. See superseded_by.
    - `superseded_by` string — The bank code that takes over, when the register names one. Re-paper the beneficiary against it.
    - `as_of` string, required — Year-month the consulted reference set was last refreshed.
  - `next_steps` object[] — Ordered advice derived from THIS result: what blocks a payment first, what merely enriches it after. Branch on `code`, never on the prose. Absent or empty for an IBAN that failed validation, since the error already says what to do.
    - `code` string, required — Stable identifier. Today: bank_code_not_allocated (the national register denies the code, do not send), bank_code_retired (allocated but being withdrawn, re-paper against superseded_by), verify_payee_name (we cannot confirm it, treat as unavailable and let a name check decide), bic_is_advisory (the BIC was picked from several candidates), issuer_not_a_known_iban_issuer (the code resolves to a BIC, but its holder is not among the providers known to issue IBANs in that country), test_bic, expect_virtual_iban (curated non-bank issuer, account holder and IBAN holder often differ), screen_compliance.
    - `do` string, required — The instruction, in one sentence an agent can relay to a person.
    - `because` string, required — The field of this response that produced the step, so the advice is auditable.
    - `action` string — An IBANforge call that performs the step, when one exists.

## Other responses

- `400` — Missing or malformed request body
- `402` — Payment required (x402)

## Changes

- **2026-07-31** `eaaa83cd6548` — 2 breaking, 2 warning, 5 info
  - the response property `issuer/type` became nullable for the status `200`
  - the response property `risk_indicators/issuer_type` became nullable for the status `200`
  - added the new `undefined` enum value to the `issuer/type` response property for the response status `200`
  - added the new `undefined` enum value to the `risk_indicators/issuer_type` response property for the response status `200`
  - …5 more
- **2026-07-26** `81718095283e` — 2 info
  - the endpoint scheme security `apiKey` was added to the API
  - added the optional property `clearing` to the response with the `200` status

[Change history](https://skmtc.dev/ibanforge/apis/ibanforge-api/changes/v1/iban/validate/post.md)

---

[API](https://skmtc.dev/ibanforge/apis/ibanforge-api.md) · [All operations](https://skmtc.dev/ibanforge/apis/ibanforge-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/ibanforge/ibanforge-api/revisions/eaaa83cd6548/schema)
