# Company Registry Unified Lookup: LEI, SEC, EU VAT & More (`accountable_eel/company-registry-unified-lookup`) Actor

One identifier in, routed automatically: LEI codes to GLEIF, tickers/CIKs to SEC EDGAR, Norwegian org numbers to Brønnøysund, Finnish business IDs to PRH, EU VAT numbers to VIES. Pay only for whichever registry actually matches.

- **URL**: https://apify.com/accountable\_eel/company-registry-unified-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (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 gleif lei matches

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

## Company Registry Unified Lookup

Give it a company identifier — an LEI code, a stock ticker or SEC CIK, a Norwegian
organization number, a Finnish business ID, or an EU VAT number — and it routes the
lookup automatically to the matching official registry: [GLEIF](https://www.gleif.org)
for LEIs, [SEC EDGAR](https://www.sec.gov/edgar) for U.S. tickers/CIKs,
[Brønnøysund](https://data.brreg.no) for Norway, [PRH](https://www.prh.fi) for Finland,
or [EU VIES](https://ec.europa.eu/taxation_customs/vies) for VAT numbers. You get one
row back per identifier with a normalized legal name, status, and jurisdiction, plus
registry-specific detail. No API keys, no scraping — every one of the five sources is
an official, free, public registry API, queried directly.

### Who it's for

The accountable\_eel catalogue sells company intelligence columns for outbound. Each actor takes a
list of domains or company identifiers and returns one flat, stably-named row per input —
firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an
n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and
per-domain: a few tenths of a cent for a row that was actually found, and nothing for a
miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists —
VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No
seat licence, no monthly minimum, no credit system to decode.

For this actor, the input identifier's own shape — 20 characters ending in two digits, a bare
ticker, nine digits, a hyphenated Finnish ID, a two-letter country prefix — is what decides
whether GLEIF, SEC EDGAR, Brønnøysund, PRH, or VIES gets called, so you can paste a mixed list of
LEIs, tickers, and VAT numbers without sorting it first.

### Why this one

- **Shape-based auto-routing.** You don't pick which of five registries to check — the
  identifier's own format decides, before any network call is made.
- **One input field for five identifier types.** LEI, SEC ticker/CIK, Norwegian org
  number, Finnish business ID, EU VAT number — mixed lists work in a single run.
- **Almost always one network call per identifier.** A shape check runs first, so a
  registry that clearly doesn't match the identifier's format is never queried, and
  never billed.
- **Same field names regardless of source.** Legal name, status, and jurisdiction come
  back in a consistent shape whether the answer came from GLEIF, EDGAR, Brønnøysund,
  PRH, or VIES.
- **No API keys, no scraping.** All five sources are official, free, public registry
  APIs — nothing to sign up for, nothing that can get blocked as a bot.

### What you get

Every row carries these fields regardless of which registry matched:

| Field | Type | Description |
|---|---|---|
| `query` | string | The identifier exactly as submitted |
| `found` | boolean | `true` if any of the five registries returned a match |
| `status` | string | `"OK"` on a match, or a short reason string on a miss |
| `sourcesFound` | array of strings | Which registry keys (below) actually returned data |
| `sourcesTried` | array of strings | Every registry key considered for this identifier — each source checks the identifier's shape first, so this reflects which sources were shape-eligible, not necessarily which fired a network request |
| `scrapedAt` | ISO 8601 datetime | When the lookup ran |

Then one object per registry — `null` if the identifier's shape doesn't match that
registry's format, or if the shape matches but no record was found:

**`lei`** — GLEIF LEI Registry match

| Field | Type | Description |
|---|---|---|
| `lei` | string | The 20-character LEI code |
| `legalName` | string | null | Registered legal entity name |
| `status` | string | null | LEI record status (e.g. `ISSUED`) |
| `jurisdiction` | string | null | Registered jurisdiction code |
| `country` | string | null | Country of the legal address |

**`secEdgar`** — SEC EDGAR match

| Field | Type | Description |
|---|---|---|
| `cik` | string | Zero-padded 10-digit SEC Central Index Key |
| `name` | string | Registered company name |
| `tickers` | array of strings | Ticker symbols on file for this filer |
| `sicDescription` | string | null | SEC industry classification (SIC) description |
| `stateOfIncorporation` | string | null | State/jurisdiction of incorporation |

**`noRegistry`** — Brønnøysund (Norway) match

| Field | Type | Description |
|---|---|---|
| `organizationNumber` | string | 9-digit Norwegian organization number |
| `name` | string | null | Registered entity name |
| `organizationForm` | string | null | Legal form (e.g. AS, ENK) |
| `industryDescription` | string | null | Primary industry code description |

**`fiRegistry`** — PRH (Finland) match

| Field | Type | Description |
|---|---|---|
| `businessId` | string | Finnish business ID (`NNNNNNN-N`) |
| `name` | string | null | Registered entity name |
| `companyForm` | string | null | Legal form description |
| `mainBusinessLine` | string | null | Primary business line description |

**`euVat`** — EU VIES VAT Registry match

| Field | Type | Description |
|---|---|---|
| `vatNumber` | string | null | The VAT number as confirmed by VIES |
| `countryCode` | string | 2-letter EU country code |
| `name` | string | null | Registered name, if the member state publishes it |
| `address` | string | null | Registered address, if the member state publishes it |

Not every member state publishes name and address through VIES — some return only a
valid/invalid flag, in which case `name` and `address` come back `null` even though
`found` is `true`.

### Price

- **GLEIF LEI match**: $4 per 1,000 company codes
- **SEC EDGAR match**: $4 per 1,000 company codes
- **Brønnøysund (Norway) match**: $4 per 1,000 company codes
- **PRH (Finland) match**: $4 per 1,000 company codes
- **EU VIES VAT match**: $4 per 1,000 company codes

Plus a $0.00005 start fee per run. Each event above is billed independently, only when it actually returns data — misses (`found:false`) are never charged.

That's $0.004 per registry matched at the FREE tier ($0.002 on GOLD), plus a $0.00005 per
run start fee. This is a composite actor, so billing is per source that returns data —
but because every source is shape-gated, a given identifier almost never matches more
than one registry. The exception is a bare 9-digit input, which is checked against both
Brønnøysund and SEC EDGAR (the SEC branch almost always comes back null for a real
Norwegian number, but if it somehow matched both, that one row would be billed
$0.008 instead of $0.004). Worst case across the whole set — every registry matching
every row — is $0.02 per row, but that shape never actually occurs given how the
identifier formats are defined.

1,000 identifiers through this actor: **~$4** if every one is found and matches exactly
one registry, which is the common case. The same 1,000 rows through a credit-based
enrichment platform: **$80–$400**.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `companyCodes` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~company-registry-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"companyCodes":["5493001KJTIIGC8Y1R12"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

1) Paste your identifiers into `companyCodes` — one per line, mixed types are fine.
2) Turn on `testRun` to check the first 5 against your list before running the full
   batch.
3) Run the actor. Each identifier gets shape-checked against all five formats, and only
   the registries it plausibly matches get queried.
4) Read the dataset: one row per identifier, `found: true` rows carry data in the
   matching registry field, `found: false` rows are still there for completeness but
   were never charged.
5) If you only care about certain registries, set `columns` to that subset — the actor
   skips querying (and billing for) any registry you didn't select.

