---
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. Pass an optional `reference` to add `reference_check`: the reference checksum verdict AND whether the reference may legally travel with this account under the Swiss Payment Standards (QRR requires a QR-IBAN, ISO 11649/SCOR forbids one).

## Request body

- object
  - `iban` string, required — IBAN to validate (spaces allowed, will be normalized)
  - `reference` string — Optional structured payment reference. When present the response carries a `reference_check` block with the checksum verdict and, for CH/LI accounts, the QRR/SCOR pairing verdict. Free-standing checksum validation is available at no cost on GET /v1/reference/validate.
  - `reference_type` 'rf' | 'scor' | 'qrr' | 'ogm' | 'vcs' | 'viitenumero' | 'kid' | 'ocr' — Optional scheme hint for an ambiguous reference. `scor` and `rf` both mean ISO 11649.

## 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 — Where the consulted register places THIS bank code. May differ from address.city, which is the legal seat — both true, different questions.
    - `source` string, nullable — Which dataset named this institution.
    - `as_of` string, nullable — Year-month that dataset was last refreshed.
    - `basis` 'national_register' | 'curated_map' | 'directory_prefix' — WHERE the bank code to BIC pairing came from, and therefore what may be done with the BIC. national_register: the country's own register publishes this BIC for this bank code — today Germany, Austria, Belgium and Bulgaria; the German Bankleitzahlendatei carries the exact 11-character BIC per BLZ. curated_map: our maintained bank-code map made the pairing on an exact key. Usually right, and not an allocation record. directory_prefix: the bic8 LIKE fallback, which can match several institutions at once — read bank_code_check.candidates. Answers the settlement question directly: only national_register is settlement-grade, so outside those registers a derived BIC is advisory and should be confirmed with the beneficiary or your bank before it becomes a stored routing instruction.
    - `authoritative` boolean — Whether this BIC may be stored and settled against. Derived from `basis` by a single table, so the two cannot disagree. NOT the same claim as bank_code_check.authoritative, which is about the BANK CODE — whether a national register was consulted about its existence. Switzerland is where they visibly differ: the SIX BankMaster answers authoritatively that an IID is allocated, while the BIC beside it still comes from our curated map.
    - `lei` string, nullable — Legal Entity Identifier, read from the same directory row /v1/bic/:code serves. Null means GLEIF publishes no LEI for this BIC, never that the institution has none.
    - `lei_status` string, nullable
    - `address` object, nullable — Registered / head-office address (GLEIF, CC0). Entity-level, not per-branch. Always dated by its own as_of, which is the entity last filing and is usually OLDER than the as_of above.
      - `type` 'registered'
      - `street` string, nullable
      - `post_code` string, nullable
      - `region` string, nullable
      - `city` string, nullable
      - `country` string
      - `romanized` string, nullable
      - `romanization` 'original_latin' | 'gleif_english' | 'unavailable' — unavailable means the entity filed a non-Latin address and GLEIF ships no official Latin form. No transliteration is invented.
      - `source` string
      - `language` string, nullable
      - `as_of` string, nullable
  - `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
  - `reference_check` ReferenceCheckBlock — Served inside POST /v1/iban/validate when a `reference` was supplied. Carries TWO independent verdicts: `valid` (the reference checksum) and `pairing` (whether it may legally travel with this account). A reference can be arithmetically valid and still illegal on that IBAN, and the reverse. Each verdict names its own document.
    - `reference` string, required
    - `scheme` 'rf' | 'qrr' | 'ogm' | 'viitenumero' | 'kid' | 'ocr', nullable, required
    - `valid` boolean, nullable, required
    - `status` 'checked' | 'unverifiable_without_creditor_config' | 'unrecognised', required
    - `check_digit_expected` string
    - `also_valid_as` object
    - `source` string, nullable, required — Provenance of the CHECKSUM verdict
    - `as_of` string
    - `pairing` 'ok' | 'qrr_requires_qr_iban' | 'scor_forbidden_with_qr_iban' | 'not_applicable', required — Per the Swiss Implementation Guidelines a QRR reference may only be used with a QR-IBAN (institution identifier in the SIX range 30000-31999), and an ISO 11649 (SCOR) reference may not. `not_applicable` outside CH/LI, where there is no QR-IBAN to pair against — including for a valid RF reference, whose own checksum verdict is unaffected.
    - `pairing_source` string — Provenance of the PAIRING verdict — a DIFFERENT document from `source`
    - `pairing_as_of` string
    - `note` string, required
  - `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
    - `vop_participant` boolean, nullable — Bank-level VoP readiness: true when the resolved institution is listed as "ready" in the EPC Verification of Payee scheme register; false when it is not; null when no institution was resolved. Listing means the bank answers VoP requests — it does not run the name check for you.
  - `issuer` object — Issuer classification for the institution behind the IBAN. Useful for vIBAN detection and KYC enrichment. Present when the IBAN is valid and either the BIC resolved or an official register names the holder of the bank code (see psd_registration).
    - `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' | 'register' | 'default', required — Whether the type was established or assumed. curated = the BIC8 is in the issuer set, so this is an identification. register = an official register names the holder of this bank code and says what it is; also an identification, and one that carries a date and an issuing authority in the psd_registration block beside it. It only ever replaces a default, never a curated verdict. 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 curated and register, never default.
    - `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.
  - `psd_registration` object — The EBA's PSD2 register of payment and electronic money institutions naming the holder of this bank code. Joined on country + national reference code, and served ONLY for countries where that code was measured to be the one the IBAN actually carries — today Spain alone. The register carries no BIC and no LEI, and in 29 of its 30 countries it files authorisations under a company or tax number from an unrelated register (a Polish NIP, a French SIREN, a Dutch DNB reference), so joining those to a bank code would attach a real institution's authorisation to an unrelated bank. Absent on a miss: there is no negative form, because the register's own disclaimer states that an institution omitted from it is authorised all the same.
    - `registered` boolean, required — Always true. There is no negative form of this block.
    - `entity_type` 'payment_institution' | 'emi' | 'aisp' | 'exempted_emi' | 'exempted_payment_institution', required — The register's own category. emi = electronic money institution, payment_institution = authorised PI, aisp = account information service provider (reads accounts, issues nothing), exempted_emi / exempted_payment_institution = small operators waived FROM authorisation, which is not a licence. Only emi and payment_institution move issuer.type.
    - `name` string, required — Institution name as the register publishes it.
    - `country` string, required — ISO country of residence, as the register publishes it.
    - `competent_authority` string, required — The national authority that filed the authorisation, e.g. 'ES_BE' for Banco de España.
    - `source` string, required — Attribution required by the EBA legal notice ("Reproduction of all EBA material on this site is authorised, provided the source is acknowledged"). Always present.
    - `as_of` string, required — Date of the golden copy this row came from (YYYY-MM-DD), read from the EBA manifest and never from a clock. Always present.
  - `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.
    - `reason` 'not_allocated' | 'absent_from_reference_data' | 'no_reference_data_for_country' | 'register_names_no_holder' | 'national_register_unavailable' | 'lookup_failed' — WHY the verdict is not verified, as one token to branch on. Present on every not_in_register and every unavailable; absent on verified. not_allocated: a national register denies the code — the only value that licenses "do not send", and it appears only with authoritative true. absent_from_reference_data: our composite map does not carry it, which says nothing about the country's own register because we did not consult one. no_reference_data_for_country: we hold nothing at all for this country. register_names_no_holder: the national register defines this code space and publishes no holder for it — silence, not a denial. national_register_unavailable: the country HAS a register we normally decide against and it could not be consulted for this call, so the verdict beside it comes from the composite map and carries composite weight. lookup_failed: the reference lookup could not run at all (timeout, unreadable database, missing table). The last two describe US, never your beneficiary: neither is evidence about the account, and neither may be escalated into a refusal.
    - `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. For LV and GI it names a published structural rule instead — Latvijas Banka and the Gibraltar Financial Services Commission (Guidance Note 07) both publish that IBAN positions 5-8 ARE the first four characters of the institution's BIC. That is a documented rule rather than our own assembly, but it says how to READ the IBAN, not that the BIC it points at was allocated, so authoritative stays false.
    - `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, BE against the Banque nationale de Belgique bank identification codes, and BG against the Bulgarian National Bank BAE register. This is the flag to branch on: everywhere else an absence is evidence of absence from our data, not of non-existence. Two asymmetries worth knowing: FI allocates prefixes to banking groups rather than to institutions, so a Finnish verified confirms the group and its BIC rather than one specific bank; and a Bulgarian BAE code covers IBAN positions 5-12 (bank code AND branch digits) while the verdict is made on the four-letter bank code alone, because the register does not enumerate every bank branch to one standard. The negative direction carries full weight in all seven.
    - `candidates` integer — BIC8 the search matched. Present for match=prefix, and for the LV/GI structural rule when the published rule alone leaves more than one BIC8 standing. 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.
    - `institution` object — What the national register publishes about the allocated institution. Present only on an authoritative answer — composite-map hits stay bare (naming a BIC holder is the bic block, and its address would imply a register that was not consulted). Depth varies by register: SIX (CH/LI) and the OeNB (AT) publish the full seat address, the Bundesbank (DE) publishes postal code and town only, the Banque nationale de Belgique (BE) and the Bulgarian National Bank (BG) publish names alone; Finland stays without this block, its codes belong to banking groups. Names are served exactly as the register writes them, which for BG means Cyrillic — transliterating would be an alteration its terms forbid. Absent fields are null, never guessed. This is the institution allocated the BANK CODE — not a branch, and not proof of any account.
      - `name` string, required
      - `street` string, nullable, required — One line, house number included, matching the GLEIF shape. Null where the register publishes none (DE, BE).
      - `post_code` string, nullable, required
      - `town` string, nullable, required
      - `country` string, required
      - `lei` string, nullable — Legal Entity Identifier, where the register publishes one (the OeNB does, 99% of entries).
    - `as_of` string, required — Year-month the consulted reference set was last refreshed. Where the register publishes an effective date of its own it is that date, not ours: the Bulgarian BAE register is republished on request rather than on a calendar, so dating it with our monthly refresh would overstate how current it is.
  - `official_identity` object — The official identity a central bank publishes for the institution behind the code we resolved: legal name, LEI, registered address, and the publisher's own category. Sources: the European Central Bank's daily list of monetary financial institutions (reached by LEI, and by the five-digit French code banque, which is what a French RIAD code contains), and the Banco de España's list of Spanish MFIs (reached by the four-digit supervisory code it publishes bare). PURELY INFORMATIONAL. It never changes `valid` and never changes `bank_code_check` — neither publisher allocates bank codes, both relay what national authorities report, and the Banco de España's terms forbid presenting its data as having legal or evidentiary effect. Present only on a match: an institution absent from a list produces no block at all, never a negative one, because absence from these lists is not evidence about the institution. Both publishers permit this reuse on conditions that travel with the data, which is why `source`, `free_of_charge` and `as_of` are always present.
    - `name` string, required — The institution's name as the publisher writes it. May differ from `institution` / `bic.bank_name`, which come from the BIC directory — both are served so the two can be compared rather than one silently overwriting the other.
    - `lei` string, nullable, required — Null where the publisher lists none, which is common for money market funds and branches.
    - `address` string, nullable, required — One-line registered address as published. Null when the publisher gives none.
    - `category` string, required — The publisher's classification.
    - `matched_by` 'lei' | 'national_code', required — lei: joined on the LEI the resolved BIC row carries — exact, and unscoped by country because a legal identity does not change with which of an entity's BICs was asked about. national_code: joined on the bank code the publisher itself publishes (FR five digits, ES four digits).
    - `source` string, required — The publisher, cited as both licences require.
    - `free_of_charge` string, required — Both publishers require that buyers of a product incorporating their data be told, on EVERY access, that the information is available free of charge from the publisher's own website. This API is sold, so that notice ships inside every block rather than living on a documentation page.
    - `attribution` string — The citation formula the Banco de España requires, reproduced verbatim. Spanish blocks only — the ECB asks to be cited as the source, which `source` does.
    - `as_of` string, date, required — Date of the list this row came from, read from the published file and never from a clock. Both lists are republished every business day.
    - `authoritative` false, required — Always false. Both publishers relay; neither allocates bank codes, and the attribution of a code remains the national authority's. Read `bank_code_check.authoritative` for the verdict that can be branched on.
  - `modulus_check` object — UK modulus check on the sorting code and account number a GB IBAN carries — present for GB only, and included at no extra cost in the 0.005 USDC validation. A second checksum, independent of mod-97: the IBAN check digits prove the string was transcribed correctly, this proves the pair is one the owning institution could have issued. A GB IBAN can pass mod-97 and still name an account no bank could have opened, which is what this catches before a payout. passed false NEVER makes the IBAN invalid — read valid and modulus_check.passed as two separate facts. Checksum only: it does not say the account exists, name its holder, or resolve a bank from a sort code.
    - `checked` boolean, required — Whether the published table covers this sorting code. False means no check was possible, not a failed one — Vocalink instructs that such a pair be presumed valid.
    - `passed` boolean, nullable, required — True when the pair satisfies the checksum for that sorting code, false when it cannot be a real account, null when checked is false.
    - `source` string, required
    - `table_fetched_on` string, date, required — The day we fetched the reference table, so a stale server is visible. Not the day Vocalink published it, which is why it is not called as_of like the register dates elsewhere in this response.
  - `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, generate_payment_qr (partner handoff to PayQR on a register-confirmed SEPA account: generate and self-check a SPAYD or EPC/GiroCode payment QR).
    - `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 — The call that performs the step, when one exists: an IBANforge endpoint, or the partner site for a partner handoff.

## Other responses

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

## Changes

> 14 revisions in range; 7 could not be searched.

- **2026-08-29** `9499dba434ea` — 3 info
  - added the optional property `bank_code_check/reason` to the response with the `200` status
  - added the optional property `bic/authoritative` to the response with the `200` status
  - added the optional property `bic/basis` to the response with the `200` status
- **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/9499dba434ea/schema)
