# Company Registry Lookup API: KYB, LEI, VAT, Officers (`chaptersoft/kyb-company-lookup`) Actor

Verify companies by name or ID in 10 official registries: UK Companies House, SEC EDGAR, GLEIF LEI, France, Norway, Finland, Czechia, Poland KRS, Latvia, Brazil CNPJ. Status, address, VAT, industry, LEI and officers as one JSON. Not-found rows are free.

- **URL**: https://apify.com/chaptersoft/kyb-company-lookup.md
- **Developed by:** [Jeremy Lam](https://apify.com/chaptersoft) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 company records

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

## Company Registry Lookup API: KYB, LEI, VAT, Officers (UK, US, EU, Brazil)

Verify any company in seconds from **official open registries**. Paste company names or registration numbers and get one clean, normalised JSON record per company: legal name, registry ID, **status (active / dissolved / in liquidation)**, legal form, incorporation date, registered address, **industry code**, **VAT / tax ID**, **LEI**, share capital and **current directors / officers**, with a link to the source.

**10 sources in one Actor:** UK Companies House · US SEC EDGAR · GLEIF LEI (2.9M+ entities, 200+ countries) · France SIRENE/RNE · Norway Brønnøysund · Finland PRH/YTJ · Czechia ARES · Poland KRS · Latvia Register of Enterprises · Brazil CNPJ.

- 💸 **$3 per 1,000 companies found** ($2.50 on Starter, $2 on Scale and above). **No start fee.** Not-found, invalid and registry-error rows are **free**.
- 👥 **Officers and LEI are included at no extra cost**, not as paid add-ons.
- 🔎 **Mix names and IDs.** IDs are auto-detected and checksum-validated (LEI, SIREN/SIRET, orgnr, Y-tunnus, IČO, CNPJ, VAT numbers, KRS, CIK, `$TICKER`), so a typo is flagged instantly instead of being billed as "not found".
- 🔗 **An LEI resolves to the national record:** give `LEI 549300A0JPRWG1KI7U06` and you get Nokia Oyj from the Finnish register, plus its LEI status.
- 🧭 **Name search across all registries at once,** ranked by match quality and then by size signals (listed, PLC/ASA, LEI holder, active), so "Tesco" returns TESCO PLC, not a dissolved shell.
- 🛟 **Built for batch KYB:** up to 5,000 queries per run, polite per-registry rate limits with automatic back-off, partial results instead of failed runs, and a clear list of anything left unprocessed.

### What you can look up

| Registry | Country | Lookup by ID | Search by name | Officers | VAT / tax ID | Source licence |
|---|---|---|---|---|---|---|
| Companies House | 🇬🇧 UK | Company number (`00445790`, `SC123456`) | ✅ | ✅ directors, secretaries | – | Open Government Licence v3 |
| SEC EDGAR | 🇺🇸 US (SEC filers) | CIK, `$TICKER` | ✅ | – | EIN where filed | Public domain |
| GLEIF | 🌍 200+ countries | LEI | ✅ | – | – | CC0 |
| SIRENE / RNE | 🇫🇷 France | SIREN, SIRET, FR VAT | ✅ | ✅ dirigeants | ✅ FR VAT | Licence Ouverte 2.0 |
| Brønnøysund | 🇳🇴 Norway | Org. number, NO VAT | ✅ | ✅ board, CEO | ✅ (if VAT-registered) | NLOD 2.0 |
| PRH / YTJ | 🇫🇮 Finland | Y-tunnus, FI VAT | ✅ | – (not published by PRH) | ✅ | CC BY 4.0 |
| ARES + Public Register | 🇨🇿 Czechia | IČO, CZ VAT | ✅ | ✅ statutory body, board | ✅ | Open data |
| KRS | 🇵🇱 Poland | KRS number | via GLEIF | ✅ (masked by the registry) | ✅ NIP / PL VAT | Public information |
| Register of Enterprises | 🇱🇻 Latvia | Reg. code | ✅ | – | – | CC0 |
| Receita Federal CNPJ | 🇧🇷 Brazil | CNPJ | via GLEIF | ✅ QSA partners | ✅ CNPJ | Receita open data ¹ |

¹ Receita Federal publishes CNPJ data only as bulk files, so Brazil is served by the open-source mirrors **minhareceita.org** (primary) and **BrasilAPI** (fallback). Every record states its source in `sourceUrl` / `sourceLicense`.

### Input examples

```json
{
  "queries": [
    "Tesco PLC",
    "GB 00445790",
    "FR27552032534",
    "NO 923609016",
    "FI 0112038-9",
    "CZ 45274649",
    "KRS 0000019193",
    "LV 40003245752",
    "BR 33.000.167/0001-01",
    "LEI 549300A0JPRWG1KI7U06",
    "CIK 320193",
    "$MSFT"
  ],
  "maxResultsPerQuery": 3,
  "includeOfficers": true,
  "includeLei": true
}
```

- **Only some countries:** `"countries": ["GB", "FR"]` limits name searches (and bare numbers) to those registries.
- **Strict matching:** `"minMatchScore": 95` keeps only same-name matches (ignoring Ltd / Oyj / a.s.).
- **Screening a list:** `"activeOnly": true` drops dissolved and bankrupt companies, and `"includeNotFound": true` (default) gives one free row per unmatched query so your list reconciles 1:1.

### Output

One dataset item per company (`"type": "company"`, charged) plus free rows for `not-found`, `invalid` and `error` queries.

```json
{
  "type": "company",
  "query": "Tesco",
  "matchedBy": "name",
  "matchScore": 97,
  "registry": "GB",
  "registryName": "UK Companies House",
  "country": "GB",
  "companyId": "00445790",
  "idType": "CRN",
  "name": "TESCO PLC",
  "legalForm": "Public limited Company",
  "status": "active",
  "statusRaw": "Active",
  "incorporationDate": "1947-11-27",
  "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA",
  "industryCode": "47110",
  "industryScheme": "UK SIC 2007",
  "lei": "2138002P5RNKC5W2JZ46",
  "leiStatus": "ISSUED",
  "officers": [{ "name": "…", "role": "Director", "appointedOn": "2020-…", "isCompany": false }],
  "officersCount": 14,
  "sourceUrl": "https://find-and-update.company-information.service.gov.uk/company/00445790",
  "sourceLicense": "Companies House public register, Open Government Licence v3.0"
}
```

Every record has the same fields whatever the country (`null` when a registry doesn't publish a field). Registry-specific extras (SEC latest filings and exchanges, French SIRET/revenue, Czech file number, GLEIF data) are in `extra`. The dataset has **Overview**, **Officers** (one row per officer) and **Not found / invalid** views, and the key-value store `OUTPUT` record has run totals and connection statistics.

`status` is normalised to `active`, `inactive`, `in-liquidation`, `bankrupt`, `dissolved` or `unknown`, and `statusRaw` keeps the registry's own wording. For SEC filers, status is **inferred from filing activity** (SEC is a filings database, not a register of incorporation), which is stated in `statusRaw`.

### Pricing

Pay per event, no start fee, no monthly rental:

| Event | Free plan | Starter | Scale and above |
|---|---|---|---|
| Company record (found) | $3.00 / 1,000 | $2.50 / 1,000 | $2.00 / 1,000 |
| Not found / invalid ID / registry error | free | free | free |

Officers, LEI enrichment and all registry extras are included. Set **Maximum cost per run** in the run options to cap spend: the Actor stops cleanly at the limit and lists the queries it did not process.

### Privacy and fair use

- Data comes only from official open-data APIs and public registers, under their open licences (listed per record). No paid data brokers, no login-walled sources.
- For officers, the Actor keeps **name, role, appointment date and whether the officer is a company**. It never stores dates of birth, home addresses, ages or personal ID numbers, even where a registry shows them. Polish officer names stay masked exactly as the KRS API publishes them.
- Requests are rate-limited per registry well under each registry's published limits, with a descriptive User-Agent (SEC fair-access policy).
- You are responsible for using personal data (officer names) lawfully, e.g. for KYB/AML, due diligence or B2B verification under GDPR's legitimate-interest basis.

### Tips and limits

- **Brand names vs legal names:** registries index legal names. "Air Baltic Corporation" or the registration number finds Latvia's airBaltic, while "airBaltic" alone may not; "LVMH" or "Michelin" alone match small companies literally named LVMH / MICHELIN (the groups are registered as "LVMH Moët Hennessy Louis Vuitton" and "Compagnie Générale des Établissements Michelin"). Use the legal name or the ID for exact hits: IDs are always exact.
- **Germany, Switzerland, Japan and other GLEIF-only countries** return the GLEIF record (legal name, LEI, status, registered address, registration number), e.g. "Siemens AG" → Siemens Aktiengesellschaft.
- **SEC records** get their LEI via an exact-name GLEIF match for US-incorporated filers; foreign SEC filers (e.g. NOKIA CORP's 20-F filings) may show `lei: null`, but the home-country record above them carries it.
- Poland KRS and Brazil CNPJ have no keyless name search; name queries reach them only when GLEIF links the company (most larger companies). Use the KRS or CNPJ number for guaranteed hits.
- **Speed:** ID lookups take about 1–2 s each, and name searches (fanned out to 8 registries + GLEIF) take about 3 s, at roughly 1,200 name searches per hour within the registries' rate limits. For very large name batches, raise the run timeout. If a run hits its timeout or cost limit, it saves everything found so far and lists the rest in `OUTPUT.remainingQueries`.
- UK data comes from the public Companies House service (OGL).
- Finland's PRH doesn't publish officers in its open API, so `officersNote` explains why they're empty.

### Roadmap

Germany (Handelsregister), Denmark CVR, Netherlands KvK, Switzerland Zefix, Estonia, Australia ABN, UK PSC (beneficial owners) via an optional Companies House API key, and US state registries. Missing a country or field you need? Open an issue on the Actor page.

# Actor input Schema

## `queries` (type: `array`):

One per line. Mix freely: company names ("Tesco PLC", "Nokia Oyj"), registration numbers with an optional country prefix ("GB 00445790", "FR 552032534", "NO 923609016", "FI 0112038-9", "CZ 45274649", "KRS 0000019193", "LV 40003245752", "BR 33.000.167/0001-01"), VAT numbers ("FR27552032534", "CZ45274649"), SIRET, LEI codes ("LEI OW6OFBNCKXC4US5C7523"), SEC CIK ("CIK 320193") or stock tickers ("$AAPL"). IDs are validated (checksums) before any request. Up to 5,000 per run.

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

Limit name searches to these registries (IDs with a country prefix always go to their own registry). Empty = all 10.

## `maxResultsPerQuery` (type: `integer`):

For name searches: how many best matches to return (ranked by match quality, then by size signals such as listed/PLC/LEI). ID lookups always return the one company.

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

Name matches below this score are dropped. 100 = same name ignoring legal form (Ltd, Oyj, a.s.); 85+ = name contained in a longer name; 70 = loose.

## `activeOnly` (type: `boolean`):

Skip dissolved, struck-off, bankrupt and in-liquidation companies.

## `includeOfficers` (type: `boolean`):

Current directors, board members, secretaries and authorised signatories where the registry publishes them (UK, FR, NO, CZ, PL, BR). No dates of birth or home addresses are stored. Free.

## `maxOfficersPerCompany` (type: `integer`):

Cap on officers per company (large groups can list hundreds). 0 = none.

## `includeLei` (type: `boolean`):

Look up each company's LEI in GLEIF and add it with its status. Free.

## `includeNotFound` (type: `boolean`):

Adds a free 'not-found' row (with the registries checked) for every query without a match, so you can reconcile your list. Invalid IDs and registry errors are always reported (free).

## Actor input object example

```json
{
  "queries": [
    "Tesco PLC",
    "NO 923609016",
    "LEI 549300A0JPRWG1KI7U06",
    "$AAPL"
  ],
  "maxResultsPerQuery": 1,
  "minMatchScore": 70,
  "activeOnly": false,
  "includeOfficers": true,
  "maxOfficersPerCompany": 50,
  "includeLei": true,
  "includeNotFound": true
}
```

# Actor output Schema

## `companies` (type: `string`):

Overview table: name, ID, registry, status, address, industry, VAT, LEI, match score.

## `officers` (type: `string`):

One row per officer per company.

## `fullJson` (type: `string`):

Every field including registry extras.

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

Totals, charged records, per-registry counts, connection statistics, notes and any queries left unprocessed.

# 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 = {
    "queries": [
        "Tesco PLC",
        "NO 923609016",
        "LEI 549300A0JPRWG1KI7U06",
        "$AAPL"
    ],
    "maxResultsPerQuery": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("chaptersoft/kyb-company-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 = {
    "queries": [
        "Tesco PLC",
        "NO 923609016",
        "LEI 549300A0JPRWG1KI7U06",
        "$AAPL",
    ],
    "maxResultsPerQuery": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("chaptersoft/kyb-company-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 '{
  "queries": [
    "Tesco PLC",
    "NO 923609016",
    "LEI 549300A0JPRWG1KI7U06",
    "$AAPL"
  ],
  "maxResultsPerQuery": 1
}' |
apify call chaptersoft/kyb-company-lookup --silent --output-dataset

```

## MCP server setup

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