### Input

```json
{
  "companyCodes": [
    "5493001KJTIIGC8Y1R12"
  ]
}
```

One code per line — an LEI code, a stock ticker or SEC CIK, a Norwegian org number, a Finnish business ID, or an EU VAT number. Routed automatically to the matching registry. Accepted formats: 5493001KJTIIGC8Y1R12, AAPL, 923609016, 0112038-9, IE6388047V.

```json
{
  "companyCodes": ["5493001KJTIIGC8Y1R12"],
  "testRun": false,
  "onlyFound": false,
  "maxConcurrency": 5
}
```

`companyCodes` is a list of identifiers — mix and match freely: an LEI
(`5493001KJTIIGC8Y1R12`), a stock ticker (`AAPL`) or SEC CIK, a 9-digit Norwegian org
number (`923609016`), a Finnish business ID (`0112038-9`), or an EU VAT number
(`IE6388047V`). One dataset row is returned per identifier; rows with `"found": false`
are never charged. Turn on `testRun` to try your list against just the first 5
identifiers before running the full batch. Optional `includeKeywords` /
`excludeKeywords` filter rows by content, `maxResults` caps the run early, and `columns`
lets you pick which of the five registries to include.

### Sample output

| query | found | status | lei | secEdgar | noRegistry | fiRegistry | euVat | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 5493001KJTIIGC8Y1R12 | true | OK | {"lei":"5493001KJTIIGC8Y1R12","legalName":"Bloomberg Finance L.P.","status":"ACTIVE","jurisdiction":"US-DE","country":"US"} | <sec edgar match> | \<brønnøysund (norway) match> | \<prh (finland) match> | <eu vies vat match> | 2026-08-31T06:50:13.194Z |

