# GLEIF LEI Scraper - Bulk LEI Lookup, Company Search & Parents (`neverempty/gleif-lei-scraper`) Actor

For KYC, AML and counterparty onboarding: look up LEIs in bulk or search the official GLEIF register, which held 3,426,628 LEIs on 2026-09-11. Every row carries the legal address, legal form, renewal status and the direct and ultimate parent. Up to 10,000 per run. No API key.

- **URL**: https://apify.com/neverempty/gleif-lei-scraper.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.65 / 1,000 result returneds

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## GLEIF LEI Scraper - Bulk LEI Lookup, Company Search & Parents

**For KYC and onboarding checks, counterparty and vendor master-data cleanup, corporate-group mapping and lead lists of newly registered companies**: look up Legal Entity Identifiers (LEI, ISO 17442) in bulk, or search the official GLEIF register by company name, country, jurisdiction, legal form, category, status, registration date or renewal date — and list every subsidiary of a group.

**The whole register, measured.** On 2026-09-10 and 2026-09-11 GLEIF held **3,426,628 LEIs**, read from the live API (`api.gleif.org/api/v1`). Up to **10,000** LEIs per bulk lookup and up to **10,000** rows per search, read with GLEIF's cursor rather than numbered pages (which stop at 10,000). No rate limit was hit in testing — **200 requests in 9 seconds** all answered.

**The GLEIF API answers "0 results" to a bad question, and this Actor refuses to pass that on as an answer.** A comma inside a name means "or": `Apple, Inc.` matched **77,060** entities (every "Apple" plus every "Inc.") while `Apple Inc` matched **24** — so commas are turned into spaces and pasting a legal name works. An unknown country code such as `ZZ` returns 200 with nothing, exactly like a country with no companies, so codes are checked against GLEIF's own list of **250** first. A legal form GLEIF does not know is not rejected either: `GMBH` returned **434** free-text entities instead of the **145,526** German GmbHs filed under the code `2HBR`.

**And a date range quietly loses a day**: `2026-09-07..2026-09-08` counts only the first minute of the last day — **1,289** entities instead of the **2,455** actually registered on those two days. Ranges here include the whole last day. Every LEI you give is accounted for: a missing one comes back as a `lei-not-found` row, never as a silently shorter list. **716** LEIs still say `ISSUED` although their renewal date has passed, so `renewalOverdue` and `daysUntilRenewal` are computed for every row.

Each result is **one row per LEI**: legal name and other names, legal address and headquarters address, jurisdiction, legal form (the ISO 20275 ELF code and its name), entity status, registration status, first registration, last update and next renewal date, whether the renewal is overdue, the business register and registration number, and the **direct and ultimate parent** — or, when a parent is not reported, GLEIF's reason code and what it means.

No API key, no browser, no proxy. Export as JSON, CSV or Excel.

This Actor is not affiliated with GLEIF. It reads GLEIF's public API, whose data GLEIF publishes under the CC0 1.0 licence.

### What the GLEIF API gets wrong, and what this Actor does about it

Measured against the live API (`api.gleif.org/api/v1`) on 2026-09-10 and 2026-09-11, when the register held 3,426,628 LEIs. Counts below are from those dates and change as GLEIF publishes new data daily.

