From an IBAN to the legal entity behind it

An agent holding a bank account, a security or a SIREN can name the company behind it in two calls — GLEIF identity by LEI, BIC, ISIN, SIREN or exact name plus country.

By Matthias Begot ·

An agent that is about to pay an invoice holds an IBAN. An agent reconciling a portfolio holds an ISIN. An agent onboarding a French supplier holds a SIREN. None of those is a company — they are handles that point at one. GET /company/lei turns any of them into an official legal identity: legal name, jurisdiction, legal form, entity and registration status, addresses, national registration number, and the market identifiers mapped to the entity. It answers on a German, Irish, Colombian or Japanese counterparty as readily as a French one, served offline from the GLEIF LEI reference — the public, CC0 global identity register.

The chain worth wiring is two calls long: IBAN → BIC → legal entity. Below it is walked with responses captured from production on 11 August 2026, against the GLEIF snapshot dated 2026-08-10 — including the case where the chain doesn’t resolve, which is the part most integrations get wrong.

The two-call chain, captured

Call 1 — the bank behind the account. GET /iban/resolve validates the IBAN and returns the institution:

{
  "data": {
    "iban": "FR7630004000031234567890143",
    "valid": true, "country": "FR", "bank_code": "30004",
    "bank": { "name": "BNP PARIBAS", "bic": "BNPAFRPP" },
    "reachable": true,
    "sepa_reachability": { "sct": true, "sct_inst": true, "sdd_core": true, "sdd_b2b": true, "match": "exact" }
  },
  "provenance": { "source": "national-bank-registries+epc-sepa-registers", "freshness": { "kind": "snapshot", "as_of": "2026-06-11T00:00:00Z" } }
}

Call 2 — the legal entity behind the bank. GET /company/lei?bic=BNPAFRPP, abridged:

{
  "data": {
    "query": { "key": "bic", "value": "BNPAFRPP" },
    "entity": {
      "lei": "R0MUWSFPU8MPRO8K5P83",
      "legal_name": "BNP PARIBAS",
      "other_names": [{ "name": "HELLO BANK!", "type": "TRADING_OR_OPERATING_NAME" }],
      "legal_form": "K65D",
      "legal_jurisdiction": "FR",
      "entity_status": "ACTIVE",
      "entity_category": "GENERAL",
      "registration": {
        "status": "ISSUED",
        "initial_registration_date": "2012-12-31",
        "last_update_date": "2026-01-26",
        "next_renewal_date": "2027-02-07",
        "managing_lou": "969500Q2MA9VBQ8BG884"
      },
      "legal_address": { "country": "FR", "city": "PARIS", "postal_code": "75009", "line": "16 BOULEVARD DES ITALIENS" },
      "registration_authority": {
        "ra_code": "RA000189", "registration_number": "662042449",
        "siren": "662042449", "verified_against_sirene": true
      },
      "identifiers": { "bics": ["BNPAAEADXXX", "…"], "bics_total": 76, "isins": ["CH0419041642", "…"], "isins_total": 41631 }
    },
    "matches": [], "total_matches": 1
  },
  "provenance": {
    "source": "gleif",
    "fetched_at": "2026-08-11T06:17:27Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-08-10T00:00:00Z" }
  }
}

Three details in that response are worth reading closely.

  • The BIC-8 resolved. GLEIF only publishes BIC-11; an agent that took BNPAFRPP off an IBAN never has the branch suffix. The lookup queries both forms, so BNPAFRPP finds the entity through the mapped BNPAFRPPXXX.
  • other_names carries the trading name. An invoice signed “Hello bank!” and a register entry reading “BNP PARIBAS” are the same counterparty, and the response says so instead of leaving the agent to guess.
  • The lists are capped, the totals are not. bics and isins are capped at 20 entries, but bics_total: 76 and isins_total: 41631 are exact. The agent always knows whether the cap hid something — a truncated list without its count is a silent lie.

From here the third hop is GET /company/ownership: who consolidates this entity. Bank account → institution → group, in three settlements, with no key and no account anywhere in the chain.

Five keys, exactly one per call