One row per identifier, for example:

```json
{
  "query": "5493001KJTIIGC8Y1R12",
  "found": true,
  "status": "OK",
  "lei": {
    "lei": "5493001KJTIIGC8Y1R12",
    "legalName": "APPLE INC.",
    "status": "ISSUED",
    "jurisdiction": "US-CA",
    "country": "US"
  },
  "secEdgar": null,
  "noRegistry": null,
  "fiRegistry": null,
  "euVat": null,
  "sourcesFound": ["lei"],
  "sourcesTried": ["lei", "secEdgar", "noRegistry", "fiRegistry", "euVat"],
  "scrapedAt": "2026-08-21T10:00:00.000Z"
}
```

Each registry that the identifier's shape doesn't match returns `null` immediately
without a network call. An identifier that matches no registry's format, or matches the
shape but isn't found in that registry, still gets a row (`"found": false`), so you
always get one row per input — but you're never charged for those.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~company-registry-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"companyCodes":["5493001KJTIIGC8Y1R12"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~company-registry-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"companyCodes":["5493001KJTIIGC8Y1R12"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~company-registry-unified-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"companyCodes":["{{company code}}"]}`, mapping the row's company code into the `companyCodes` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Unified Company Registry API | Apify" — the agent will find and run this actor.

### Tips

- **Narrow with `columns` if you only care about one or two registries.** Unselected
  registries are neither queried nor billed, so this is a real way to cut cost, not
  just a display filter.
- **Don't pre-sort your list by identifier type.** The whole point of this actor is that
  mixed lists work — an LEI, a ticker, and a VAT number can sit in the same
  `companyCodes` array.
- **Ticker resolution depends on SEC's own mapping, refreshed at actor startup.** A
  ticker that was delisted or renamed since the last refresh may resolve to
  `"found": false"` even if the company still exists.
- **Run `testRun` first on an unfamiliar list.** It's the cheapest way to confirm your
  identifiers are shaped the way you think they are before committing to the full
  batch.
- **A bare 9-digit number is checked against two registries, not one.** If you have a
  list of purely Norwegian org numbers, expect the `secEdgar` field to come back `null`
  on nearly every row — that's the SEC branch correctly finding nothing, not a bug.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`company-registry-unified-lookup`) | $0.004 per registry matched (FREE tier, $0.002 on GOLD), $0.00005 actor start, nothing for a miss | One row per identifier — legal name, status, and jurisdiction from whichever of five official registries (GLEIF, SEC EDGAR, Brønnøysund, PRH, EU VIES) matches the identifier's shape | Five registries, but each identifier only ever routes to the one it actually matches by shape — it won't find a company you only know by name, and it doesn't cover registries outside these five countries/schemes |
