# KvK Handelsregister Scraper - Dutch Chamber of Commerce (`crawloop/kvk-handelsregister-scraper`) Actor

Scrape the Dutch Chamber of Commerce (KvK) Handelsregister: search by name, city, postal code, or look up 8-digit KvK numbers. Get legal form, addresses, trade names, activity text, and registration date. Fast HTTP crawl on Apify, no login.

- **URL**: https://apify.com/crawloop/kvk-handelsregister-scraper.md
- **Developed by:** [Andrej Kiva](https://apify.com/crawloop) (community)
- **Categories:** Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 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

## KvK Handelsregister Scraper - Dutch Chamber of Commerce

> **Disclaimer:** Unofficial tool — not affiliated with, sponsored by, or endorsed by the Dutch Chamber of Commerce (Kamer van Koophandel / KvK) or its affiliates. Data is read from publicly accessible search cards only. No login. You are responsible for complying with applicable law (including GDPR) and the source terms. This is not a substitute for a paid extract, UBO extract, or other official KvK products. No warranty on accuracy or availability. Provided for informational and research use.

| KvK Handelsregister (NL) | Europages (Europe) | WLW (DACH) |
| :--- | :--- | :--- |
| **KvK Handelsregister Scraper** ◄── you are here | [Europages Scraper](https://apify.com/crawloop/europages-scraper) | [WLW Scraper](https://apify.com/crawloop/wlw-scraper) |
| Dutch Chamber of Commerce search + KvK-number lookup | EU B2B directory, VAT, contacts, certificates | DE / AT / CH suppliers, phone, email, VAT |

**KvK Handelsregister scraper** (Kamer van Koophandel) for Apify — a **KvK API alternative** for public search cards. Turn a **company name**, **city / postcode**, or **8-digit KvK number** into structured **Dutch Chamber of Commerce** rows. Collect **KvK number**, **branch number (vestigingsnummer)**, **legal form**, **active / deregistered status**, **establishment type**, **visiting and postal address**, **current and former trade names**, **activity text**, and **registration date** when the public search card shows them.

Ideal for **KYB / counterparty checks**, **CRM enrichment**, **Dutch SME lead lists**, and **market mapping**. Fast HTTP crawl — no headless browser, no login. Export **JSON**, **CSV**, or **Excel**. Works from **Python**, **Node.js**, or an **MCP** / AI assistant via Apify.

### When to use this Actor

- You need **KvK Handelsregister** identity from the public search (name, number, address, legal form)
- You want **batch lookup** of 8-digit KvK numbers in one run
- You prefer a **browser-free** Dutch company-register crawl on Apify
- You will enrich contacts later with a directory Actor (Europages / WLW)

### When not to use this Actor

- **Directors, UBO, RSIN, numeric SBI, employee count, or annual accounts** — those are paid KvK products, not public search cards
- **A legally certified extract (uittreksel)** — this Actor does not order or download official PDFs
- **Phone / email / website** — not published on the public search card
- **Non-Dutch registers** — use a source-specific Actor instead

### Key features

- **Name search** — trade name, statutory name, or keyword
- **Batch KvK lookup** — one or many 8-digit numbers
- **City and postcode** — appended to the query (the public index has no separate city parameter)
- **Handelsregister-only rows** — no advice-article pollution in the dataset
- **Registration date** on the public card when present
- **Filters** — active vs deregistered, establishment type, current vs former trade names
- **Dutch or English** search language
- **Pagination** beyond the website’s 50-result screen, still capped by `maxItems`
- **Streaming results** — dataset rows appear while the run is in progress
- **Deduped push** — unique by KvK number + branch number within a run
- **Lightweight** — `curl_cffi` Chrome TLS fingerprint, proxy optional (off by default)

### Input parameters

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `searchQuery` | String | `"Philips"` | Company name or keyword. Empty when you only look up KvK numbers. |
| `kvkNumbers` | Array | `[]` | 8-digit KvK numbers (batch). |
| `city` | String | — | Appended to the search query (e.g. Amsterdam). |
| `postalCode` | String | — | Dutch postcode (`1012AB`). Can be used alone. |
| `activeOnly` | Boolean | `true` | Only currently registered companies. |
| `establishmentTypes` | Array | `[]` | `hoofdvestiging`, `nevenvestiging`, `rechtspersoon`, `overig`. Empty = all. |
| `tradeNameFilter` | String | — | `bestaandeHandelsnamen` or `vervallenHandelsnamen`. Empty = both. |
| `language` | String | `"nl"` | `nl` or `en`. |
| `maxItems` | Integer | `20` | Max dataset rows (`0` = all hits for your queries). |
| `requestDelaySecs` | Number | `0.25` | Pause between HTTP requests. |
| `proxyConfiguration` | Object | proxy off | Enable Apify Proxy only if you see 403/429. |

Provide at least one of: `searchQuery`, `kvkNumbers`, `city`, `postalCode`.

#### Example — name search

```json
{
  "searchQuery": "Philips",
  "activeOnly": true,
  "language": "nl",
  "maxItems": 20
}
```

#### Example — batch KvK lookup

```json
{
  "kvkNumbers": ["90428587", "17062897"],
  "maxItems": 50
}
```

#### Example — keyword + city

```json
{
  "searchQuery": "bakker",
  "city": "Amsterdam",
  "activeOnly": true,
  "maxItems": 50
}
```

### Output fields

One dataset item per establishment or legal entity.

| Field | Type | Description |
| :--- | :--- | :--- |
| `kvkNumber` | String | 8-digit KvK number |
| `branchNumber` | String | 12-digit vestigingsnummer when present |
| `name` | String | Name shown on the search card |
| `statutoryName` | String | Current statutory name |
| `tradeNames` | Array | Current trade names |
| `formerTradeNames` | Array | Former names when the search matched them |
| `legalForm` / `legalFormCode` | String | e.g. Besloten Vennootschap / BV |
| `isActive` | Boolean | Currently registered |
| `registrationStatus` | String | `ingeschreven` or `uitgeschreven` |
| `establishmentType` | String | `hoofdvestiging`, `nevenvestiging`, `rechtspersoon`, `overig` |
| `registrationDate` | String | YYYY-MM-DD when published |
| `activityDescription` | String | Free-text activity (numeric SBI is **not** public) |
| `visitingAddress` / `postalAddress` | Object | street, houseNumber, postalCode, city |
| `city` / `postalCode` / `street` | String | Flattened visiting address for table views |
| `url` | String | Public search URL for this KvK number |
| `scrapedAt` | String | UTC timestamp |

#### Example output

```json
{
  "kvkNumber": "90428587",
  "branchNumber": "000056159579",
  "name": "Philips & Philips B.V.",
  "statutoryName": "Philips & Philips B.V.",
  "tradeNames": ["Philips & Philips B.V."],
  "legalForm": "Besloten Vennootschap",
  "legalFormCode": "BV",
  "isActive": true,
  "registrationStatus": "ingeschreven",
  "establishmentType": "hoofdvestiging",
  "registrationDate": "2023-06-07",
  "activityDescription": "Het uitoefenen van de rechtspraktijk als advocaat.",
  "visitingAddress": {
    "street": "Van Hengellaan",
    "houseNumber": "2",
    "postalCode": "1217AS",
    "city": "Hilversum"
  },
  "city": "Hilversum",
  "postalCode": "1217AS"
}
```

### Use cases

| Use case | What you get |
| :--- | :--- |
| **Verify a Dutch counterparty** | KvK number, legal form, active flag, visiting address |
| **Batch-enrich CRM KvK ids** | One row per number with name, branch, status |
| **Local lead list** | Keyword + city or postcode, capped with `maxItems` |
| **Main vs branch mapping** | Same `kvkNumber`, distinct `branchNumber` |
| **Deregistered check** | Set `activeOnly: false` and read `registrationStatus` |

### Public vs paid KvK data

| On this Actor (public search) | Not on this Actor (paid KvK products) |
| :--- | :--- |
| Name, KvK, vestigingsnummer, legal form | Directors / bestuurders, UBO |
| Visiting / postal address | RSIN, numeric SBI, employee count |
| Trade names, activity text, registration date | Annual accounts, certified extract PDF |
| Active vs deregistered | Insolvency extras beyond the public flag |

### Integration examples

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('crawloop/kvk-handelsregister-scraper').call({
  searchQuery: 'Philips',
  language: 'nl',
  maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.slice(0, 5));
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient(token)
run = client.actor("crawloop/kvk-handelsregister-scraper").call(
    run_input={"searchQuery": "Philips", "language": "nl", "maxItems": 20}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("kvkNumber"), item.get("name"), item.get("city"))
```

#### cURL

```bash
curl "https://api.apify.com/v2/acts/crawloop~kvk-handelsregister-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQuery":"Philips","language":"nl","maxItems":20}'
```

### MCP and AI assistants

Use this Actor from AI tools via [Apify MCP](https://docs.apify.com/platform/integrations/mcp).
Connect your Apify account, then call this Actor by its Store ID / name.

Example prompts:

- "Run KvK Handelsregister Scraper for searchQuery Philips, maxItems 20, and return KvK number, legal form, and city as JSON"
- "Look up KvK numbers 90428587 and 17062897 with KvK Handelsregister Scraper and summarise registration status"
- "Scrape bakeries in Amsterdam with KvK Handelsregister Scraper, then find matching EU suppliers with Europages Scraper"

### Suite next step

After you have Dutch register identity (KvK number, legal form, address), run [Europages Scraper](https://apify.com/crawloop/europages-scraper) for EU B2B contacts and VAT, or [WLW Scraper](https://apify.com/crawloop/wlw-scraper) for DACH supplier listings. For officers and published financials across EU registers, use [North Data Scraper](https://apify.com/crawloop/northdata-scraper).

### FAQ

**Does this return directors?** No. Public search cards do not include bestuurders. Do not expect an `officers` field.

**Is there a 50-result limit?** The public website UI shows at most 50 hits. This Actor paginates the same search index and stops at `maxItems`.

**Can I filter by city?** Yes — put the city in `city` (or in `searchQuery`). A dedicated city API parameter is not available on the public index.

**Why is SBI / RSIN empty?** Those fields are not on the public card. This Actor does not invent empty columns for them.

**Do I need a proxy?** Usually no. Enable Apify Proxy only if a run hits HTTP 403 or 429.

**Is this the official KvK API?** No. It reads public Handelsregister search cards only. You do not need a KvK API key, and it is not a substitute for Basisprofiel or a certified extract.

**How do I export?** Every run writes a dataset. Download JSON, CSV, or Excel from the run, or pull items with the Apify API / Python / Node.js client.

### Related Actors

| Actor | Use for |
| :--- | :--- |
| **KvK Handelsregister Scraper** ◄── you are here | Dutch Chamber of Commerce public search + KvK lookup |
| [Europages Scraper](https://apify.com/crawloop/europages-scraper) | Europe-wide B2B directory, VAT and contacts |
| [WLW Scraper](https://apify.com/crawloop/wlw-scraper) | DACH B2B suppliers from Wer liefert was |
| [North Data Scraper](https://apify.com/crawloop/northdata-scraper) | EU commercial-register identity, officers, published financials |

# Actor input Schema

## `searchQuery` (type: `string`):

Trade name, statutory name, or keyword. Combine with city or postal code for a tighter list. Leave empty when you only look up KvK numbers.

## `kvkNumbers` (type: `array`):

8-digit KvK numbers for direct lookup (batch). Non-digit characters are stripped.

## `city` (type: `string`):

Appended to the search query (the public index does not honour a separate city filter). Example: Amsterdam.

## `postalCode` (type: `string`):

Dutch postcode, with or without a space (1012AB). Can be used alone.

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

On (default): only currently registered (ingeschreven) companies. Off: include deregistered records.

## `establishmentTypes` (type: `array`):

Leave empty for all types. Applied after the search (hoofdvestiging, nevenvestiging, rechtspersoon, other).

## `tradeNameFilter` (type: `string`):

Limit hits to current or expired trade names. Empty = both.

## `language` (type: `string`):

Search UI language. Activity text is usually Dutch either way.

## `maxItems` (type: `integer`):

Hard cap across search and KvK lookups. 0 = all hits the public index returns for your queries. You are charged per dataset row.

## `requestDelaySecs` (type: `number`):

Polite pause between HTTP requests. 0.25 is enough for typical runs.

## `proxyConfiguration` (type: `object`):

Optional. Public KvK search usually works without a proxy. Enable Apify Proxy only if you hit HTTP 403/429.

## Actor input object example

```json
{
  "searchQuery": "Philips",
  "kvkNumbers": [],
  "city": "",
  "postalCode": "",
  "activeOnly": true,
  "establishmentTypes": [],
  "tradeNameFilter": "",
  "language": "nl",
  "maxItems": 20,
  "requestDelaySecs": 0.25,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Default dataset items (one row per establishment or legal entity).

# 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 = {
    "searchQuery": "Philips",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawloop/kvk-handelsregister-scraper").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 = {
    "searchQuery": "Philips",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("crawloop/kvk-handelsregister-scraper").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 '{
  "searchQuery": "Philips",
  "maxItems": 20
}' |
apify call crawloop/kvk-handelsregister-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawloop/kvk-handelsregister-scraper"
        }
    }
}
```

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/26KJ58njM5Jyu8eqP/builds/mCMp7neOF2LbI18PM/openapi.json