KeyWhat an agent typically holds it fromValidation
leiA counterparty record, a regulatory filingISO 17442 check digits
bicAn IBAN (via /iban/resolve), a payment messageForm only — ISO 9362 has no check digit
isinA security, a portfolio lineLuhn check digit
sirenA French invoice, a supplier formLuhn check digit
name + countryA contract header, an email footerExact match after normalization

Zero keys or two keys is a 400 INVALID_INPUT — the service does not rank keys or pick one. A well-formed key that fails its check digit gets a distinct 400 INVALID_CHECKSUM rather than a 404, because those two answers demand opposite reactions: fix your input, versus conclude the entity is not in the register.

GET /company/lei?lei=R0MUWSFPU8MPRO8K5P84
→ 400 { "code": "INVALID_CHECKSUM", "error": "lei failed its ISO 17442 check digits: likely a typo" }

The siren bridge is worth one line of explanation, because it is the one key GLEIF does not publish directly. GLEIF carries a national registration authority and a self-declared, messy registration number per entity; the bridge is derived at ingest under three closed rules — only the two French authorities whose number is a SIREN are bridged, separators are stripped, and a 14-digit SIRET yields its 9-digit prefix. A number that merely looks like a SIREN under another authority is not bridged: a Luhn coincidence is not a declaration. Where the derived SIREN could be cross-checked against the served Sirene register, verified_against_sirene says so — and it has three states, true, false and null, because “GLEIF declares a SIREN that does not exist” and “we had no register to check against” are different facts.

When the chain breaks: the BIC map is self-declared

This is the failure an agent must handle, and it is not exotic. Société Générale’s head-office BIC returns nothing:

GET /company/lei?bic=SOGEFRPP   → 404 NOT_FOUND, not billed

The entity is plainly in the register. Query it by name instead:

GET /company/lei?name=Société%20Générale&country=FR
→ 200 { "entity": { "lei": "O2RNE8IBXP4R0TD8PU41", "legal_name": "SOCIETE GENERALE",
                    "legal_jurisdiction": "FR", "entity_status": "ACTIVE",
                    "registration_authority": { "siren": "552120222", "verified_against_sirene": true },
                    "identifiers": { "bics": ["NEWGFRP1XXX"], "bics_total": 1 } } }

One declared BIC, and it is not the one on the IBAN. The GLEIF BIC mapping reflects what each entity chose to declare, not what SWIFT assigns. So the IBAN → BIC → LEI chain is a high-value shortcut, not a guarantee: BNP Paribas, Crédit Agricole, BRED, Crédit Mutuel and Qonto all resolve from their BIC-8 in the same snapshot; Société Générale and Caisse d’Épargne do not. Wire the fallback — bic first, then name + country, or the siren you already hold from the invoice.

That a 404 here is not billed matters for exactly this reason: an agent that retries through a second key has paid once, for the call that answered.

An ambiguous key returns the ambiguity, never a pick

Several entities can share a normalized name inside one country. The service then serves entity: null, the candidates, and the exact count:

{
  "query": { "key": "name", "value": "victoire", "country": "FR" },
  "entity": null,
  "matches": [
    { "lei": "9695003FOBCTRX2HMC63", "legal_name": "VICTOIRE", "entity_status": "ACTIVE", "registration_status": "ISSUED" },
    { "lei": "969500M534WKMOFRE728", "legal_name": "VICTOIRE", "entity_status": "ACTIVE", "registration_status": "LAPSED" },
    "… 4 more …"
  ],
  "total_matches": 6
}

Six VICTOIRE in France. matches is capped at 10 and total_matches is exact. An agent handed one arbitrarily chosen entity has been given a fact it cannot audit; an agent handed six knows its key is too weak and can escalate to a SIREN or a LEI. Note also query.value: "victoire" — the echoed normalized key, which is why Société Générale matched societe generale above. The matching is exact after normalization and never fuzzy: “close enough” would be a guess wearing an official register’s authority.

LAPSED is a fact, and it is a common one

entity_status and registration.status are served exactly as published, from closed sets, with no editorial comment:

FieldClosed set
entity_statusACTIVE · INACTIVE · the literal NULL GLEIF publishes on some records
registration.statusISSUED · LAPSED · RETIRED · DUPLICATE · ANNULLED · PENDING_TRANSFER · PENDING_ARCHIVAL