| **Clay** | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |
| **The official portals** (GLEIF, EDGAR, Brønnøysund, PRH, VIES) directly | Free | The same data, from the same source | It's the same source — this actor exists for the batch: identifiers in, rows out, with the right registry picked automatically and retries handled. One identifier, once? Use the portal. |
| **Doing it yourself** | Your time + five different APIs, five different auth/rate-limit/response shapes, and the shape-detection logic to route each identifier correctly | The same data | This actor is that routing logic and those five integrations, already written and already handling the edge cases (delisted tickers, VIES member states that don't publish name/address, the 9-digit overlap) |

Prices for third-party tools are their published list prices as of August 2026 and are
not tracked here — check the vendor before relying on the comparison.

### FAQ

**What happens if an identifier doesn't match any registry's shape, or matches but isn't
found?**
It's still returned as a row, with `"found": false` and every registry field `null`.
Neither case is billed — you only pay for a registry that actually returned data.

**How does the actor decide which registry to check?**
Purely by the identifier's shape: 20-character alphanumeric strings are tried as an
LEI, all-letter strings as a ticker, all-digit strings as an SEC CIK or (if exactly 9
digits) also as a Norwegian org number, `NNNNNNN-N` as a Finnish business ID, and a
2-letter country prefix followed by digits/letters as an EU VAT number. Most inputs
only match one shape, so only one registry is ever actually called.

**Why does a 9-digit number sometimes get checked against two registries?**
A bare 9-digit string is a plausible Norwegian organization number and also technically
fits the SEC CIK pattern (which allows up to 10 digits). There's no reliable way to
distinguish them by digits alone, so both are tried — in practice, real SEC CIKs are far
smaller once zero-padded, so the SEC branch almost always returns null for a genuine
Norwegian number.

**Do I need API keys for any of the five registries?**
No. GLEIF, SEC EDGAR, Brønnøysund, PRH, and EU VIES are all free and public — none of
them requires credentials on your end, and the actor doesn't need any either.

**Are stock ticker lookups reliable, and how fresh is the ticker list?**
Tickers are resolved against SEC's own ticker-to-CIK mapping, refreshed once at actor
startup. A ticker not in SEC's current listing — delisted, foreign-only, or a typo —
resolves to `"found": false"` for the `secEdgar` field rather than a guess.

**Does this route through a proxy, and will a large batch get rate-limited?**
No proxy — this actor talks to each registry's public API directly rather than
scraping, so there's nothing to route through Apify Proxy for. `maxConcurrency`
controls how many identifiers are processed in parallel; lower it if you see failed
requests on a very large batch, since these are still live third-party APIs with their
own fair-use limits.

**Is this personal data under GDPR?**
The fields returned here are public company-registration facts — legal entity name,
status, jurisdiction, registry ID — published by government or regulatory registries,
not personal data about individuals. If your own use of the output involves EU
residents' data elsewhere in your pipeline, that's a question for your own DPO, not
something this actor's output introduces on its own.

**Can I schedule this to run automatically, and can an AI agent call it directly?**
Yes to both. Apify's built-in Scheduler can re-run this actor on a cron schedule against
a saved input (useful for periodically re-checking a vendor or counterparty list), and
because it exposes a synchronous HTTP endpoint, any MCP client with the Apify MCP server
connected can find and run it by name — see the integrations section above.

### Related actors

- [EU VAT Validator](https://apify.com/accountable_eel/eu-vat-validator) — single-purpose
  VIES lookup, if VAT numbers are all you need
- [GLEIF LEI Lookup](https://apify.com/accountable_eel/gleif-lei-lookup) — single-purpose
  LEI registry lookup
- [SEC EDGAR Company Lookup](https://apify.com/accountable_eel/sec-edgar-company-lookup)
  — single-purpose ticker/CIK lookup against SEC EDGAR

# Actor input Schema

## `companyCodes` (type: `array`):

One code per line — an LEI code, a stock ticker or SEC CIK, a Norwegian org number, a Finnish business ID, or an EU VAT number. Routed automatically to the matching registry. Accepted formats: 5493001KJTIIGC8Y1R12, AAPL, 923609016, 0112038-9, IE6388047V. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

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

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which sources to check. Only the sources you select here are checked — and only the ones that actually return data are billed. All are included by default.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## Actor input object example

```json
{
  "companyCodes": [
    "5493001KJTIIGC8Y1R12"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "lei",
    "secEdgar",
    "noRegistry",
    "fiRegistry",
    "euVat"
  ],
  "maxConcurrency": 5
}
```

# Actor output Schema

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

No description

# 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 = {
    "companyCodes": [
        "5493001KJTIIGC8Y1R12"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/company-registry-unified-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 = {
    "companyCodes": ["5493001KJTIIGC8Y1R12"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/company-registry-unified-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 '{
  "companyCodes": [
    "5493001KJTIIGC8Y1R12"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/company-registry-unified-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/company-registry-unified-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/gPmHMwyuzJ85BvLvU/builds/tngtxJsBN6ObjLBa4/openapi.json
