# Corporate Ownership Tree — GLEIF Parent & Subsidiary Chains (`yzy001/gleif-ownership-tree`) Actor

Give it a company name or LEI and get the whole ownership tree from official GLEIF data: the parent chain up to the ultimate parent, the subsidiary tree, and derived fields like depth, path to the ultimate parent, subtree size and countries covered.

- **URL**: https://apify.com/yzy001/gleif-ownership-tree.md
- **Developed by:** [宗諭 楊](https://apify.com/yzy001) (community)
- **Categories:** Lead generation, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 entities

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Corporate Ownership Tree — GLEIF Parent & Subsidiary Chains

Give it a company name or an LEI. Get back the whole ownership tree: every parent up to the
ultimate parent, every subsidiary down to the depth you ask for, and the derived fields you
were going to work out by hand.

The data is GLEIF's official Legal Entity Identifier register — free, public, no API key,
and currently carrying over **3.4 million** registered legal entities.

### What you get per entity

| Field | Example |
|---|---|
| `legalName` / `lei` | `APPLE OPERATIONS INDIA PRIVATE LIMITED` / `2549008YU9EOMHFUA249` |
| **`relationToRoot`** | `self`, `ancestor` or `descendant` |
| **`depth`** | `0` for the company you asked about, `-1` for its parent, `+2` for a grandchild |
| **`pathToUltimateParent`** | `Apple Inc. → Apple Operations Europe` |
| **`isUltimateParent`** / `ultimateParentName` | `false` / `Apple Inc.` |
| **`directChildrenCount`** / **`subtreeSize`** | `8` / `47` |
| `jurisdiction` / `legalCountry` / `hqCountry` | `US-CA` / `US` / `US` |
| `entityStatus` / `registrationStatus` | `ACTIVE` / `ISSUED` |
| `legalCity` / `hqCity` / `legalFormId` | `Glendale` / `Cupertino` / `H1UM` |
| `bic` / `spglobal` | `["APLEUS66XXX"]` / `["24937"]` |
| `lastUpdateDate` / `nextRenewalDate` / `corroborationLevel` | provenance |

Two dataset views: **Ownership tree** (the whole structure) and **Path to ultimate parent**
(the chain, for compliance and KYC write-ups).

### Uses

- **KYC and ultimate beneficial ownership** — the parent chain is the answer to "who
  ultimately controls this counterparty", and `pathToUltimateParent` is the audit trail.
- **Sales and account mapping** — one `subtreeSize` tells you whether a lead is a standalone
  company or one node of a 200-entity group, and `countriesInTree` tells you where.
- **Supplier and sanctions screening** — walk down from a group to find the subsidiaries you
  actually contract with, with the jurisdiction of each.
- **Entity resolution** — a name resolves to an LEI, and every other LEI that matched the
  same name is reported, so near-duplicates surface instead of hiding.

### Input example

```json
{
  "companies": ["Apple Inc.", "Siemens AG", "HWUPKR0MPOU8FGXBT394"],
  "direction": "both",
  "maxDepth": 3,
  "maxChildrenPerNode": 50
}
```

`maxDepth` costs one request per node per level, so deep trees on large groups get
expensive. If a limit cuts the tree off, `treeTruncated` is set and `subtreeSize` becomes a
lower bound rather than a count.

### How it handles the things that catch naive clients

**GLEIF uses HTTP 404 to say "there is no such relationship".** Apple Inc.'s
`/direct-parent` returns 404 with `"Related resource not found"` — because Apple **is** the
ultimate parent. The same endpoint on one of its subsidiaries returns 200 and points back to
Apple Inc. A client that treats 404 as an error reports a failure for **every top-level
company**, which is exactly the layer most users are asking about. This Actor treats that
specific 404 as a valid answer and everything else as a failure.

| Check | What it catches |
|---|---|
| Name not resolved | A name that matches no LEI is reported as a resolution failure, never as "this company has no relationships". If none of the companies resolves, the run is refused with the closest candidates (no rows, so no per-row charge); if some resolve, the rest are named in the warnings. |
| Ambiguous name | Every matching LEI is listed in the summary, with a warning that the first was used. |
| Ownership cycle | Third-party data does contain cycles. The walk carries a path guard and stops with an explanation instead of looping until the run times out. |
| Truncated tree | Hitting `maxDepth` or `maxChildrenPerNode` sets `treeTruncated`, and the summary says which node was cut. |
| Stale registration | An entity whose `registrationStatus` is not `ISSUED` is named in the warnings, because its relationship data may be out of date. |
| Request failure | A non-404 error is raised rather than returned as an empty tree. |

### Notes on the data

- Relationship completeness depends on **self-reporting**. GLEIF does not compel entities to
  file parent or child relationships, so "no subsidiaries" can mean "no subsidiaries
  reported". Do not read an empty tree as proof of a flat group structure.
- `subtreeSize` counts the entities this run actually retrieved. With `treeTruncated` set it
  is a lower bound; `subtreeComplete` says, row by row, whether every node below was expanded
  and fully retrieved.
- `directChildrenCount` is the number of direct subsidiaries GLEIF reports, which can be larger
  than the rows retrieved when `maxChildrenPerNode` cuts a list. It is `null` on ancestor rows
  and on nodes that were not expanded (beyond `maxDepth`, or `direction: "up"`): those were not
  looked up, which is different from having zero subsidiaries.
- Addresses are the legal and headquarters addresses as registered, which for many entities
  is a registered-agent address rather than an operating site.

# Actor input Schema

## `companies` (type: `array`):

Company names or 20-character LEI codes. A name is resolved against GLEIF; if it matches several entities the first is used and every candidate is reported, so give the LEI when you need to be certain which one.

## `direction` (type: `string`):

Walk up to the ultimate parent, down through subsidiaries, or both.

## `acceptBestMatch` (type: `boolean`):

By default a company name must match a registered legal name exactly (after folding case, accents and punctuation) or the run reports the candidates instead of guessing. GLEIF's name filter matches tokens, so 'Siemens AG' returns Siemens Energy AG of Switzerland first - a different company. Turn this on to use the closest match anyway.

## `maxDepth` (type: `integer`):

How many levels of subsidiaries to expand. Each level costs one request per node, so deep trees on large groups get expensive. If the limit cuts the tree off, treeTruncated is set and subtreeSize becomes a lower bound.

## `maxChildrenPerNode` (type: `integer`):

Cap on direct subsidiaries fetched for each entity. If a node has more, the run reports that the tree is incomplete rather than looking complete.

## `delaySeconds` (type: `number`):

Pause between GLEIF requests. The API is free and needs no key; this keeps the request rate polite.

## Actor input object example

```json
{
  "companies": [
    "Apple Inc.",
    "Siemens AG"
  ],
  "direction": "both",
  "acceptBestMatch": false,
  "maxDepth": 3,
  "maxChildrenPerNode": 50,
  "delaySeconds": 0.3
}
```

# Actor output Schema

## `entities` (type: `string`):

LEI, legal name, jurisdiction, status, addresses, and the derived relation, depth, path to the ultimate parent, subtree size and direct subsidiary count.

## `summary` (type: `string`):

How each company name resolved and which other LEIs matched it, ancestor and descendant counts, countries covered, whether the tree was truncated, and the caveats.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "Apple Inc.",
        "Siemens AG"
    ],
    "direction": "both",
    "acceptBestMatch": false,
    "maxDepth": 3,
    "maxChildrenPerNode": 50,
    "delaySeconds": 0.3
};

// Run the Actor and wait for it to finish
const run = await client.actor("yzy001/gleif-ownership-tree").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companies": [
        "Apple Inc.",
        "Siemens AG",
    ],
    "direction": "both",
    "acceptBestMatch": False,
    "maxDepth": 3,
    "maxChildrenPerNode": 50,
    "delaySeconds": 0.3,
}

# Run the Actor and wait for it to finish
run = client.actor("yzy001/gleif-ownership-tree").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    "Apple Inc.",
    "Siemens AG"
  ],
  "direction": "both",
  "acceptBestMatch": false,
  "maxDepth": 3,
  "maxChildrenPerNode": 50,
  "delaySeconds": 0.3
}' |
apify call yzy001/gleif-ownership-tree --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yzy001/gleif-ownership-tree"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2kQt8ip8ej4nh1u1i/builds/oRUdl5VxYZ4gKpYTw/openapi.json
