# LEI Lookup: Bulk LEI Search, Company Matching & Parents (`guileless_concerto/lei-lookup`) Actor

Bulk LEI lookup and company-name-to-LEI matching with match scores, plus direct and ultimate parent companies in the same row. Free public Global LEI data (CC0), no API key.

- **URL**: https://apify.com/guileless_concerto/lei-lookup.md
- **Developed by:** [Scott P](https://apify.com/guileless_concerto) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 lei 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: bulk LEI search, company name matching & parent companies

Look up **Legal Entity Identifiers (LEIs)** in bulk, **match company names to LEIs** even when they're misspelled, and get each entity's **direct and ultimate parent company** in the same flat row. The data comes from the public **Global LEI Index** through the free GLEIF API (no API key, no login), released under **CC0 1.0**.

Built for KYC/AML onboarding, counterparty and vendor master-data cleanup, regulatory reporting (EMIR, MiFID II, SFTR), and corporate-ownership research.

### Why this Actor

- **Real bulk lookup:** 200 LEIs per API request, so 10,000 LEIs take about 50 requests instead of 10,000. Unknown or invalid LEIs (bad ISO 17442 checksum) come back as free rows flagged `found: false`, so you can see exactly which of your codes failed.
- **Company name to LEI matching:** each candidate gets a **0-100 `matchScore`**. Legal forms are normalised (AG = Aktiengesellschaft, Ltd = Limited, Inc = Incorporated ...), and fuzzy matching catches typos ("siemens mobilty" still finds Siemens Mobility). Set `maxMatchesPerName: 1` for best-match-only entity resolution.
- **Parent chain in the same row:** direct parent and ultimate parent LEI, name and country. When no parent is reported, you get the declared reason instead (e.g. `NATURAL_PERSONS`, `NON_CONSOLIDATING`, `NO_KNOWN_PERSON`). There is no per-record parent request, because parent names are fetched in bulk.
- **Browse mode:** list LEIs by country, entity status and LEI registration status (e.g. all **LAPSED** LEIs in Luxembourg, for renewal outreach or data-quality checks).

### Input

| Field | Description |
|---|---|
| `leis` | List of LEI codes for bulk lookup. |
| `companyNames` | Company names to match to LEIs. |
| `fuzzyMatch` | Also use fuzzy completions for misspelled names (default on). |
| `maxMatchesPerName` | Candidates returned per name (default 3; 1 = best match only). |
| `minMatchScore` | Drop candidates below this score (default 60). |
| `countries` | 2-letter ISO codes of the legal address (name matching & browse). |
| `entityStatus` | `ANY` / `ACTIVE` / `INACTIVE`. |
| `registrationStatuses` | e.g. `ISSUED`, `LAPSED`, `RETIRED`. |
| `includeParents` | Add direct/ultimate parent columns (default on). |
| `maxResults` | Maximum number of records returned. |

Examples:

```json
{ "leis": ["529900T8BM49AURSDO55", "5493001KJTIIGC8Y1R12"] }
```

```json
{ "companyNames": ["Deutsche Bank AG", "siemens mobilty"], "maxMatchesPerName": 1, "entityStatus": "ACTIVE" }
```

```json
{ "countries": ["LU"], "registrationStatuses": ["LAPSED"], "maxResults": 500 }
```

### Output (one flat row per LEI record)

`inputType`, `inputQuery`, `found`, `matchScore`, `matchRank`, `lei`, `legalName`, `otherNames`, `entityStatus`, `registrationStatus`, `entityCategory`, `legalFormCode` (ISO 20275 ELF), `jurisdiction`, `registeredAs` (business-register number), `registrationAuthorityId`, legal and headquarters address (line, city, region, postal code, country), `entityCreationDate`, `leiInitialRegistrationDate`, `leiLastUpdateDate`, `leiNextRenewalDate`, `corroborationLevel`, `bic`, `openCorporatesId`, `successorLei`, `directParentLei`, `directParentName`, `directParentCountry`, `directParentStatus`, `directParentExceptionReason`, `ultimateParent…` (same fields), `hasSubsidiaries`, `gleifRecordUrl`, `source`.

```json
{
  "inputType": "name",
  "inputQuery": "siemens mobilty",
  "matchScore": 97,
  "lei": "529900R6QFNA89TXEZ72",
  "legalName": "Siemens Mobility",
  "legalAddressCountry": "BE",
  "entityStatus": "ACTIVE",
  "registrationStatus": "ISSUED",
  "directParentName": "Siemens Aktiengesellschaft",
  "directParentCountry": "DE",
  "ultimateParentName": "Siemens Aktiengesellschaft"
}
```

### Pricing

Pay per result: **$0.0025 per LEI record** returned, plus a small flat start fee per run. Invalid and not-found lookups are free. Set **Maximum cost per run** and the Actor stops cleanly at your limit.

### Rate limits

The GLEIF API is free and needs no registration. This Actor stays at about 0.9 requests per second (below the commonly documented 60 requests/minute) and backs off automatically on HTTP 429/5xx. Name matching uses 2-5 requests per name; bulk LEI lookup uses about 2 requests per 200 LEIs.

### Notes

- `matchScore` is a heuristic string similarity, not a guarantee of identity. For compliance decisions, confirm the LEI, legal name, jurisdiction and registry number.
- LEI records describe legal entities. Some entities (e.g. sole traders) carry a person's name, as published in the public Global LEI Index. For records with category `SOLE_PROPRIETOR`, this Actor withholds street address, postal code and city.
- Parent data reflects what entities reported as accounting-consolidation parents (LEI "Level 2" data).

### Data source, licence and disclaimer

LEI data: **Global LEI Index, accessed through the GLEIF API (https://api.gleif.org)**, available free of charge under the **CC0 1.0** licence (see GLEIF's LEI Data Terms of Use: https://www.gleif.org/en/meta/lei-data-terms-of-use). **This Actor is an independent tool. It is not provided, endorsed, or authorised by, or otherwise associated with, GLEIF or any LEI issuer (LOU).** It does not use GLEIF trademarks or logos. Match scores, normalisation and flattening are this Actor's own processing, not GLEIF data. Data is provided as is; see GLEIF's terms for the disclaimer.

### Changelog

- 0.1: first version (bulk lookup, name matching with scores, parent chain, browse mode).

# Actor input Schema

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

List of 20-character LEI codes. Looked up 200 per API request. Invalid or unknown LEIs are reported free of charge.

## `companyNames` (type: `array`):

Company names to match to LEIs. Each candidate gets a 0-100 match score; typos and legal-form differences (AG / Aktiengesellschaft, Ltd / Limited) are handled.

## `fuzzyMatch` (type: `boolean`):

Also use GLEIF fuzzy completions so misspelled names still match (one or two extra API requests per name).

## `maxMatchesPerName` (type: `integer`):

How many best-scoring candidates to return per company name. Use 1 for 'best match only' entity resolution.

## `minMatchScore` (type: `integer`):

Drop name candidates scoring below this (0-100).

## `countries` (type: `array`):

2-letter ISO country codes of the legal address (e.g. DE, GB, US). Applies to name matching and browse mode.

## `entityStatus` (type: `string`):

Filter by legal entity status (name matching and browse mode).

## `registrationStatuses` (type: `array`):

Filter by LEI registration status, e.g. ISSUED (current) or LAPSED (not renewed). Empty = all.

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

Add direct and ultimate parent LEI, name and country (or the declared reporting-exception reason when no parent is reported) to each row. Parent names are fetched in bulk (about 1 extra API request per 200 records).

## `maxResults` (type: `integer`):

Maximum number of charged records to return. The run also stops at your maximum cost per run.

## `requestsPerSecond` (type: `number`):

Politeness limit for the GLEIF API (guidance: 60 requests/minute). Default 0.9/s.

## Actor input object example

```json
{
  "leis": [
    "529900T8BM49AURSDO55",
    "529900LT6G2TUO3TZB94"
  ],
  "companyNames": [
    "Deutsche Bank AG"
  ],
  "fuzzyMatch": true,
  "maxMatchesPerName": 3,
  "minMatchScore": 60,
  "entityStatus": "ANY",
  "includeParents": true,
  "maxResults": 1000,
  "requestsPerSecond": 0.9
}
```

# Actor output Schema

## `results` (type: `string`):

LEI records / match results (one row per record) in the default dataset.

# 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": [
        "529900T8BM49AURSDO55",
        "529900LT6G2TUO3TZB94"
    ],
    "companyNames": [
        "Deutsche Bank AG"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("guileless_concerto/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": [
        "529900T8BM49AURSDO55",
        "529900LT6G2TUO3TZB94",
    ],
    "companyNames": ["Deutsche Bank AG"],
}

# Run the Actor and wait for it to finish
run = client.actor("guileless_concerto/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": [
    "529900T8BM49AURSDO55",
    "529900LT6G2TUO3TZB94"
  ],
  "companyNames": [
    "Deutsche Bank AG"
  ]
}' |
apify call guileless_concerto/lei-lookup --silent --output-dataset

```

## MCP server setup

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