# Corporate Family Tree — Parents & Subsidiaries by LEI (`mouadapi/gleif-family-tree`) Actor

Returns a company's corporate family tree from GLEIF: its direct and ultimate parents, its subsidiaries and theirs (two levels), one row per entity with LEI, name, country and status; failed and not-found rows are never charged. Not affiliated with or endorsed by GLEIF.

- **URL**: https://apify.com/mouadapi/gleif-family-tree.md
- **Developed by:** [COMPASS DEV](https://apify.com/mouadapi) (community)
- **Categories:** Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 tree entities

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

Returns a company's corporate family tree from GLEIF: its direct and ultimate parents, its subsidiaries and theirs (two levels), one row per entity with LEI, name, country and status; failed and not-found rows are never charged.

Give LEIs (or exact legal names) and get each company's group as flat rows: the company itself, its direct and ultimate
parent, its direct subsidiaries and their subsidiaries, each with its LEI, legal name, country, legal form and status, and
where it sits in the tree (`relation`, `level`, `parentLei`). Built for KYB and supplier checks ("who owns this company,
and what else does the group own?"), group exposure, and AI agents. *Not affiliated with or endorsed by GLEIF.*

### What it does

- **Up and down the tree:** `relation` is `self`, `direct_parent`, `ultimate_parent` or `subsidiary`; `level` is 0, -1, 1
  or 2; each subsidiary names its own parent in the tree (`parentLei`).
- **Two levels of subsidiaries** (`depth` 2, the default), read only where GLEIF links children, so a small group takes a
  few requests and a large one is read 100 subsidiaries a request. `subsidiariesTotal` says how many direct subsidiaries
  GLEIF lists.
- **Big groups stay bounded:** a tree stops at `maxEntities` rows (500 by default); its rows say `truncated`.
- **LEIs or names:** an LEI is looked up directly; a legal name must match a record exactly (case, punctuation and
  legal-form spelling ignored; add " | DE" for the country). Several matches give a free row with the candidates; for messy
  names, find the LEI first with [LEI Lookup & Company Name Matcher](https://apify.com/mouadapi/company-id-matcher).
- **Watch mode** (give a watch list name in `stateName`): returns only entities new or changed in the trees since the last
  run (a new subsidiary, a new parent, a name or status change). A watch run with nothing new returns **exactly one free
  `no_data` row** that says so.
- **No people:** organisation records only. A record that is, may be, or names a person (sole proprietors, legal forms
  off GLEIF's organisation list, a person's name in a name or address) is never returned, only counted
  (`peopleLeftOut`); a company whose parent is a person is left out entirely.
- **You are never charged for failed results:** failed, no_data and unchanged rows are free.

### Quick start

```json
{ "companies": ["5493003NRW7LS2QZRH60", "ICU Medical B.V. | NL"] }
```

A large group, direct subsidiaries only, without parents:

```json
{ "companies": ["7LTWFZYICNSX8D621K86"], "depth": 1, "includeParents": false, "maxEntities": 1000 }
```

Watch your suppliers' groups every month (with an Apify schedule):

```json
{ "companies": ["5493003NRW7LS2QZRH60"], "stateName": "supplier-groups" }
```

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `companies` | list of LEIs or legal names | — (prefilled with 2 examples) | One per line; a name may end with " | CC" (a country code). Never a URL; a person's name is refused |
| `depth` | 1 or 2 | `2` | 1 = direct subsidiaries; 2 = their subsidiaries too |
| `includeParents` | boolean | `true` | Return the direct and ultimate parents |
| `maxEntities` | integer 1–5,000 | `500` | Most rows per company's tree; a larger group is cut there |
| `mode` | `watch` or `export` | — | Empty: watch when `stateName` is given, otherwise export. An explicit mode wins |
| `stateName` | string | — | Watch list name. Watch mode without a name uses the list `default` |
| `maxItems` | integer 1–10,000 | `1000` | Most charged rows per run; in watch mode the rest come in the next run |

### Output

```json
{
    "status": "ok",
    "attempts": 1,
    "error": null,
    "input": "5493003NRW7LS2QZRH60",
    "mode": "export",
    "watchList": null,
    "noDataReason": null,
    "rootLei": "5493003NRW7LS2QZRH60",
    "rootName": "ICU MEDICAL, INC.",
    "relation": "subsidiary",
    "level": 1,
    "parentLei": "5493003NRW7LS2QZRH60",
    "lei": "254900891YA8DKEHTV75",
    "legalName": "ICU Medical B.V.",
    "legalCountry": "NL",
    "legalForm": "54M6",
    "entityStatus": "ACTIVE",
    "registrationStatus": "ISSUED",
    "hasSubsidiaries": false,
    "subsidiariesTotal": 0,
    "peopleLeftOut": 0,
    "truncated": false,
    "changeType": null,
    "changedFields": null,
    "previousValues": null,
    "url": "https://search.gleif.org/#/record/254900891YA8DKEHTV75",
    "source": "GLEIF API (Global Legal Entity Identifier Foundation). Not affiliated with or endorsed by GLEIF.",
    "license": "CC0 1.0 (GLEIF LEI Data Terms of Use)",
    "licenseUrl": "https://www.gleif.org/en/meta/lei-data-terms-of-use",
    "scrapedAt": "2026-10-06T23:30:00.000Z"
}
```

| Status | Meaning | Charged? |
|---|---|---|
| `ok` | An entity of a company's tree | Export: yes. Watch: `baseline`, `new` and `changed` yes; unchanged entities no |
| `no_data` | No LEI record with this code or a record left out, no exact name match, several, or nothing new since the last run | No |
| `failed` | An invalid entry (a URL, a person's name, an LEI with wrong check digits) or no answer from GLEIF (`error` says why) | No |

The key-value store holds `RUN_REPORT` (counts, charged and free rows, trees cut, the GLEIF data set's date, stop reason).

### Output fields

| Field | Type | Description |
|---|---|---|
| `status` | string (or null) | ok = an entity of a company's tree (charged in export mode, and in watch mode when it is a baseline, new or changed entity); no_data = no such LEI or a record left out by the people filter, no exact name match, several, or nothing new since the last run (free); failed = an invalid entry (a URL, a person's name, a wrong LEI) or no answer (free) |
| `attempts` | integer (or null) | Requests made for this row (retries included) |
| `error` | string (or null) | Why a row is no_data or failed; null on ok rows |
| `input` | string (or null) | The company as you gave it |
| `mode` | string (or null) | watch or export |
| `watchList` | string (or null) | Watch list name (watch mode); null in export mode |
| `noDataReason` | string (or null) | not_found (no LEI record, or one left out), no_match (no exact legal name), ambiguous (several records with that name), nothing_new (watch); null otherwise |
| `rootLei` | string (or null) | The LEI of the company asked for (the tree's root) |
| `rootName` | string (or null) | The legal name of the company asked for |
| `relation` | string (or null) | self (the company asked for), direct_parent, ultimate_parent or subsidiary |
| `level` | integer (or null) | 0 for the company, -1 for a parent, 1 for a direct subsidiary, 2 for a subsidiary of a subsidiary |
| `parentLei` | string (or null) | For a subsidiary: the LEI of its direct parent in the tree |
| `lei` | string (or null) | The entity's Legal Entity Identifier (ISO 17442) |
| `legalName` | string (or null) | The legal name on the entity's LEI record |
| `legalCountry` | string (or null) | The country of the legal address (ISO 3166-1 alpha-2) |
| `legalForm` | string (or null) | The legal form code on the record (ISO 20275 ELF code, or the text given) |
| `entityStatus` | string (or null) | ACTIVE or INACTIVE (GLEIF) |
| `registrationStatus` | string (or null) | ISSUED, LAPSED, RETIRED … (GLEIF) |
| `hasSubsidiaries` | boolean (or null) | True when GLEIF links direct children to this entity |
| `subsidiariesTotal` | integer (or null) | How many direct children GLEIF lists for this entity, when they were read (0 when it links none); null when not read (parents, or level 2) |
| `peopleLeftOut` | integer (or null) | This entity's direct children left out by the people filter (are, may be, or name a person): counted, never listed |
| `truncated` | boolean (or null) | True on every row of a tree cut at maxEntities |
| `changeType` | string (or null) | Watch mode: baseline (first run of the list), new (an entity added to a tree) or changed (its parent, name or status); null in export mode |
| `changedFields` | string (or null) | Watch mode: the tracked fields that changed, comma-separated |
| `previousValues` | string (or null) | Watch mode: the changed fields' previous values, as JSON |
| `url` | string (or null) | The entity's record on GLEIF's public LEI search |
| `source` | string (or null) | Where the data comes from |
| `license` | string (or null) | The terms of the data |
| `licenseUrl` | string (or null) | Where the terms are published |
| `scrapedAt` | string (or null) | When the row was written (ISO 8601, UTC) |

### Pricing

Pay per event: one `tree-entity` event per entity row (export: every entity; watch: `baseline`, `new` and `changed`).

| Plan | Price per entity | Per 1,000 entities |
|---|---|---|
| Free (no discount) | $0.0015 | $1.50 |
| Bronze | $0.0013 | $1.30 |
| Silver | $0.00115 | $1.15 |
| Gold (and Platinum, Diamond) | $0.0010 | $1.00 |

- Never charged: failed rows, no_data rows and unchanged entities. You are never charged for failed results.
- Set `maxEntities` and `depth` to bound the cost for a large group. Apify also charges its small per-run start event;
  there are no usage fees on top, so the actor is eligible for x402 agent payments.

### Use it from AI agents

One clear main input, `companies` (LEIs or exact legal names); every row has `status`, `relation`, `level`, `lei`,
`legalName`, `legalCountry` and `parentLei`. Call it through the Apify API, the Apify MCP server
(`mouadapi/gleif-family-tree`) or x402 agentic payments. Copy-paste call (your Apify token in place of `YOUR_APIFY_TOKEN`):

```bash
curl -X POST "https://api.apify.com/v2/acts/mouadapi~gleif-family-tree/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" -H "Content-Type: application/json" -d '{"companies": ["5493003NRW7LS2QZRH60"]}'
```

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

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('mouadapi/gleif-family-tree').call({ companies: ['7LTWFZYICNSX8D621K86'], depth: 1, maxEntities: 200 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Limits

- Only relationships reported to GLEIF: a subsidiary without an LEI, or a parent the company reports as an exception, is not
  in the tree.
- Watch mode reports new and changed entities; an entity that left a tree is not reported.
- GLEIF publishes no rate limit: this actor sends one request a second at most. On HTTP 429 it pauses 60, 120 and 240
  seconds on the same connection, then stops with free `failed` rows. No other IP or proxy is ever tried.

### Known issues

- A very large group (thousands of subsidiaries) is cut at `maxEntities`; raise it, or use `depth` 1.

### FAQ

**Why does a company have no parents?** GLEIF records no parent LEI for it: it is the top of its group, or it reported an
exception (no consolidating parent, or a parent that is not to be disclosed).

**Why did a watch run return one `no_data` row?** Nothing changed in the trees since the last run of the watch list; the
row says how many entities were unchanged. It is free.

### Data and licence

- Source: the GLEIF API (api.gleif.org), documented at https://www.gleif.org/en/lei-data/gleif-api.
- Licence: the LEI data are provided under CC0 1.0 (GLEIF LEI Data Terms of Use,
  https://www.gleif.org/en/meta/lei-data-terms-of-use).
- This actor is not affiliated with or endorsed by GLEIF.

# Actor input Schema

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

One company per line: an LEI (e.g. 7LTWFZYICNSX8D621K86) or the legal name as registered, optionally with a country code after a bar ("ICU Medical B.V. | NL"). Never a URL; a person's name is refused.

## `depth` (type: `integer`):

How far down: 1 = direct subsidiaries, 2 = their subsidiaries too.

## `includeParents` (type: `boolean`):

Return the direct and ultimate parents too.

## `maxEntities` (type: `integer`):

Most rows per company (itself, its parents and subsidiaries); a larger group is cut there (truncated is true on its rows).

## `mode` (type: `string`):

Empty: watch when a watch list name is given, otherwise export. "watch" returns only entities new or changed in the trees since the last run of the list; "export" returns every entity. An explicit mode wins.

## `stateName` (type: `string`):

Name of your watch list (kept in your own storage between runs). Giving a name turns on watch mode; watch mode without a name uses the list "default".

## `maxItems` (type: `integer`):

Most entity rows returned and charged per run. In watch mode the rest come in the next run.

## Actor input object example

```json
{
  "companies": [
    "5493003NRW7LS2QZRH60",
    "ICU Medical B.V. | NL"
  ],
  "depth": 2,
  "includeParents": true,
  "maxEntities": 500,
  "maxItems": 1000
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with one row per entity (status ok / no_data / failed)

## `runReport` (type: `string`):

Summary of the run (counts, charged and free rows, trees cut, stop reason)

# 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": [
        "5493003NRW7LS2QZRH60",
        "ICU Medical B.V. | NL"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mouadapi/gleif-family-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": [
        "5493003NRW7LS2QZRH60",
        "ICU Medical B.V. | NL",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("mouadapi/gleif-family-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": [
    "5493003NRW7LS2QZRH60",
    "ICU Medical B.V. | NL"
  ]
}' |
apify call mouadapi/gleif-family-tree --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mouadapi/gleif-family-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/O6Hp1bze57EBVg6Hg/builds/QnaSTM4sLJ6pSxTYx/openapi.json
