KVK Company Search Scraper avatar

KVK Company Search Scraper

Pricing

Pay per event

Go to Apify Store
KVK Company Search Scraper

KVK Company Search Scraper

Search the Dutch Chamber of Commerce (KVK) Handelsregister by company name or activity keyword and get clean company rows: KVK number, branch number, trade names, legal form, status, address and activity. Deduplicated, with a town filter.

Pricing

Pay per event

Rating

0.0

(0)

Developer

DevilScrapes

DevilScrapes

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

Devil Scrapes mark

KVK Company Search Scraper

💰 $5.00 / 1 000 company records  ·  plus a $0.20 start fee per run  ·  pay per record delivered

The devil's in the data. 😈

Search the Dutch Chamber of Commerce business register (KVK Handelsregister) by company name or activity keyword and get clean, deduplicated rows: KVK number, branch number, trade names, legal form, status, registration date, visiting and postal address and the activity description. Run one query or many, narrow them to a town, and let us page through the results for you.

🎯 What this scrapes

The KVK Handelsregister is the official register of Dutch companies, and its public search has no export. This Actor runs the same search the kvk.nl search page runs (for example bakkerij or loodgieter), walks the results ten records at a time, and returns one row per register entry: the KVK number and branch number, the registered name, current and former trade names, the statutory name, legal form (code and Dutch description), whether the registration is active, whether the entry is a head office or a branch, registration date, visiting address (street, house number with suffix, postcode, town), postal address when the register lists one, and the activity description. Each row also records the query that found it and the number of records the register reports for it.

🔥 Features

  • 🛡️ We rotate browser fingerprints (curl-cffi Firefox, Chrome and Safari profiles) when a request is refused, so the retry looks like a different browser.
  • 🔁 We retry with exponential backoff on rate limits and server errors, honouring Retry-After. Up to 5 attempts per page.
  • 🌐 We rotate Apify Proxy sessions on every retry, with a fresh exit each time.
  • 🧊 Run-wide deduplication on KVK number plus branch number, across pages and across queries in one run.
  • 🧱 Fault isolation. A page that cannot be read after its retries is skipped and the walk continues; one query that fails doesn't end the others. The status message tells you what was skipped.
  • 📍 Town filter. Add a place and every query is narrowed to that town.

💡 Use cases

  • Sales and lead generation: list every registered business of a type in a town, with its KVK number and address.
  • KYC and onboarding: check that a company exists, is active, and which legal form and address it is registered under.
  • Market analysis: count and map businesses by activity, legal form and town.
  • Data enrichment: attach a KVK number and registered address to a list of company names.

⚙️ How to use it

  1. Add one or more search terms to queries, for example bakkerij and loodgieter.
  2. Optionally set place to a town such as Utrecht to narrow every query.
  3. Set maxResultsPerQuery and maxResults to bound how much you pull.
  4. Run it, then export to JSON, CSV, or Excel, or pull the dataset straight from the Apify API.

📥 Input

FieldTypeRequiredDefaultNotes
queriesarray<string>yesnoneCompany names or activity keywords. Blanks and duplicates are dropped.
placestringnononeTown added to every query, for example Utrecht.
maxResultsPerQueryintegerno100Row cap per query (1-10000).
maxResultsintegerno100Hard cap on total rows across the run (1-100000).
proxyConfigurationobjectyesApify ProxyDatacenter by default; see Limitations.

Example input

{
"queries": ["bakkerij", "loodgieter"],
"maxResultsPerQuery": 100,
"maxResults": 200,
"proxyConfiguration": { "useApifyProxy": true }
}

📤 Output

One row per deduplicated register entry.

FieldTypeNotes
kvkNummerstring8-digit KVK number.
vestigingsnummerstring12-digit branch number; with kvkNummer the dedupe key.
namestring | nullRegistered name shown in the search result.
tradeNames / formerTradeNamesarray<string>Current and former trade names.
statutoryNamestring | nullStatutory name of the legal entity, when listed.
legalFormCode / legalFormstring | nulle.g. BV and Besloten Vennootschap.
isActive / isBranchboolean | nullRegistration status and branch flag.
registrationType / registrationDatestring | nulle.g. Hoofdvestiging, and the ISO date.
street / houseNumber / postalCode / citystring | nullVisiting address.
postalAddressstring | nullPostal address on one line, when listed.
activityDescriptionstring | nullActivity description from the register.
recordIdstring | nullSearch index id of the record.
searchQuerystringEffective query that found the record.
totalResultsinteger | nullRecords the register reports for the query.
positioninteger1-based position within the query results.
sourcestringkvk-handelsregister.
scrapedAtstringUTC ISO-8601 run timestamp.

Example output record

{
"kvkNummer": "18078744",
"vestigingsnummer": "000010349758",
"name": "Bakkerij Saada",
"tradeNames": ["Bakkerij Saada"],
"formerTradeNames": [],
"legalFormCode": "VOF",
"legalForm": "Vennootschap Onder Firma",
"isActive": true,
"isBranch": true,
"registrationType": "Hoofdvestiging",
"registrationDate": "2005-06-01",
"street": "Verdiplein",
"houseNumber": "97",
"postalCode": "5049NR",
"city": "Tilburg",
"activityDescription": "Bakkerij",
"searchQuery": "bakkerij",
"totalResults": 15940,
"position": 4,
"source": "kvk-handelsregister",
"scrapedAt": "2026-10-13T07:39:44Z"
}

💰 Pricing

Pay-Per-Event: you pay only when these events fire.

EventUSDWhat it is
actor-start$0.20One-off start charge per run, charged with the first delivered batch
result-emitted$0.005Per deduplicated company record written to the dataset

Example: 1 000 rows = $5.20 per run ($0.20 start + 1000 x $0.005). No subscription, no minimum.

🚧 Limitations

  • About a thousand records per query. The register serves the first part of a query's results only (1 000 records were reachable in testing; far deeper requests are refused). For wider coverage, split a broad query by town with place.
  • Search is by name or keyword. Lookup by KVK number, postcode-only search and legal-form or status filters are not offered yet.
  • Town filter is search text. place is added to the query, so it matches records whose searchable text contains that town.
  • Overlapping queries overlap. A record matching two queries is returned once, under the first query that found it.
  • Datacenter proxy by default. If the register starts refusing it, switch proxyConfiguration to a residential group with a Dutch exit.
  • A query with no matches is a success. It finishes with zero rows and a status message naming what was searched.

❓ FAQ

Do I need a KVK account or API key? No. This reads the public search on kvk.nl.

How many records can I pull per query? Up to maxResultsPerQuery, bounded by the register's result window of roughly a thousand records per query. The totalResults column tells you the query's full size.

Why did my run return fewer rows than maxResults? Either the query has fewer records than your cap, the register's result window ended it, or another query already returned the same records. The status message says which.

What happens if a page is blocked? We retry up to 5 times with a fresh browser fingerprint and proxy session each time. If a page still can't be read it is skipped and counted in the status message. The run ends as failed only when a query was blocked and nothing at all could be collected.

Can I run this on a schedule? Yes. Use Apify Schedules to re-run your queries and catch newly registered businesses.

Use with n8n

Call the Actor from the Apify node in n8n, Make or Zapier, pass queries and place from your workflow, and read the dataset items back as rows.

💬 Your feedback

Found a bug, or need a field we don't return yet? Open an issue on the Actor's Issues tab. We read every one.