| GLEIF's behaviour | What you get here |
|---|---|
| An LEI it does not know is silently left out of a bulk lookup - no error, just a shorter list | Every LEI you give is accounted for: missing ones come back as a `lei-not-found` row, and LEIs beyond the limit of 10,000 per run are counted in one `not-looked-up` row naming the first and last of them. Each LEI is also checked against its ISO 17442 check digits (mod 97) before it is sent, so a typo is reported as a typo |
| An unknown country code such as `ZZ` returns 200 with 0 results, exactly like a country with no companies | Country codes are checked against GLEIF's own list of 250 codes before anything is sent |
| A comma inside a name means "or": `Apple, Inc.` matched 77,060 entities (every "Apple" plus every "Inc."), `Apple Inc` matched 24 (2026-09-11) | Commas in the name are turned into spaces, so pasting a legal name works. If that search finds nothing, the row returned says the commas were sent as spaces, so a search meant as "Siemens or Bosch" is not mistaken for "no such company" |
| Name search matches whole words only. `insur` finds none of the 63 Gibraltar entities whose legal name contains "Insurance" (2026-09-11) | Said plainly in the input form and in the row returned when nothing matches, so an empty result is not mistaken for "no such company" |
| A date range `2026-09-07..2026-09-08` counts only the first minute of the last day: 1,289 entities instead of the 2,455 registered on those two days (2026-09-11) | Date ranges are sent so the whole last day is included (checked against a full count: 1,200 entities on 2026-09-08, exactly) |
| Numbered pages stop at 10,000 results | Results are read with GLEIF's cursor, up to 10,000 per run. When more match, a row says how many, so a capped list is not mistaken for the whole list |
| The "next page" link GLEIF returns drops the parent information, so pages 2 onward silently lose every parent | Each page is requested with the parent information again |
| 716 LEIs still say `ISSUED` although their renewal date has passed (2026-09-10) | `renewalOverdue` and `daysUntilRenewal` are computed for every row |
| A legal form code GLEIF does not know is not rejected: `GMBH` returned 434 entities whose legal form was typed as free text, not the 145,526 German GmbHs filed under the code `2HBR` (2026-09-11) | Legal form codes are checked against GLEIF's ELF list first; an unknown one is reported with a hint instead of returning the wrong companies |
| A parent is often not reported at all, and the reason sits in a separate record | Parent LEI and name when reported; otherwise `exception` with GLEIF's reason code (such as `NATURAL_PERSONS`, `NON_CONSOLIDATING`, `NO_KNOWN_PERSON`, `NON_PUBLIC`, `NO_LEI`) and a plain-English meaning. A few retired records carry no parent information at all (3 in a sample of 100); those say `not-reported` instead of leaving the columns empty |
| "Owned by" only works from the top of a group: on 2026-09-11 Siemens AG had 573 entities below it and Siemens Healthineers AG (itself a Siemens subsidiary) had 0 - although it was the direct parent of 87 | Choose `ultimate` or `direct` subsidiaries. Direct uses GLEIF's direct-children list (Siemens AG: 461, Siemens Healthineers AG: 87 on 2026-09-11; these counts change as GLEIF updates). That list ignores every other filter, so this Actor refuses to combine them rather than pretend they were applied |

Temporary failures (no response, 429, 5xx) are retried with increasing waits. No rate limit was hit in testing (200 requests in 9 seconds all answered), but a 429 is still honoured. If a lookup still fails, you get a `failed` row saying so - nothing is guessed.

### Input

Give **LEIs** for a bulk lookup, **or** any combination of the search fields. If both are given, the LEIs are looked up and a row says the search fields were not used.

| Field | Default | What it does |
|---|---|---|
| `leis` | - | LEIs to look up, up to 10,000 per run (200 per request). Commas, spaces and new lines all separate. LEIs beyond 10,000 are not looked up and are counted in one row |
| `name` | - | Words in the company name. Whole words, all required, any order, case and accents ignored. A comma is sent as a space (GLEIF would read it as "or") |
| `nameScope` | `all-names` | `all-names` (legal name, other names and transliterated names) or `legal-name` |
| `countries` | - | Country of the legal address, ISO 3166 two-letter codes (`DE`, `US`, `GB`). Several means any of them |
| `jurisdictions` | - | Jurisdiction of formation: `DE`, or country and subdivision such as `US-DE` (Delaware), `US-NY`, `CA-QC` |
| `legalForms` | - | ISO 20275 ELF codes such as `2HBR` (German GmbH) or `6QQB` (German AG) |
| `categories` | - | `BRANCH`, `FUND`, `SOLE_PROPRIETOR`, `GENERAL`, `RESIDENT_GOVERNMENT_ENTITY`, `INTERNATIONAL_ORGANIZATION` |
| `entityStatus` | `any` | `ACTIVE` or `INACTIVE` |
| `registrationStatus` | - | `ISSUED`, `LAPSED`, `RETIRED`, `MERGED`, `ANNULLED`, `DUPLICATE`, `PENDING_TRANSFER`, `PENDING_ARCHIVAL` |
| `subsidiariesOf` | - | A parent LEI; returns the entities GLEIF lists under it |
| `subsidiaryLevel` | `ultimate` | `ultimate` (ultimate parent is this LEI) or `direct` (direct parent is this LEI; cannot be combined with other search fields). Ignored when `subsidiariesOf` is empty |
| `registeredFrom` / `registeredTo` | - | First registration date, `YYYY-MM-DD` (UTC), both days included |
| `renewalFrom` / `renewalTo` | - | Next renewal date, `YYYY-MM-DD` (UTC), both days included. A past `renewalTo` finds overdue LEIs |
| `sort` | `lei` | `lei`, `newest`, `oldest` (first registration) or `renewal-soonest` |
| `maxResults` | `100` | Most rows a search returns, up to 10,000. If more than 10,000 match, a row says how many |

If nothing at all is given, three example LEIs (Apple Inc., Siemens AG, Bloomberg Finance L.P.) are looked up so the Actor always returns something, and every row records what was looked up in its `input` column.

#### Examples

Bulk lookup:

