GET /company/ownership
Answers “who controls this counterparty?” from a LEI: the direct parent, the ultimate parent, the dated chain of links between them, and optionally the direct subsidiaries. The answer is served from a local store built from the official GLEIF Level 2 relationship records - the parents entities declare to GLEIF, published under CC0 - with no network call and no secret at request time.
This is what makes sanctions screening actually useful. A French counterparty
whose own record is spotless, owned by a designated entity, is exactly the case
an endpoint-by-endpoint check walks past. Screening the account tells you about
the account; this tells you about the group above it. See the live
/catalog for the authoritative endpoint listing and
price.
x402 golden rule: the agent pays for the answer to its question. A chain
that comes back empty because the entity published a reason for not
reporting a parent is a successful answer -> 200 billed. Only requests
the service cannot answer - an unknown LEI, a malformed input, a depth out of
bounds - leave the 200 range, and they are not billed.
Accounting-consolidation parent ≠ beneficial owner
Read this before using the response. GLEIF Level 2 publishes the accounting consolidation parent - the entity that consolidates this one in its financial statements - and nothing else. It is not the ultimate beneficial owner (UBO) as defined by AML/KYC rules, it carries no ownership percentage (Level 2 publishes none), and the absence of a declared parent is not evidence of an absence of control.
The response says so itself, in a fixed
data.limitssentence that is part of the contract. Treatingultimate_parentas a UBO would be selling one piece of data as another - so the service states the distinction rather than letting an integration assume it away.
If you need beneficial ownership, this endpoint is not it, and no endpoint in this catalog claims to be.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
lei | string | yes | 20-character LEI, ISO 17442 check digits verified before any lookup |
direction | string | no | up (default) · down · both - walk parents, subsidiaries, or both |
depth | number | no | 1-5, default 3 - how many links the upward walk may follow |
GET /company/ownership?lei=9695000AHZHGK28ZO963&depth=5
GET /company/ownership?lei=R0MUWSFPU8MPRO8K5P83&direction=down
A depth outside 1..5 is a 400, never a silent clamp: an agent that
asked for 7 must know it was not served at 7. An unknown direction is a
400 too.
200 response - UnifiedResponse
{
"data": { "subject": { ... }, "direct_parent": { ... }, "ultimate_parent": { ... }, "chain": [ ... ], "limits": "..." },
"provenance": {
"source": "gleif",
"fetched_at": "2026-07-29T15:44:09Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
Fields of data
| Field | Type | Description |
|---|---|---|
query | object | { lei, direction, depth } echoed back, defaults resolved |
subject | object | Short identity of the queried entity - same shape as the matches of /company/lei |
direct_parent | object | { status, link, exception } - the entity that directly consolidates the subject |
ultimate_parent | object | Same shape - the top of the consolidation chain, as declared |
chain | array | Upward walk, link by link, bounded by depth; null when direction=down |
chain_truncated | bool | The walk stopped on the depth bound while a parent still existed above |
cycle_detected | bool | An already-visited LEI cut the walk |
direct_children | array | Entities declaring the subject as their direct parent, capped at 100; null when direction=up |
children_truncated | bool | The cap hid subsidiaries |
total_children | number | Exact count of direct subsidiaries; null when not requested |
limits | string | The fixed sentence above - parents are self-declared accounting consolidation, not beneficial ownership |
null and [] are different answers. null means “you did not ask for
this block”; [] means “you asked, and the reference declares nothing”. An
agent must be able to tell those apart, so the service never collapses one into
the other.
direct_parent / ultimate_parent
status | Meaning | Populated field |
|---|---|---|
declared | A relationship is published | link |
reporting_exception | The entity published a reason for not reporting a parent | exception |
not_declared | Neither a relationship nor a published exception | neither |
link and exception are never both populated.
link:{ lei, entity, relationship_status, period: { start, end } }. The join to the identity record is an outer one: a declared parent whose Level 1 record is absent is served with its LEI andentity: null- the link is published, the record is not, and neither is hidden nor invented.exception:{ category, reasons }, carrying the GLEIF reason codes as published -NATURAL_PERSONS,NON_CONSOLIDATING,NO_KNOWN_PERSON,NO_LEI- with no added label, because the Golden Copy publishes none.
A parent in force always wins over a historical one. When several
relationships exist, an ACTIVE one beats an INACTIVE one; at equal status,
the most recent period, then the LEI. The order never depends on what the
storage engine happens to return. The case is real - serving a stale link would
name a wrong parent.
Relationship statuses are served as published, including the literal NULL
GLEIF publishes on a handful of records.
Example - a bounded control chain
GET /company/ownership?lei=9695000AHZHGK28ZO963&depth=5, against the
2026-07-28 GLEIF snapshot. LE MARQUIS is a Paris property company whose own
identity gives away nothing about its owner; the walk goes through CNP
Assurances and La Banque Postale up to the Caisse des dépôts et
consignations. depth=5 stops one link short of the top - the fact is
told (chain_truncated: true), and the ultimate parent is still served
because it is declared, not derived from the walk.
{
"data": {
"query": { "lei": "9695000AHZHGK28ZO963", "direction": "up", "depth": 5 },
"subject": {
"lei": "9695000AHZHGK28ZO963", "legal_name": "LE MARQUIS",
"legal_jurisdiction": "FR", "legal_country": "FR",
"entity_status": "ACTIVE", "registration_status": "ISSUED"
},
"direct_parent": {
"status": "declared",
"link": {
"lei": "969500B5IW6GWFPJ1347",
"entity": { "lei": "969500B5IW6GWFPJ1347", "legal_name": "ASSURECUREUIL PIERRE 4",
"legal_jurisdiction": "FR", "legal_country": "FR",
"entity_status": "ACTIVE", "registration_status": "ISSUED" },
"relationship_status": "ACTIVE",
"period": { "start": "2002-12-24", "end": null }
},
"exception": null
},
"ultimate_parent": {
"status": "declared",
"link": {
"lei": "969500Q2PFTTP0Y5QL44",
"entity": { "lei": "969500Q2PFTTP0Y5QL44",
"legal_name": "CAISSE DES DEPOTS ET CONSIGNATIONS",
"legal_jurisdiction": "FR", "legal_country": "FR",
"entity_status": "ACTIVE", "registration_status": "ISSUED" },
"relationship_status": "ACTIVE",
"period": { "start": "2002-12-24", "end": null }
},
"exception": null
},
"chain": [
{ "lei": "969500B5IW6GWFPJ1347", "entity": { "legal_name": "ASSURECUREUIL PIERRE 4", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2002-12-24", "end": null } },
{ "lei": "969500QKVPV2H8UXM738", "entity": { "legal_name": "CNP ASSURANCES", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "1994-10-14", "end": null } },
{ "lei": "9695008OXM90U326XP05", "entity": { "legal_name": "CNP ASSURANCES HOLDING", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2023-01-01", "end": null } },
{ "lei": "96950066U5XAAIRCPA78", "entity": { "legal_name": "LA BANQUE POSTALE", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2009-07-25", "end": null } },
{ "lei": "9695000YG7TR7PAP0L59", "entity": { "legal_name": "LA POSTE", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2005-12-31", "end": null } }
],
"chain_truncated": true,
"cycle_detected": false,
"direct_children": null,
"children_truncated": false,
"total_children": null,
"limits": "Relationships are self-declared by the entities to GLEIF and describe accounting consolidation (the parent that consolidates the entity in its financial statements). This is NOT beneficial ownership under AML/KYC rules, and Level 2 publishes no ownership percentage. The absence of a declared parent is not evidence of an absence of control."
},
"provenance": {
"source": "gleif",
"fetched_at": "2026-07-29T15:44:09Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
}
96950066U5XAAIRCPA78 (La Banque Postale) is reachable from
/company/lei?bic=: an agent can chain
iban/resolve -> company/lei -> company/ownership and end on the same
entity.
Example - an absence that is a declaration, not a silence
GET /company/ownership?lei=2138002I6NWA7IG4J251. SOFIGA., a family holding:
Level 2 names nobody, but says why. That is exactly where what the LEI
reference knows stops - and the agent reads it in the response instead of
inferring “no parent” from an empty block.
{
"data": {
"query": { "lei": "2138002I6NWA7IG4J251", "direction": "up", "depth": 3 },
"subject": { "lei": "2138002I6NWA7IG4J251", "legal_name": "SOFIGA.",
"legal_jurisdiction": "FR", "legal_country": "FR",
"entity_status": "ACTIVE", "registration_status": "LAPSED" },
"direct_parent": {
"status": "reporting_exception",
"link": null,
"exception": { "category": "DIRECT_ACCOUNTING_CONSOLIDATION_PARENT",
"reasons": ["NATURAL_PERSONS"] }
},
"ultimate_parent": {
"status": "reporting_exception",
"link": null,
"exception": { "category": "ULTIMATE_ACCOUNTING_CONSOLIDATION_PARENT",
"reasons": ["NATURAL_PERSONS"] }
},
"chain": [],
"chain_truncated": false,
"cycle_detected": false,
"direct_children": null,
"total_children": null,
"limits": "…"
}
}
NATURAL_PERSONS means the entity is held by individuals, who have no LEI -
1.15M records carry that reason. The subject’s own
registration_status: "LAPSED" is served as-is: a diligence signal, not a
verdict.
Example - direct subsidiaries, capped and counted
GET /company/ownership?lei=R0MUWSFPU8MPRO8K5P83&direction=down. BNP PARIBAS
is a group head with no declared parent (published reason NO_KNOWN_PERSON)
and 137 direct subsidiaries at the 2026-07-28 image, of which 100 are
served.
{
"data": {
"query": { "lei": "R0MUWSFPU8MPRO8K5P83", "direction": "down", "depth": 3 },
"subject": { "legal_name": "BNP PARIBAS", "…": "…" },
"direct_parent": {
"status": "reporting_exception", "link": null,
"exception": { "category": "DIRECT_ACCOUNTING_CONSOLIDATION_PARENT",
"reasons": ["NO_KNOWN_PERSON"] }
},
"chain": null,
"direct_children": [
{ "lei": "2138002Y7Q2J14MAI422",
"entity": { "legal_name": "BNP PARIBAS FUND ADMINISTRATION SERVICES (IRELAND) LIMITED",
"legal_country": "IE", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2017-05-15", "end": null } },
{ "lei": "21380036SM8RMK9HM241",
"entity": { "legal_name": "CARDIF COLOMBIA SEGUROS GENERALES S A",
"legal_country": "CO", "…": "…" },
"relationship_status": "ACTIVE", "period": { "start": "2017-07-11", "end": null } }
],
"children_truncated": true,
"total_children": 137,
"limits": "…"
}
}
chain: null (not requested) is not chain: [] (requested, nothing to walk).
The distinction is the contract.
Cycles are cut and reported
The upward walk keeps the set of visited LEIs; an already-seen LEI stops it
and raises cycle_detected: true. Real consolidation data has no cycle, but
nothing in the format forbids one - and an infinite loop on a perfectly valid
input would be a 5xx or worse. The guard is a behaviour of the contract, not an
implementation detail: if it fires, the agent is told.
What this endpoint will not do
- No beneficial ownership (UBO), no ownership percentages, no fund relationships - none of them are published in Level 2.
- No automatic screening of the parents against the sanctions lists. That
composition is the job of the
/preflight/*composites, not of this route. - No multi-level downward org chart - only direct subsidiaries are served, capped at 100 with the exact total.
- No coverage beyond the LEI reference. An entity without a LEI has no relationship record, and a company whose parents simply never declared has an empty chain. Neither is evidence of anything.
Freshness
Answers come from a snapshot of the GLEIF Golden Copy; freshness.as_of
carries its publication date. If a refresh is missed, the last good snapshot
keeps being served - honestly dated - and staleness is never turned into a
5xx.
Errors
Only requests the service cannot answer leave the 200 range - none of them are billed.
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | lei absent |
| 400 | INVALID_INPUT | Malformed lei, depth outside 1..5, unknown direction |
| 400 | INVALID_CHECKSUM | Well-formed LEI failing its ISO 17442 check digits - a probable typo |
| 404 | NOT_FOUND | Well-formed LEI unknown to the reference; also unknown routes |
| 503 | DATA_UNAVAILABLE | The GLEIF dataset has not been ingested yet |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
An empty chain, a parent that is not declared and an empty subsidiary list are answers (“the reference declares nothing on this”), not errors, and they are served as billed 200s.
The 404 message is shared with /company/lei: the
same key cannot exist for one route and not the other.
Attribution
Relationship data comes from the GLEIF Level 2 Golden Copy (relationship records and reporting exceptions), published under CC0 1.0 - no account, no key, no attribution requirement, redistributable.
See also
GET /company/lei- the legal identity behind a LEI, BIC, ISIN, SIREN or exact name plus country.GET /company/report- the counterparty due-diligence report by SIREN, which carries the ultimate parent alongside identity, BODACC events, VAT and Peppol.GET /iban/screen- OFAC/EU/UN sanctions screening on the counterparty’s bank account, the check this one completes.- For agents - discovery surfaces, the live
/catalogand how settlement works.