# EU UBO Lookup (`mikee368/eu-ubo-lookup`) Actor

Beneficial owners from sources that can be reused commercially, and a not-available row with the official route where a register is closed.

- **URL**: https://apify.com/mikee368/eu-ubo-lookup.md
- **Developed by:** [Mike Berckmoes](https://apify.com/mikee368) (community)
- **Categories:** Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.40 / 1,000 company checks

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

## EU UBO Lookup

**Check one company and get the beneficial owners where a commercial Actor is allowed to read them.** For every other EU or EEA country the row says not available, with the reason and the official way to ask. Accounting parents from GLEIF are included when a LEI is known. They are not beneficial owners.

Checked against the public sources on 27 Oct 2026. The Court of Justice ruled on 22 Nov 2022 (joined cases C-37/20 and C-601/20, WM and Sovim) that beneficial-ownership registers cannot be open to any member of the public in all cases. Directive (EU) 2024/1640, article 12, replaces that with legitimate-interest access. Member states had to apply those rules by 10 July 2026.

### What does EU UBO Lookup do?

Give it a company name, a registration number, a LEI, or a dataset from EU Company Register Lookup.

1. The company is resolved with the same register and LEI search as [EU Company Register Lookup](https://apify.com/mikee368/eu-company-register-lookup). Those functions run inside this container. The public Actor is not called. Sole traders are dropped, the same way that Actor drops them.
2. When a LEI is known, direct and ultimate accounting parents are read from GLEIF Level 2 (CC0). A natural-person parent is not named by GLEIF, and a missing parent is not turned into a guessed owner.
3. Natural-person owners are read only from the sources in the table below that are marked open.

A personal identifier (for example a PESEL or a Latvian personal code) is format-checked and **not sent** to any register. `notAvailableReason` is `personal_identifier`.

`datasetId` accepts rows from eu-company-register-lookup, company-ownership-lookup, tender-supplier-due-diligence and domain-company-resolver (`legalName`, `name`, `lei`, `registrationNumber`, `country`).

The default cap is 20 companies (maximum 50).

### Coverage

| ISO | Country | Access | Source this Actor calls | Official route |
|---|---|---|---|---|
| AT | Austria | Legitimate interest | None | [WiEReg](https://wiereg.justiz.gv.at/) |
| BE | Belgium | Legitimate interest | None | [FPS Finance UBO](https://finances.belgium.be/fr/ubo) |
| BG | Bulgaria | No commercial open-data licence found | None | [Registry Agency](https://portal.registryagency.bg/) |
| HR | Croatia | eID in practice | None | [FINA](https://www.fina.hr/) |
| CY | Cyprus | Legitimate interest | None | [Registrar of Companies](https://www.companies.gov.cy/) |
| CZ | Czechia | Public portal suspended 17 Dec 2025 | None | [Evidence skutečných majitelů](https://esm.justice.cz/) |
| DK | Denmark | Closed to the public 1 Sep 2025 | None | [CVR](https://datacvr.virk.dk/) |
| EE | Estonia | Open-data file withdrawn 10 Jul 2026 | None | [e-Business Register](https://ariregister.rik.ee/) |
| FI | Finland | Legitimate interest | None | [PRH](https://www.prh.fi/en/commercial_register/beneficial_owners.html) |
| FR | France | Closed to the public 31 Jul 2024 | None | [INPI](https://www.inpi.fr/) |
| DE | Germany | Closed to the public 22 Nov 2022 | None | [Transparenzregister](https://www.transparenzregister.de/) |
| GR | Greece | Legitimate interest | None | [GSIS](https://www.gsis.gr/) |
| HU | Hungary | Case-by-case request | None | [NAV](https://nav.gov.hu/) |
| IE | Ireland | Legitimate-interest certificate | None | [RBO](https://rbo.gov.ie/) |
| IT | Italy | Register suspended by the courts | None | [Registro Imprese](https://www.registroimprese.it/) |
| LV | Latvia | Open data, commercial redistribution allowed | Enterprise Register CSV | [beneficial_owners.csv](https://dati.ur.gov.lv/beneficial_owners/beneficial_owners.csv) |
| LT | Lithuania | Legitimate interest | None | [Centre of Registers](https://www.registrucentras.lt/) |
| LU | Luxembourg | LuxTrust or eIDAS | None | [LBR](https://www.lbr.lu/) |
| MT | Malta | Legitimate-interest certificate | None | [MBR](https://mbr.mt/) |
| NL | Netherlands | Legitimate interest | None | [KVK UBO](https://www.kvk.nl/en/ubo/) |
| PL | Poland | Public official web service | CRBR, company NIP or KRS only | [CRBR](https://crbr.podatki.gov.pl/) |
| PT | Portugal | Legitimate interest | None | [RCBE](https://rcbe.justica.gov.pt/) |
| RO | Romania | Legitimate interest | None | [ONRC](https://www.onrc.ro/) |
| SK | Slovakia | Authorities and national obliged entities | None | [Ministry of the Interior](https://www.minv.sk/) |
| SI | Slovenia | Legitimate interest | None | [AJPES](https://www.ajpes.si/) |
| ES | Spain | Legitimate interest | None | [Registradores](https://www.registradores.org/) |
| SE | Sweden | Legitimate interest from 1 Jul 2026 | None | [Bolagsverket](https://bolagsverket.se/) |
| GB | United Kingdom | Open with your Companies House key | PSC register, Open Government Licence | [Companies House](https://find-and-update.company-information.service.gov.uk/) |
| NO | Norway | No open UBO query | None. Shareholders are not returned as owners | [Brønnøysund](https://www.brreg.no/) |
| IS | Iceland | No open UBO file found | None | [Skatturinn](https://www.skatturinn.is/) |
| LI | Liechtenstein | No open UBO file found | None | [LLV](https://www.llv.li/) |
| CH | Switzerland | Outside the EU directive | None. Zefix is not a UBO file | [Zefix](https://www.zefix.ch/) |

Latvia: the Enterprise Register says its open data may be redistributed without restriction, including commercial use ([notice of 6 May 2026](https://www.ur.gov.lv/en/registers-open-data-latvian-open-data-portal)). The file was online on 27 Oct 2026. The masked identity number and the birth date are dropped before the row is stored.

Poland: the Ministry of Finance publishes a public CRBR web service for company lookups (no login). The request is a company NIP or KRS. PESEL, date of birth, home address and representatives are dropped before the row is stored. The Polish VAT white list is not called, because that client keeps the raw payload for a day and a sole trader's payload can contain a PESEL. A restriction draft (UC75) was not treated as in force on 27 Oct 2026. An HTTP 401 or 403 becomes `source_unavailable` rather than a guess.

Estonia: the Estonian open-data page says beneficial-owner files stopped on 10 July 2026. The contractual API needs an account. The portal is not scripted.

United Kingdom: PSC records are read only when you paste your own free Companies House key. Month and year of birth are dropped. Ceased PSC records are dropped.

GLEIF Level 2 is CC0. `accountingParents` uses `IS_DIRECTLY_CONSOLIDATED_BY` and `IS_ULTIMATELY_CONSOLIDATED_BY`. Those are legal entities. OpenOwnership bulk data and OpenSanctions are not used. The BODS schema licence is not a licence for the data, and the OpenSanctions licence is non-commercial.

### One row

| Field | Meaning |
|---|---|
| `status` | `found` when at least one natural person came from an open source. `not_found` when that source was queried and had no natural person. `not_available` when the source cannot be used |
| `beneficialOwners` | Name, nationality, residence country, nature of control, source. No national id, no birth date, no home address |
| `accountingParents` | GLEIF consolidating parents. Not beneficial owners |
| `corporateControllers` | A legal-entity PSC or a group controller. Not a natural person |
| `notAvailable` | `reason`, a short `detail`, and `officialAccess` (`register`, `url`, `who`, `how`) |
| `charged` | False when the input was a personal identifier and nothing was sent |

`RUN_SUMMARY` is counts and source flags. It does not contain a person's name. The open-data file is held in memory for the rest of the run and is not written to the key-value store.

### How much does a company cost?

Pay per event. The live price is `.actor/pricing.json`.

| Event | Price | When |
|---|---|---|
| `company-check` | $0.004 | A company that was checked. Closed-country rows are charged. A refused personal identifier, a missing source, a missing key, a missing registration number, or a withdrawn file is free |
| `lv-open-data` | $0.020, once per run | The Latvia open-data file was fetched |
| `apify-actor-start` | $0.00005 | Synthetic. The platform charges it. This Actor does not |

`apify-default-dataset-item` is not priced, so a not-available row is not charged again.

Apify keeps 20% of event revenue. Platform usage is deducted from the other 80%. The prices below use the FREE/BRONZE unit prices, which are the expensive tier: compute **$0.20 per CU**, external transfer **$0.20 per GB**. Default memory is **256 MB**. One CU is 1 GB for one hour, so 256 MB is 256/1024 GB. `apify-actor-start` covers the first 5 seconds of compute.

One closed-country company (GLEIF only, about 15 seconds, about 100 KB):

- Billable compute: 10 seconds × (256/1024) GB / 3600 × $0.20 ≈ **$0.00014**
- Transfer ≈ **$0.00002**
- `company-check` at $0.004 keeps 80% = $0.0032. Profit about **$0.003**

One Latvia company, file measured at about 17 MB on 27 Oct 2026, about 45 seconds:

- Billable compute: 40 seconds × (256/1024) GB / 3600 × $0.20 ≈ **$0.00056**
- Transfer: 17/1024 GB × $0.20 ≈ **$0.0033**
- Events $0.024, of which 80% is $0.0192. Profit about **$0.015**

A file that grew to 60 MB would be about $0.012 of transfer. The $0.02 one-time event still covers it (80% is $0.016) plus the compute. Revisit the one-time price if the file passes about 80 MB.

Poland and the UK PSC call are small responses. They are covered by `company-check`.

### How to run it

1. Paste companies, a LEI, a registration number, or a dataset ID.
2. Add a Companies House key only if you want UK PSC records.
3. Download the dataset. Empty input finishes without a charge and without an HTTP call.

```json
{"companies": [{"lei": "529900SSGT49ZZSWYE62"}], "maxCompanies": 1}
```

That German LEI returns accounting parents and `not_available`, because the Transparenzregister is legitimate-interest only.

### Personal data

Beneficial owners are natural persons. The dataset keeps only what the lawful source publishes and what a KYB check needs: the name as published, nationality, country of residence, and the nature of control. National identifiers, dates of birth, home addresses, email and phone are dropped even when the file contains them.

You are the controller for the names you download. Customer due diligence uses Directive (EU) 2015/849, and the GDPR basis is Article 6(1)(c) or 6(1)(f). Delete the names when you no longer need them. This Actor is not a marketing list.

`RUN_SUMMARY` and the log carry counts, not names. Nothing in this Actor writes a natural person into a named key-value store that outlives the run.

### Limitations

- A Latvia or Poland lookup needs a company registration number. A name that GLEIF cannot resolve is `registration_number_required`. The Latvia file has no company-name column, and CRBR is not searched by a person's name.
- GLEIF fair use spaces requests about 1.05 seconds apart, the same gap as the register Actor.
- Norway's shareholder file is not a beneficial-ownership file and is not queried for owners.
- Coverage will change when a member state opens or closes a file. `source_withdrawn` and `source_unavailable` mean this Actor stopped, not that it filled the gap.

### FAQ

**What does EU UBO Lookup return?**
Beneficial owners from sources a commercial Actor is allowed to read. For every other EU or EEA country the row says not available, with the reason and the official way to ask.

**Why are most countries not available?**
The Court of Justice ruled on 22 Nov 2022 (C-37/20 and C-601/20) that beneficial-ownership registers cannot be open to everyone in all cases. Directive (EU) 2024/1640 replaces that with legitimate-interest access, which a commercial Actor cannot assume.

**Are GLEIF parents beneficial owners?**
No. When a LEI is known, direct and ultimate accounting parents are read from GLEIF Level 2 and labelled as accounting parents. A missing parent is never turned into a guessed owner.

**Are personal identifiers sent to a register?**
No. A personal identifier is format-checked only. The row carries notAvailableReason personal_identifier.

**What can I give it as input?**
A company name, a registration number, a LEI, or a dataset ID from EU Company Register Lookup or Tender Supplier Due Diligence. Up to 50 companies per run.

**How is it priced?**
$0.004 per company-check, plus a separate lv-open-data event ($0.02) where that source is read. A small apify-actor-start event applies per run; see the Pricing tab.

### Related Actors

- [EU Company Register: UK, FR, NO, FI, PL, CZ, SK, IE, RO, CH](https://apify.com/mikee368/eu-company-register-lookup): Resolves the company first and can feed this Actor through a dataset ID.
- [Supplier Risk Monitor](https://apify.com/mikee368/supplier-risk-monitor): Includes an optional ownership check in a recurring supplier re-check.
- [Tender Supplier Due Diligence](https://apify.com/mikee368/tender-supplier-due-diligence): Builds supplier profiles from award notices that can feed this Actor.

# Actor input Schema

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

Each item is a name, or an object with name, registrationNumber, lei and country (ISO 3166-1 alpha-2). Maximum 50 per run. A personal identifier is not sent to a register.

## `datasetId` (type: `string`):

Optional dataset from eu-company-register-lookup, company-ownership-lookup, tender-supplier-due-diligence or domain-company-resolver. legalName, lei, registrationNumber and country are mapped.

## `maxCompanies` (type: `integer`):

Stop after this many companies. Default 20. Maximum 50.

## `maxDatasetItems` (type: `integer`):

How many rows to read from datasetId.

## `matchMode` (type: `string`):

strict (default) keeps a name match only at 75 or above.

## `companiesHouseApiKey` (type: `string`):

Free key from developer.company-information.service.gov.uk. Without it, UK persons with significant control are not fetched. The same key pattern as EU Company Register Lookup.

## Actor input object example

```json
{
  "companies": [
    {
      "lei": "529900SSGT49ZZSWYE62"
    }
  ],
  "datasetId": "",
  "maxCompanies": 20,
  "maxDatasetItems": 100,
  "matchMode": "strict"
}
```

# Actor output Schema

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

Dataset with one row per company

## `summary` (type: `string`):

Counts only. Person names are not stored here.

# 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": [
        {
            "lei": "529900SSGT49ZZSWYE62"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mikee368/eu-ubo-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 = { "companies": [{ "lei": "529900SSGT49ZZSWYE62" }] }

# Run the Actor and wait for it to finish
run = client.actor("mikee368/eu-ubo-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 '{
  "companies": [
    {
      "lei": "529900SSGT49ZZSWYE62"
    }
  ]
}' |
apify call mikee368/eu-ubo-lookup --silent --output-dataset

```

## MCP server setup

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