```json
{ "leis": ["HWUPKR0MPOU8FGXBT394", "W38RGI023J3WT1HWRP32", "5493001KJTIIGC8Y1R12"] }
```

German GmbHs registered this month, newest first:

```json
{ "countries": ["DE"], "legalForms": ["2HBR"], "registeredFrom": "2026-09-01", "sort": "newest", "maxResults": 500 }
```

Delaware entities whose LEI lapsed:

```json
{ "jurisdictions": ["US-DE"], "registrationStatus": ["LAPSED"], "maxResults": 1000 }
```

Every direct subsidiary of Siemens AG:

```json
{ "subsidiariesOf": "W38RGI023J3WT1HWRP32", "subsidiaryLevel": "direct", "maxResults": 1000 }
```

### Output

A real row (Apple Inc., looked up on 2026-09-11; some columns omitted):

```json
{
  "status": "ok",
  "rowType": "lei-record",
  "input": "HWUPKR0MPOU8FGXBT394",
  "lei": "HWUPKR0MPOU8FGXBT394",
  "legalName": "Apple Inc.",
  "legalAddress": "C/O C T Corporation System, 330 N. Brand Blvd, Suite 700, Glendale, US-CA, 91203, US",
  "legalCountry": "US",
  "jurisdiction": "US-CA",
  "legalFormName": "For-Profit Corporation General Stock",
  "entityStatus": "ACTIVE",
  "registrationAuthorityName": "Business Entity Records",
  "registrationAuthorityOrganization": "Secretary of State",
  "registrationStatus": "ISSUED",
  "nextRenewalDate": "2027-03-08T17:27:20Z",
  "renewalOverdue": false,
  "directParentStatus": "exception",
  "directParentLei": null,
  "directParentExceptionReason": "NATURAL_PERSONS",
  "directParentExceptionMeaning": "No parent entity to report: the entity is controlled by one or more natural persons, not by another legal entity.",
  "ultimateParentStatus": "exception",
  "ultimateParentExceptionReason": "NATURAL_PERSONS",
  "gleifRecordUrl": "https://search.gleif.org/#/record/HWUPKR0MPOU8FGXBT394"
}
```

All columns: `lei`, `legalName`, `legalNameLanguage`, `otherNames`, `transliteratedNames`, `legalAddress`, `legalAddressLines`, `legalCity`, `legalRegion`, `legalPostalCode`, `legalCountry`, `hqAddress`, `hqCity`, `hqRegion`, `hqPostalCode`, `hqCountry`, `jurisdiction`, `legalFormCode`, `legalFormName`, `legalFormOther`, `category`, `subCategory`, `entityStatus`, `entityCreatedDate`, `entityExpirationDate`, `entityExpirationReason`, `successorLei`, `successorName`, `registrationAuthorityId`, `registrationAuthorityName`, `registrationAuthorityOrganization`, `registrationNumber`, `registrationStatus`, `initialRegistrationDate`, `lastUpdateDate`, `nextRenewalDate`, `renewalOverdue`, `daysUntilRenewal`, `managingLou`, `corroborationLevel`, `conformityFlag`, `bic`, `directParentStatus`, `directParentLei`, `directParentName`, `directParentExceptionReason`, `directParentExceptionMeaning`, `ultimateParentStatus`, `ultimateParentLei`, `ultimateParentName`, `ultimateParentExceptionReason`, `ultimateParentExceptionMeaning`, `gleifRecordUrl`, `goldenCopyDate`, `checkedAt`, plus `status`, `note`, `input` and `source`.

`directParentStatus` / `ultimateParentStatus` is `reported` (the parent's LEI and name are filled), `exception` (GLEIF's reason for not reporting one is filled), or `not-reported` (the record carries no parent information at all). A reason code this Actor does not recognise is passed through unchanged with an empty meaning rather than guessed. Values GLEIF does not hold are `null`, never `0`, `false`, an empty string or an empty list: `otherNames`, `transliteratedNames`, `legalAddressLines` and `bic` are `null` when the record has none.

Rows with `status` other than `ok` (`lei-not-found`, `not-looked-up`, `bad-input`, `no-results`, `failed`, `incomplete`, `filters-ignored`) explain themselves in `note` and are never charged.

### Pricing

Pay per result: you are charged for each LEI row returned. Rows that only explain a rejected input, an LEI GLEIF does not hold, an empty search or a failed lookup are free.

### Source and licence

Data comes from GLEIF's public API. GLEIF publishes LEI data under the CC0 1.0 licence, free to use and redistribute, including commercially. This Actor is not affiliated with GLEIF.

### Support

Found a wrong value or a missing field? Open an issue in the Issues tab with the input you used and the LEI concerned.

# Actor input Schema

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

