# LEI Lookup (`yappman/lei-lookup`) Actor

Look up Legal Entity Identifier (LEI) records by LEI, by name or by registration status, in one fixed row per entity. Sole proprietors' details are withheld. Pay per entity: $0.004, no start fee. Data from the Global LEI Index (CC0). Not affiliated with or endorsed by GLEIF.

- **URL**: https://apify.com/yappman/lei-lookup.md
- **Developed by:** [Russell Yapp](https://apify.com/yappman) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$4.00 / 1,000 entity records

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

## LEI Lookup: Legal Entity Identifier records from the Global LEI Index (CC0)

You get Legal Entity Identifier (LEI) records from the Global LEI Index, one fixed row per entity, built to hold no personal data.

| lei | legalName | registrationStatus | entityStatus | jurisdiction | legalForm | legalAddress | nextRenewalDate |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 2138002P5RNKC5W2JZ46 | TESCO PLC | ISSUED | ACTIVE | GB | B6ES | {"country": "GB", "region": "GB-HRT", "city": "WELWYN GARDEN CITY"} | 2027-01-10T00:00:00Z |

A default run looks up one LEI and costs $0.004, with no start fee. Add up to 1,000 LEIs to the `leis` list to look up more, at $0.004 for each one found; to search by name or status instead, see Modes.

**Run it with the default input.**

[![LEI Lookup on the Apify Store: running the Actor with the default input. Click to play the demo on YouTube.](https://data.yappman.com/assets/lei-lookup-demo.gif)](https://youtu.be/vV4ro9fiNV4)

35-second demo on YouTube: https://youtu.be/vV4ro9fiNV4

### What you get

One row per entity, the same fields in every mode. A missing value is `null` (or `[]` for lists), never an absent key.

| Field | Type | Meaning |
| --- | --- | --- |
| `lei` | string | The 20-character LEI |
| `legalName` | string or null | Legal name. `null` when withheld |
| `otherNames` | array of strings | Other and transliterated names. `[]` when withheld |
| `category` | string or null | Entity category, e.g. `GENERAL`, `FUND`, `SOLE_PROPRIETOR` |
| `subCategory` | string or null | Entity sub-category |
| `legalForm` | string or null | Legal form code (ISO 20275 ELF code) |
| `jurisdiction` | string or null | Legal jurisdiction |
| `entityStatus` | string or null | `ACTIVE` or `INACTIVE` |
| `registrationStatus` | string or null | LEI registration status, e.g. `ISSUED`, `LAPSED`, `MERGED` |
| `corroborationLevel` | string or null | How well the registration data is corroborated |
| `initialRegistrationDate` | string or null | When the LEI was first registered |
| `lastUpdateDate` | string or null | When the record was last updated |
| `nextRenewalDate` | string or null | When the LEI is next due for renewal |
| `managingLou` | string or null | LEI of the issuer that manages this LEI |
| `registeredAt` | string or null | Code of the business register the entity is registered in |
| `registeredAs` | string or null | The entity's number in that register. `null` when withheld |
| `legalAddress` | object or null | `{country, region, city}` only. `null` when withheld |
| `headquartersAddress` | object or null | `{country, region, city}` only. `null` when withheld |
| `successorLei` | string or null | LEI of the successor entity, if any |
| `expirationReason` | string or null | Why the entity's record expired, if it did |
| `bic` | array of strings | BIC codes linked to the LEI |
| `conformityFlag` | string or null | The record's conformity flag |
| `matchType` | string | `lei`, `name`, `similar` or `status`: how the row was found |
| `soleProprietor` | boolean | `true` when the entity's category is `SOLE_PROPRIETOR` or its legal form is a sole-trader form |
| `withheld` | array of strings | Fields withheld because the entity is a person (see Privacy). `[]` otherwise |
| `recordUrl` | string | The official API record for this LEI |
| `goldenCopyDate` | string or null | Publish date of the daily Golden Copy the row was read from |

### Modes

#### By LEI

```json
{
  "mode": "lei",
  "leis": ["2138002P5RNKC5W2JZ46", "5493001KJTIIGC8Y1R12"]
}
```

- 1 to 1,000 LEIs per run, read 200 at a time. Duplicates are looked up once.
- Each LEI is checked as ISO 17442 (20 characters, valid check digits) before any request. A bad one is refused on its own, listed in `RUN_SUMMARY.refused`, and costs nothing; the rest are looked up.
- Rows come back in input order. An LEI the source does not know gives no row and no charge, and is listed in `RUN_SUMMARY.notFound`.

#### By name

```json
{
  "mode": "name",
  "name": "Tesco PLC",
  "country": "GB",
  "status": "ISSUED",
  "maxItems": 10
}
```

- `name`: 2 to 255 characters. It matches the words in a legal name, not the exact string. Characters other than letters, digits, `&`, `.`, `'` and `-` are turned into spaces.
- `country` (optional): two-letter ISO 3166 code of the legal address. `status` (optional): defaults to `ISSUED`; `ANY` for all.
- `maxItems`: default 10, at most 200.
- If nothing matches, similar names are tried and the rows are marked `matchType: "similar"`, filtered by the same `status` and `country`. Otherwise `matchType` is `name`.
- People are left out and counted in `RUN_SUMMARY.withheldPersons`, so fewer rows than `maxItems` can come back.

#### By registration status

```json
{
  "mode": "status",
  "status": "LAPSED",
  "country": "GB",
  "category": "FUND",
  "maxItems": 25
}
```

- `status` and `country` are both required, so a run cannot page the whole world. `status` is one of `ISSUED`, `LAPSED`, `ANNULLED`, `PENDING_TRANSFER`, `PENDING_ARCHIVAL`, `DUPLICATE`, `MERGED`, `RETIRED`.
- `category` (optional): `GENERAL`, `BRANCH`, `FUND`, `RESIDENT_GOVERNMENT_ENTITY` or `INTERNATIONAL_ORGANIZATION`. Sole proprietors are never listed.
- `maxItems`: default 100, at most 10,000. The source does not page past 10,000 results; if that limit is reached, `RUN_SUMMARY.depthCapped` is `true`.
- Rows are sorted by LEI, so the same input on the same data gives the same order. People are left out and counted in `RUN_SUMMARY.withheldPersons`.

### Privacy: people are withheld

A sole proprietor is a person trading under their own name: a record with category `SOLE_PROPRIETOR`, or one registered under a sole-trader legal form (98 ISO 20275 codes, such as an eingetragener Kaufmann, an eenmanszaak or an entreprise individuelle), even if its category says GENERAL. The same goes for legal arrangements named after a person: settlements; family, will, discretionary and life-interest trusts; and "estate of". Organisations whose names merely include "Trust" (charities, trust companies) are not caught by this rule.

- **lei mode:** the row keeps the LEI, the statuses and the dates, so you can still check that the LEI is valid and live. `legalName`, `otherNames`, `registeredAs`, `legalAddress` and `headquartersAddress` are `null` (or `[]`) and are listed in `withheld`. These rows are returned and charged like any other.
- **name and status modes:** these entities are left out of the results and counted in `RUN_SUMMARY.withheldPersons`.
- **Addresses for everyone** are country, region and city only. No street, number or postcode is ever read.

### Pricing

Pay per event.

| Event | Charged | Price |
| --- | --- | --- |
| `entity` | Each row returned | $0.004, flat |

There is no start fee. LEIs not found, LEIs refused as invalid and searches with no results cost nothing. A run looking up 1,000 LEIs that are all found costs $4.00. Cap spend with `maxItems` and with Apify's maximum-charge setting on the run; if your limit is reached the run stops reading and the summary reports `chargeLimitReached`.

### Run summary

Every run writes a `RUN_SUMMARY` record to its key-value store, and the run's status message says in one line what happened. Main fields:

| Field | Meaning |
| --- | --- |
| `status` | `ok`, or `refused` (input rejected, `reason` says why; the run fails and nothing is read or charged) |
| `mode`, `maxItems` | The mode run; `maxItems` in name and status modes |
| `pushed`, `found` | Rows returned |
| `withheldPersons` | lei mode: rows returned with personal details withheld. Name and status modes: people left out |
| `requested`, `notFound`, `refused`, `unavailable` | lei mode only: LEIs given; LEIs the source does not know; invalid LEIs with the reason; LEIs not read because of an error or a stop |
| `alreadyDelivered` | Rows skipped because a restarted run's dataset already held them (not charged again) |
| `matchType` | Name mode: `name` or `similar` |
| `incomplete` | Name and status modes: `true` if the search stopped before it could finish |
| `depthCapped` | Status mode: `true` if the 10,000-result limit was reached |
| `retryAfter` | `null`, or `{seconds, until, leis}` after a rate-limit refusal: retry after `until`. `leis` lists the LEIs still to look up (lei mode) |
| `stoppedBy` | `null`, `refused`, `deadline` or `spending-limit` |
| `payPerEvent`, `chargeLimitReached` | Charging state |
| `api` | Request statistics |
| `goldenCopyDate` | Golden Copy date of the data read |

### Data and licence

- Data is from the Global LEI Index published by GLEIF through its official API, under CC0 per GLEIF's LEI Data Terms of Use. No attribution is required; the source is credited here as a courtesy.
- This Actor is not affiliated with, or endorsed by, GLEIF or any LEI issuer.
- The data is the daily Golden Copy. Each row's `goldenCopyDate` says which one it was read from.
- `recordUrl` is the official API record for the LEI.
- Not included: parent and child relationships, ISINs and history.
- Official source only. No proxies are used.

### Limits

- lei mode: 1 to 1,000 LEIs per run.
- name mode: up to 200 results.
- status mode: up to 10,000 results (the source's depth limit), and `country` is required.
- Pacing: at most one request every 1.5 seconds.
- A rate-limit refusal (HTTP 429 or 503) stops the run's requests at once; there is no retry loop. Rows already read are returned and charged. What was not read is listed in `RUN_SUMMARY.retryAfter` (and `unavailable` in lei mode) with no charge, and the run itself ends normally, so a script or agent can read the summary and retry after `until`.
- The run stops requesting after 10 minutes and reports what it could not read.
- The input takes no URLs, headers or query strings: the Actor builds every request itself.

# Actor input Schema

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

lei: look up Legal Entity Identifiers; name: find entities by legal name; status: list entities by registration status in one country.

## `leis` (type: `array`):

1 to 1,000 LEIs (20 characters, ISO 17442). A malformed LEI is refused on its own, free; an unknown one is listed as not found, free.

## `name` (type: `string`):

An entity's legal name or the words in it, 2 to 255 characters. If nothing matches, similar names are tried and marked matchType similar.

## `status` (type: `string`):

Name mode: defaults to ISSUED (ANY for all). Status mode: required.

## `country` (type: `string`):

Two-letter ISO 3166 code of the legal address, e.g. GB. Optional in name mode, required in status mode.

## `category` (type: `string`):

Optional. Sole proprietors are people and are never listed.

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

Name mode: default 10, at most 200. Status mode: default 100, at most 10,000. Each entity returned is charged.

## Actor input object example

```json
{
  "mode": "lei",
  "leis": [
    "2138002P5RNKC5W2JZ46"
  ]
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

# 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 = {
    "leis": [
        "2138002P5RNKC5W2JZ46"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yappman/lei-lookup").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 = { "leis": ["2138002P5RNKC5W2JZ46"] }

# Run the Actor and wait for it to finish
run = client.actor("yappman/lei-lookup").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 '{
  "leis": [
    "2138002P5RNKC5W2JZ46"
  ]
}' |
apify call yappman/lei-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yappman/lei-lookup"
        }
    }
}
```

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/mBbVng2hGfXEBsmCJ/builds/bfJ43tmeeqkes0c7w/openapi.json
