# LEI, VAT and Company Identity Extractor (`s-r/lei-vat-identity`) Actor

One row per company identity: GLEIF entity with parent chain, EU VIES VAT validation with consultation number, annual accounts where a free register has them, and domain registration facts. Give a domain, VAT number, LEI or company name.

- **URL**: https://apify.com/s-r/lei-vat-identity.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Business, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$6.00 / 1,000 identity row delivereds

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, VAT and Company Identity Extractor

One row per company identity, joined across the public registers that
actually hold the facts. Give a Legal Entity Identifier, an EU VAT number,
a national registry number, a company name or a website domain, and get
back the legal entity with its LEI, its registry number, its parent chain,
its VAT consultation number and whatever annual accounts a free public
register will hand over. This is the KYB lookup that turns "who is behind
this counterparty" into one canonical record you can approve or reject
against.

### What you get

- The GLEIF Level 1 identity: legal name, jurisdiction, national registry
  number (`entity.registeredAs`), legal form, entity status, creation date
  and both address blocks (legal seat and headquarters).
- GLEIF registration trust flags: `registration.status` (ISSUED, LAPSED,
  MERGED, RETIRED), `nextRenewalDate`, `managingLou`,
  `corroborationLevel` (FULLY\_CORROBORATED versus ENTITY\_SUPPLIED\_ONLY)
  and the validation reference on the record.
- The joins: BIC codes, ISIN instruments, conformity flag and the
  `registeredAt` registry authority.
- GLEIF Level 2 parent chain: the direct parent and the ultimate parent,
  each as a full mini-record, or the reporting-exception reason when no
  parent is being reported (NATURAL\_PERSONS, NO\_KNOWN\_PERSON,
  NON\_CONSOLIDATING and the rest). Plus a count of direct children.
- EU VIES VAT validation: the `valid` flag, the `userError` code, and the
  `requestIdentifier` consultation number that is the proof-of-check token
  your compliance file needs. Where the member state returns them, the row
  also carries the trader name and address, plus the approximate-match
  codes (`matchName`, `matchCity`, `matchStreet`, `matchPostalCode`,
  `matchCompanyType`) when you send candidate trader fields.
- Annual accounts from a free public register where one exists, with the
  source named on the row and honest nulls where it does not. UK Companies
  House filing history when you bring a Companies House API key. Nothing is
  invented and nothing is interpolated: a missing year stays missing.
- For a domain input: RDAP registration facts (registrar, registrant where
  published, creation and expiry dates, nameservers, status codes) plus the
  site's own title and a couple of trivial technology hints.

### Why scrape company identity

Compliance teams onboard sellers and suppliers from a webshop domain or a
marketplace handle. The registers that answer the question are split: GLEIF
holds the legal entity and its ownership chain, the EU VIES service holds
the VAT finding, the national registries hold the accounts, and RDAP holds
the domain. Nobody joins them for you. A VAT number on its own says a
seller is registered; it does not say who owns them or whether the LEI on
their invoice is still ISSUED.

The decision this changes is binary. A marketplace that checks a seller
gets a VIES result of `valid: true` with consultation number WAPIAAAA on
the file, while GLEIF shows no LEI and no parent chain and the registration
has lapsed, so the account goes to enhanced due diligence instead of
straight through. A comparative score ("97% of sellers validated") changes
nothing in that moment. The joined row does.

### Input

| Field | Required | What it does |
|---|---|---|
| `lei` | one of these | 20-character Legal Entity Identifier, exact record lookup |
| `vat_number` | one of these | EU VAT number. The country prefix is stripped before the VIES call, so `LU26375245` and `26375245` both work once the member state is known |
| `registered_as` | one of these | National registry number: Dutch KvK, German HRB, UK company number |
| `company_name` | one of these | Legal name for the GLEIF name search when you have no identifier |
| `domain` | one of these | Website domain. Adds RDAP and the site title, and can seed the name search |
| `jurisdiction` | optional | Two-letter country code that narrows fuzzy name search and decides which accounts register to consult |
| `company_number` | optional | Companies House number for the UK accounts door |
| `fiscal_year` | optional | Fiscal year to look for in the accounts register |
| `requester_member_state_code` | optional | Your own member state code |
| `requester_number` | optional | Your own VAT number. With the requester member state this is what makes VIES return a consultation number |

Give at least one of the five identifiers. The actor resolves whichever
you gave and enriches the row with whatever else it can reach.

### Output

One row per entity. A trimmed example:

```json
{
  "lei": "H1FJE8H61JGM1JSGM897",
  "legal_name": "Koninklijke Philips N.V.",
  "jurisdiction": "NL",
  "registered_as": "17001910",
  "legal_form_id": "D9OW",
  "entity_status": "ACTIVE",
  "creation_date": "1991-09-30T00:00:00Z",
  "registration_status": "ISSUED",
  "next_renewal_date": "2027-06-26",
  "managing_lou": "5493001KJTII",
  "corroboration_level": "FULLY_CORROBORATED",
  "bic": ["PHNLNL2A"],
  "isins": ["NL0000000953"],
  "direct_parent": null,
  "direct_parent_reporting_exception": {
    "category": "DIRECT_ACCOUNTING_CONSOLIDATION_PARENT",
    "reason": "NO_KNOWN_PERSON"
  },
  "direct_children_count": 8,
  "vat_number": "NL00017001910B01",
  "vies": {
    "valid": true,
    "request_identifier": "WAPIAAAAaDUdDiAE",
    "name": "TRADER NAME WHERE THE MEMBER STATE RETURNS ONE",
    "address": "TRADER ADDRESS WHERE THE MEMBER STATE RETURNS ONE",
    "vat_derived_from_registry_id": true,
    "trader_match_codes": {"match_name": "NOT_PROCESSED"}
  },
  "jaarcijfers": {
    "source": "nl_public_registers",
    "available": false,
    "reason": "no free machine-readable per-company figure door in NL",
    "filings": []
  },
  "sources": ["gleif", "vies"],
  "completeness": {"gleif": true, "vies": true, "jaarcijfers": false, "domain": false}
}
```

Fields that no register could supply come back `null` or with an explicit
`reason`, never with a plausible-looking guess.

### Use cases

Marketplaces and B2B payment platforms onboarding sellers. A seller signs
up with a webshop domain. The actor resolves the domain to a legal entity,
returns the LEI and the registry number, checks the VAT number and stamps
the consultation number into the onboarding file, and shows the parent
chain so the compliance officer can see whether the seller is a subsidiary
of a group you already know. One call replaces five register lookups and a
spreadsheet.

Distributors and PE/M\&A teams resolving counterparties before money moves.
Give the target's legal name and jurisdiction, get the LEI, the registration
status, the renewal date and the ownership chain. A lapsed LEI or a
FULLY\_CORROBORATED absence of parents is a finding you can act on, not a
metric.

Credit teams sizing a counterparty from its last filed year. Point the actor
at a UK company number and bring a Companies House key; the filing history
comes back attached to the right legal entity rather than to a name string.
Where no free register publishes figures, the row says so and hands you the
registry id so you can buy the extract.

Brand and grey-import checks. A listing claims to be an authorised
distributor of brand B. The LEI record either sits inside brand B's
ownership chain or it does not. The direct-children count and the parent
links are what settle it.

### How it compares

| | This actor | LEI-only lookup actors | Standalone VAT checkers |
|---|---|---|---|
| GLEIF L1 + L2 identity | yes | yes | no |
| Parent chain + reporting exceptions | yes | sometimes | no |
| VIES validation + consultation number | yes | no | yes |
| Trader name and address where returned | yes | no | sometimes |
| Annual accounts where free | yes | no | no |
| Domain / RDAP resolution | yes | no | no |
| One joined row instead of two exports | yes | no | no |
| Price per 1k rows | $6.00 | from $2.00 | from $5.00 |

The LEI-only lookups on the Store are cheaper per record and should be
your pick if all you want is a single LEI. The standalone VAT checkers are
cheaper per check and should be your pick if all you want is a validity
flag. This actor exists for the join: one row that carries the VAT finding
and the legal identity and the ownership chain together, so the two
answers can never drift apart in your file.

### Pricing

$0.006 per identity row. All pricing is pay-per-event — you only pay for
results you receive. No actor-start fee, no per-compute-unit charges.

### Limits and gotchas

- Give at least one identifier. A run with nothing to resolve fails fast
  with `bad_input` rather than returning an empty row.
- Bare national digits for VIES. `LU26375245` is accepted and normalised;
  sending the prefix twice is the one form the service refuses.
- The consultation number only appears when you also send
  `requester_member_state_code` and `requester_number`. Without them VIES
  still validates, it just does not issue a proof-of-check token.
- Member states differ on what they disclose. Some return trader name and
  address; some strip both; Spain answers through approximate matching
  only. The row reports what came back and leaves the rest null.
- VIES rate-limits concurrency per member state and signals it inside the
  response body rather than with an HTTP status. The actor reads that,
  treats it as a retry and never records it as an INVALID result. If a
  member state stays busy you get a `user_error` of
  `MS_MAX_CONCURRENT_REQ` with `valid: null` — re-run it.
- Annual accounts coverage is uneven by design. The UK door needs a
  Companies House API key in the environment. The NL door reports honestly
  that no free machine-readable per-company figure source exists rather
  than filling the gap with an estimate.
- A VAT number derived from a registry id is a conventional construction,
  not a guarantee. The actor builds the standard Dutch form from a KvK
  number, checks it against VIES and reports what came back, including
  `valid: false`. When you hold the real VAT number, send it.
- Name search is fuzzy. When you have an LEI, a registry number or a VAT
  number, use it; a name alone can land on a similarly named entity, so
  check `registered_as` and `jurisdiction` on the row before trusting it.
- Cold start is a few seconds. The registers are queried live; there is no
  stale cache standing between you and the current record.

### FAQ

