# Company Legal ID Finder: VAT & Registry Numbers (`olliolli/company-legal-id-finder`) Actor

Find the VAT and company registry number a website publishes about its owner (BE, NL, FR, LU, DE), checked with official checksums and live in VIES. Agency and host numbers are kept apart.

- **URL**: https://apify.com/olliolli/company-legal-id-finder.md
- **Developed by:** [Tom J](https://apify.com/olliolli) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 company identities

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

## Company Legal ID Finder (BE / NL / FR / LU / DE) — VAT & registry numbers with VIES check

Give a list of company websites. For each one you get the company number and VAT number published on that company's own website, checked with the official checksum rules and, optionally, with the European Commission's VIES service. Every number comes with the page where it was found.

**Use it to:** de-duplicate and match lead lists to registry data, fill in VAT numbers for invoicing or Peppol, check that a supplier's website names a valid company, or enrich a list from a business-listing or contact actor.

### What you get per website

- **Company number and VAT number** of the site owner, with the type and the country:
  - Belgium: enterprise number (KBO/BCE), which is also the VAT number
  - Netherlands: KvK number and VAT number (btw-id)
  - France: SIREN, SIRET and the intra-community VAT number
  - Luxembourg: VAT number, RCS number and legal-entity matricule
  - Germany: USt-IdNr and register number (Handelsregister HRB/HRA, or Vereinsregister VR, Partnerschaftsregister PR, Genossenschaftsregister GnR), with the register court
- **Checksum status** for every number: `valid`, `format_only` (KvK, RCS, HRB have no public checksum) or `invalid`.
- **VIES result** (when `viesCheck` is on): `valid`, `invalid` or `source_unavailable`, with the date of the check.
  - For a French company that only publishes its SIREN, the VAT number is derived from the SIREN and checked. The output marks this as derived.
  - The registered name and address are shown only when they contain a legal form (SA, BV, SARL, GmbH…). This keeps data of sole traders, who are natural persons, out of the output.
- **Name match** between the VIES name and the names the website uses: `match`, `partial`, `no_match` or `not_available`.
- **Confidence**: `high`, `medium` or `low`, with the reasons.
- **Several companies on one site** (`entity_conflict`), for example group or franchise sites.
- **Source evidence** for every number: the page URL and the text around the number. When no company name is confirmed by VIES (no VIES check, or a KvK/HRB number only), only the label and the number are kept, so that names, addresses or phone numbers of a sole trader around it are not repeated.
- **Third-party numbers kept apart**: numbers of the hosting provider, web agency or payment provider in a legal notice, and numbers of other parties in the site's structured data (creator, provider, parent organisation …), go to `third_party_identifiers` or are left out, not into the company identity.
- **Status per website**: `found` (billed: a checksum-valid number, a labelled register number or an active VIES number of the site owner), `found_unverified` (shown, not billed: weaker evidence, or the website redirects to another domain whose company it would be), `not_found`, `invalid_only`, `unreachable`, `redirected_to_other_domain`, `failed`, `invalid_input` (not a usable public website address; free).

### How it works

- Reads only the company's own website: the same domain, robots.txt respected, at most 5 pages. Contact and legal-notice pages (impressum, mentions légales, privacy) are read first.
- Numbers are taken only when a label is next to them (BTW/TVA/VAT, SIREN/SIRET/RCS, KvK, HRB, USt-IdNr…), or when they carry a country prefix with a valid checksum. Loose digit strings are ignored.
- No forms, no logins, no mailbox checks, no third-party platforms.
- VIES requests are spread out and retried when a member state is busy. An error is reported as `source_unavailable`, never as "invalid".

### Pricing

Pay per event (USD). Every run also carries Apify's standard start event (`apify-actor-start`): $0.00005 per run, or 5 cents per 1,000 runs.

| Event | When | Price |
|---|---|---|
| `website-checked` | every website that was checked (failures on our side are free) | $0.002 |
| `company-identity` | status `found`: a valid identifier of the site owner (checksum-valid, a labelled register number such as KvK/HRB/RCS, or active in VIES) | $0.02 |

- Example: 1,000 websites with identities on 260 of them cost about $2 + $5.20 = $7.20.
- Unreachable or empty websites only cost the small check. Rows are stored before they are charged. The run stops before your spending limit is passed and leaves websites it had no time for (also near `maxRunSeconds`); unprocessed websites are listed in OUTPUT and picked up when the run is resumed.

### Limits — please read

- A number on a website is a claim of that website. VIES confirms that a VAT number is registered. It does not prove that the website belongs to that company; the name match and confidence level are signals, not proof.
- `invalid` or `source_unavailable` in VIES does not prove that a company does not exist.
- Third-party numbers are recognised from the surrounding wording. Unusual layouts can be missed.
- Many small businesses do not publish their number on their website. Expect a result for a minority of websites. French and German sites must publish a legal notice; Dutch sites show their numbers less often.
- PDF documents are not read in this version.

### Measured results

Blind test, October 2026, on 400 company websites that the actor had never seen before: 80 each from Belgium, the Netherlands, France, Luxembourg and Germany, drawn at random from open map data.

- **What it found:** 103 websites (26%) published an identifier that the actor returned as the company identity.
  - 108 websites in the sample could not be reached at all.
  - Of the websites that could be read, about 1 in 3 gave a result.
  - Per country: Germany 36, Belgium 26, France 19, the Netherlands 14 and Luxembourg 8, out of 80 each.
- **How accurate it was:** for **99 of the 103**, the main identifier was the number of the company operating the website, copied exactly and with the right type.
  - The 4 misses were a hosting company's number, a truncated German register number, a Luxembourg permit number read as a French SIREN, and a Vereinsregister number labelled as a commercial register number.
  - The first three have since been fixed. The fixes are not part of the blind result.
- **How it was checked:** two reviewers, working separately, checked every number against the saved pages and agreed on all of them.
- **What it does not tell you:** this is a measured result on one sample, not a guarantee for your list.

### Data sources and terms

- The websites you provide, read according to their robots.txt.
- VIES, the VAT Information Exchange System of the European Commission (public service, no key needed).

# Actor input Schema

## `urls` (type: `array`):

Up to 500 company websites. Only each company's own site is read (robots.txt respected, at most 5 pages).

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

Optional. A dataset with a website column (for example the output of a business-listing or contact actor). Up to 5,000 rows / 500 unique websites are used. You are responsible for the origin of that list.

## `viesCheck` (type: `boolean`):

Checks the found VAT number with the European Commission's official VIES service and compares the registered name with the names the website uses. Registered names/addresses are only shown when they contain a legal form.

## `maxPages` (type: `integer`):

Includes the homepage. Contact and legal-notice pages are read first.

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

Bounded parallel scans.

## `maxRunSeconds` (type: `integer`):

Stops new work at this time budget. Unprocessed URLs are listed in OUTPUT.

## Actor input object example

```json
{
  "urls": [
    "https://www.example.be/"
  ],
  "viesCheck": true,
  "maxPages": 5,
  "maxConcurrency": 5,
  "maxRunSeconds": 900
}
```

# Actor output Schema

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

One row per website: status, identifiers with source page, VIES result, name match and confidence.

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

Counts by status, VIES requests and unprocessed websites (OUTPUT).

# 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 = {
    "urls": [
        "https://www.example.be/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("olliolli/company-legal-id-finder").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 = { "urls": ["https://www.example.be/"] }

# Run the Actor and wait for it to finish
run = client.actor("olliolli/company-legal-id-finder").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 '{
  "urls": [
    "https://www.example.be/"
  ]
}' |
apify call olliolli/company-legal-id-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,olliolli/company-legal-id-finder"
        }
    }
}
```

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/faEvMWtOwkcR5I94M/builds/ydwimhHAj8d7ELJdz/openapi.json