Legal Entity Identifiers to look up in bulk (20 characters, for example HWUPKR0MPOU8FGXBT394). Up to 10,000 per run, read 200 at a time; any beyond that are not looked up and are counted in one row. Each LEI is checked against its ISO 17442 check digits before it is sent: GLEIF silently leaves out an LEI it does not know, so a mistyped one is reported as its own row instead. When LEIs are given, the search fields below are not used.

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

Words in the company name, for example Siemens or deutsche bank. GLEIF matches whole words only and every word must appear, in any order, ignoring case and accents: 'insur' does not find 'Insurance'. A comma is sent as a space, because GLEIF would read it as 'or'.

## `nameScope` (type: `string`):

Any name searches the legal name, other names and transliterated names. Legal name searches the registered legal name only.

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

Two-letter ISO 3166 country codes of the legal address, such as DE, US, GB. Several codes mean any of them. A code GLEIF does not know is rejected with a reason rather than sent, because GLEIF would answer it with an empty result.

## `jurisdictions` (type: `array`):

Jurisdiction of formation: a country code (DE) or a country and subdivision code (US-DE for Delaware, US-NY, CA-QC). Several codes mean any of them.

## `legalForms` (type: `array`):

ISO 20275 entity legal form codes, for example 2HBR (German GmbH) or 6QQB (German AG). Several codes mean any of them. Every row carries the code and its name.

## `categories` (type: `array`):

Any of BRANCH, FUND, SOLE\_PROPRIETOR, GENERAL, RESIDENT\_GOVERNMENT\_ENTITY, INTERNATIONAL\_ORGANIZATION.

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

Whether the legal entity itself is active or inactive (for example dissolved or merged).

## `registrationStatus` (type: `array`):

Any of ISSUED, LAPSED (not renewed on time), RETIRED, MERGED, ANNULLED, DUPLICATE, PENDING\_TRANSFER, PENDING\_ARCHIVAL.

## `subsidiariesOf` (type: `string`):

An LEI, for example W38RGI023J3WT1HWRP32 for Siemens AG. Returns the entities GLEIF lists under it, at the level chosen below.

## `subsidiaryLevel` (type: `string`):

Ultimate: every entity whose ultimate parent is this LEI; GLEIF lists these only under the top entity of a group (on 2026-09-11: Siemens AG 573, Siemens Healthineers AG 0). Direct: every entity whose direct parent is this LEI (on 2026-09-11: Siemens AG 461, Siemens Healthineers AG 87). Direct cannot be combined with the other search fields, because GLEIF ignores them there. Ignored when no parent LEI is given.

## `registeredFrom` (type: `string`):

YYYY-MM-DD (UTC). Finds LEIs issued from this day on, for example newly registered companies.

## `registeredTo` (type: `string`):

YYYY-MM-DD (UTC). The whole day is included.

## `renewalFrom` (type: `string`):

YYYY-MM-DD (UTC). Combine with Next renewal on or before to find LEIs whose renewal falls due in a window.

## `renewalTo` (type: `string`):

YYYY-MM-DD (UTC). The whole day is included. A date in the past finds LEIs whose renewal is overdue.

## `sort` (type: `string`):

Order of search results. LEI order is GLEIF's own order; newest and oldest use the first registration date; renewal-soonest puts the earliest next renewal date first.

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

The most LEI records a search returns. Up to 10,000; if more match, a row says how many.

## Actor input object example

```json
{
  "leis": [
    "HWUPKR0MPOU8FGXBT394",
    "W38RGI023J3WT1HWRP32",
    "5493001KJTIIGC8Y1R12"
  ],
  "nameScope": "all-names",
  "entityStatus": "any",
  "subsidiaryLevel": "ultimate",
  "sort": "lei",
  "maxResults": 100
}
```

# Actor output Schema

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

One row per Legal Entity Identifier (LEI): legal name, other names, legal and headquarters address, jurisdiction, legal form, entity and registration status, renewal dates, and direct and ultimate parent (or the reason the parent is not reported), plus one row when an input was rejected, not found, or could not be read.

# 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": [
        "HWUPKR0MPOU8FGXBT394",
        "W38RGI023J3WT1HWRP32",
        "5493001KJTIIGC8Y1R12"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/gleif-lei-scraper").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": [
        "HWUPKR0MPOU8FGXBT394",
        "W38RGI023J3WT1HWRP32",
        "5493001KJTIIGC8Y1R12",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/gleif-lei-scraper").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": [
    "HWUPKR0MPOU8FGXBT394",
    "W38RGI023J3WT1HWRP32",
    "5493001KJTIIGC8Y1R12"
  ]
}' |
apify call neverempty/gleif-lei-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/ehdUzLdqpl0nKSRVE/builds/ztf1q2mMuYwEbOoTR/openapi.json