**Can I look up a company by LEI and get its VAT number in one call?**
Yes. Give the LEI. The actor fetches the GLEIF record, derives the Dutch
VAT form from the registry number where the jurisdiction allows it, checks
it against VIES and returns both the identifier and the consultation
number on the same row.

**Does a GLEIF record contain a VAT number?**
No. The LEI record has no tax-number field at all. The join from
`entity.registeredAs` to a VAT identifier is the part this actor does for
you, and the VIES result is what proves the join landed on a real
registration.

**What is a VIES consultation number and why do I need one?**
`requestIdentifier`, the consultation number, is the token the EU service
issues to the requester as proof a specific check happened at a specific
time. Your compliance file wants that token next to the finding. You only
get one by sending your own requester member state and number.

**How do I find the ultimate owner of a company from its LEI?**
The row carries `ultimate_parent` as a mini-record. When there is no
ultimate parent being reported, `ultimate_parent_reporting_exception`
gives the category and reason instead of a silent null, so you can tell
"not disclosed" apart from "not looked up".

**Can this actor give me annual accounts for a Dutch BV?**
Not the figures. No free public register publishes per-company Dutch
annual accounts as machine-readable data. The row returns the registry id
and an explicit reason so you can buy the extract. The UK door does return
filing history when you bring a Companies House key.

### Related Actors

- https://apify.com/s-r/leaktix
- https://apify.com/s-r/backlinks-checker
- https://apify.com/s-r/google-shopping-search-scraper

# Actor input Schema

## `lei` (type: `string`):

20-character Legal Entity Identifier, e.g. H1FJE8H61JGM1JSGM897 (Koninklijke Philips N.V.).

## `vat_number` (type: `string`):

EU VAT number with country prefix, e.g. LU26375245. The prefix is stripped before the VIES call; bare national digits also work if jurisdiction is set.

## `registered_as` (type: `string`):

National registry id such as a Dutch KvK number (17001910), a German HRB (HRB 6684) or a UK company number.

## `company_name` (type: `string`):

Legal name to search for when you have no identifier. Optional jurisdiction narrows the GLEIF name search.

## `domain` (type: `string`):

Website domain to resolve to an entity. Adds RDAP registration facts and the site title to the row.

## `jurisdiction` (type: `string`):

Two-letter country code that narrows fuzzy name search and decides which accounts register to consult. Optional.

## `company_number` (type: `string`):

Companies House number, for the UK accounts door. Optional.

## `fiscal_year` (type: `string`):

Fiscal year to look for in the accounts register, e.g. 2024. Optional.

## `requester_member_state_code` (type: `string`):

Your own member state code. Required with requester number before VIES will issue a consultation number.

## `requester_number` (type: `string`):

Your own VAT number. With the requester member state this is what makes VIES return a requestIdentifier (consultation number).

## Actor input object example

```json
{
  "lei": "H1FJE8H61JGM1JSGM897",
  "vat_number": "LU26375245",
  "registered_as": "17001910",
  "company_name": "Koninklijke Philips N.V.",
  "domain": "philips.com",
  "jurisdiction": "NL",
  "company_number": "00423456",
  "fiscal_year": "2024",
  "requester_member_state_code": "NL",
  "requester_number": "NL123456789B01"
}
```

# Actor output Schema

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

One row per entity: GLEIF identity, VIES check, accounts and domain facts.

## `output` (type: `string`):

OUTPUT record with the run's counts and status flags.

## `errors` (type: `string`):

Failures with a code and a redacted message. Absent when the run had none.

# 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 = {
    "lei": "H1FJE8H61JGM1JSGM897",
    "vat_number": "LU26375245",
    "registered_as": "17001910",
    "company_name": "Koninklijke Philips N.V.",
    "domain": "philips.com",
    "jurisdiction": "NL",
    "company_number": "",
    "fiscal_year": "2024",
    "requester_member_state_code": "",
    "requester_number": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/lei-vat-identity").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 = {
    "lei": "H1FJE8H61JGM1JSGM897",
    "vat_number": "LU26375245",
    "registered_as": "17001910",
    "company_name": "Koninklijke Philips N.V.",
    "domain": "philips.com",
    "jurisdiction": "NL",
    "company_number": "",
    "fiscal_year": "2024",
    "requester_member_state_code": "",
    "requester_number": "",
}

# Run the Actor and wait for it to finish
run = client.actor("s-r/lei-vat-identity").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 '{
  "lei": "H1FJE8H61JGM1JSGM897",
  "vat_number": "LU26375245",
  "registered_as": "17001910",
  "company_name": "Koninklijke Philips N.V.",
  "domain": "philips.com",
  "jurisdiction": "NL",
  "company_number": "",
  "fiscal_year": "2024",
  "requester_member_state_code": "",
  "requester_number": ""
}' |
apify call s-r/lei-vat-identity --silent --output-dataset

```

## MCP server setup

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

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/ezbPtArBTGEi60S5h/builds/ZOoNbyeal74FaAcay/openapi.json