Two of those deserve care. A LAPSED registration means the entity did not renew its LEI on the anniversary — it does not mean the company is dead. It is common: GLEIF reported the overall renewal rate stable at 56.6% in Q1 2026, on an active population that passed 3.02 million entities (GLEIF, The LEI in Numbers, 28 April 2026). In the VICTOIRE list above, three of six ACTIVE entities carry a LAPSED registration.

An entity found with a lapsed registration is therefore a 200, and it is billed — that status is the diligence signal the agent came for, delivered raw for the agent’s own policy to interpret. There is no risk score in this response, and there will not be one.

Why a payment agent is meeting the LEI more often

The LEI stopped being a securities-only identifier. CPMI’s harmonised ISO 20022 data requirements for cross-border payments — updated report published 26 February 2026, with adoption flexibility maintained until the end of 2027 (BIS/CPMI) — propose the LEI and/or the BIC to identify financial institutions in payment messages (GLEIF press release). An agent that can move between a BIC and an LEI is moving between the two identifiers the payments stack is converging on.

What it will not do

  • No beneficial ownership. The LEI reference publishes an accounting consolidation parent, never the ultimate beneficial owner under AML rules. That distinction is spelled out on GET /company/ownership.
  • No fuzzy search, no search without a country, no search by address.
  • No financials, no scoring, no invented labels. legal_form is served as the raw ELF code (K65D, SGST) because GLEIF publishes no label for it.
  • No sanctions verdict. Naming an entity is not screening it — that is GET /screen/entity.
  • Register coverage, not world coverage. An entity has a LEI only because it obtained one, typically to trade on markets or to report under a regulation. A small French company has no reason to appear here, and its absence says nothing about it: GET /company/resolve is the French identity card for that case.

Billing and failure modes

CaseResult
Entity found — including LAPSED, INACTIVE, RETIRED200, billed
Key ambiguous — entity: null plus candidates and exact count200, billed
Zero or two keys, name without country, malformed key400 INVALID_INPUT, not billed
Well-formed key failing its check digits400 INVALID_CHECKSUM, not billed
Well-formed key unknown to the reference404 NOT_FOUND, not billed
GLEIF dataset not ingested, or siren= on an image predating the bridge503 DATA_UNAVAILABLE, not settled

That last 503 is deliberate rather than a 404: an image with no bridge table cannot assert “this SIREN has no LEI”, so it declares an unavailability and the four other keys keep working. And a missed refresh keeps serving the last good snapshot under its honest as_ofstaleness is never turned into a 5xx.

Unlike GET /company/peppol, there is no 200 not_registered equivalent here. Peppol publishes a directory, so “absent from it” is a fact worth paying for. GLEIF publishes a register of entities that applied; the absence of a LEI is not a published fact, it is the absence of a record — hence an unbilled 404.

Wiring it up

  1. Pick one key, and rank your fallbacks. lei > siren/isin > bic > name + country. Expect the bic hop to miss on some institutions and fall through, unbilled.
  2. Branch on entity vs matches, not on the array length. entity: null with total_matches > 1 means “narrow your key”, which is a different instruction from “not found”.
  3. Store freshness.as_of with the answer. The response is a snapshot of the GLEIF Golden Copy, and six months from now “on what basis did the agent pay this counterparty” is answerable only if the date was kept.
  4. Read status as input, never as verdict. LAPSED and INACTIVE feed your policy; the service passes no judgement.
  5. Discover, then pay. The 402 → pay → replay cycle is walked with runnable snippets in the Quickstart, and the machine discovery surfaces are described in For agents.

Every amount and every accepted settlement rail lives in the gateway’s /catalog, which is the single source of truth — no price appears in this article. If you would rather buy the whole French counterparty picture in one settlement instead of orchestrating the family yourself, GET /company/report bundles identity, insolvency announcements, VAT, Peppol reachability, LEI and ownership by SIREN.

The captures above are snapshot-dated illustrations, not a contract: a GLEIF refresh moves a renewal date, an entity declares a new BIC, a name becomes ambiguous. Only the rules are guaranteed — one key per call, an ambiguity served as an ambiguity, status served as published, totals exact beside capped lists, and an unanswerable request never billed. The full parameter and field contract is on the GET /company/lei documentation.