← Blog

Data scraping

KvK, RDW, Kadaster scrapen: een veldgids voor operators

Drie Nederlandse registers bevatten vrijwel alle publieke bedrijfsdata die je team ooit nodig heeft. De truc is weten welk register je scrapet, welk je betaalt, en welk je IP verbrandt.

Jacob Molkenboer· Oprichter · A Brand New Company· 4 jun 2026· 8 min
Drie manilla mappen met touw, koperen passer op kaart, groen lint, gebroken donkere lakzegel, rode postzegel.

Je hebt achtduizend KvK-nummers in een spreadsheet. De sales-lead wil directeuren, oprichtingsdata en SBI-codes voor vrijdag. Je opent een browser, richt een Puppeteer-script op kvk.nl, zet de concurrency op vijftig, en om 14:32 komen de requests terug met een Cloudflare-interstitial. Om 14:40 is de wifi op kantoor ook geblokkeerd. Welkom bij het verkeerd scrapen van Nederlandse publieke registers.

Het meeste in de Nederlandse publieke registers is inderdaad openbaar. De valkuil is dat openbaar niet hetzelfde is als vrij te scrapen. Drie registers bevatten bijna alles wat een enrichment-pijplijn ooit zal opvragen: de Kamer van Koophandel voor bedrijven, het Kadaster voor vastgoed en grond, en de RDW voor voertuigen. Elk heeft een andere houding tegenover toegang, en die houding is het hele verhaal.

Gratis toegang, per register

Drie korte oriëntaties, op volgorde van vrijgevigheid.

RDW. De voertuigautoriteit draait een open-data portal op opendata.rdw.nl via Socrata. Geen authenticatie nodig. Het volledige voertuigregister, zonder persoonlijk identificeerbare velden, zit achter JSON-, CSV- en SoQL-endpoints. Anonieme limieten liggen rond duizend requests per voortschrijdend uur; met een gratis app-token kom je in de miljoenen. Van kenteken naar merk, model, bouwjaar en APK-historie is één GET ver weg. Als elke Nederlandse dataset er zo uitzag, was dit artikel niet nodig geweest.

Kadaster. Het kadaster splitst zijn wereld in tweeën. De gratis helft staat op PDOK (Publieke Dienstverlening Op de Kaart): BAG-adressen en gebouwen, BRT- en BGT-topografie, AHN-hoogtegegevens, een aantal andere basisregisters, allemaal aangeboden als WFS, WMS en Atom-feeds onder expliciete hergebruiklicenties. De betaalde helft is het kadastrale dossier zelf: wie eigenaar is van perceel X, hoe het hypothecair belast is, wat er van eigenaar is gewisseld en voor hoeveel. Dat zit achter Mijn Kadaster en wordt per query gefactureerd. De tweede helft scrapen is geen technische vraag. Het is de verkeerde vraag.

KvK. De Kamer van Koophandel is het lastige geval. Er is een developer-portal op developers.kvk.nl met een Search API en een Profile API. Het testniveau geeft dummydata terug. Het live-niveau wordt per call gefactureerd. Bulkextracten via de Handelsregister Dataservice zijn een apart contract. De publieke website op kvk.nl zit achter Cloudflare en begint na een handvol requests vanuit dezelfde origin met interstitials. Wil je schone records op schaal, dan is de API de enige weg. De scraper is een manier om jezelf te laten blokkeren en je klant een klacht te bezorgen.

De juridische ondergrens die de meeste playbooks overslaan

Drie teksten horen op het bureau te liggen voordat je met registerwerk begint.

De eerste is de Algemene Verordening Gegevensbescherming (AVG), de Europese privacyverordening. Records van eenmanszaken en met naam genoemde bestuurders zijn persoonsgegevens. B2B-verrijking kan meestal steunen op gerechtvaardigd belang, maar de belangenafweging moet zijn opgeschreven vóórdat je begint, niet geïmproviseerd nadat er een klacht binnenkomt. De Autoriteit Persoonsgegevens heeft organisaties beboet voor precies dit patroon, en een LIA van één pagina met doel, noodzaak, balans en waarborgen is de goedkoopste verzekering die je ooit zult kopen.

De tweede is de Databankenwet, die EU-richtlijn 96/9/EG implementeert. Wie een substantiële investering doet in het verzamelen, verifiëren of presenteren van een databank heeft een sui generis-recht tegen substantiële opvraging of hergebruik, ook als de onderliggende data technisch gezien openbaar is. De jurisprudentie om te lezen is Innoweb tegen Wegener, waar een metazoekmachine die een advertentiesite bevraagde inbreuk maakte, en Ryanair tegen PR Aviation, waar een uitsluiting in de gebruiksvoorwaarden werd gehandhaafd bij afwezigheid van een sui generis-recht. Samen maken ze het scrapen van een gepubliceerde databank een gok met grote spreiding.

De derde is artikel 138ab van het Wetboek van Strafrecht: computervredebreuk. Een open URL ophalen is prima. Een captcha oplossen die het register voor je heeft gezet, een login omzeilen, of residentiële proxy's roteren om een IP-ban te ontwijken is op zijn minst verdedigbaar als het omzeilen van een technische beveiliging. Dat is een strafrechtelijk gesprek, geen vriendelijk technisch gesprek.

Waarschuwing

Zodra een register je een Cloudflare-challenge of captcha toont en je besluit die te verslaan, ben je van publieke data scrapen overgegaan naar een positie die je niet bij de Autoriteit Persoonsgegevens wilt verdedigen.

Hoe je IP in twintig minuten verbrandt

Moderne edge-bescherming kijkt niet naar de URL. Cloudflare, Akamai en de rest fingerprinten de TLS-handshake (JA3, JA4), de HTTP/2-frame-volgorde, de header-volgorde, de ontbrekende Sec-Fetch-* headers, en de request-rate per ASN. Een standaard Python requests-sessie is in één pakket herkenbaar, en veertig parallelle workers vanaf één kantoor-IP zien er precies zo uit als ze zijn.

Het defensieve recept is saai en werkt. Identificeer jezelf in de User-Agent met een echte productnaam, een echte URL en een echt contactadres. Draai één gelijktijdige verbinding per host. Slaap tussen requests met jitter; één tot twee seconden is een redelijke standaard. Respecteer Retry-After. Zegt de server zestig, slaap dan zestig. Stuur conditionele GETs met If-Modified-Since en ETag, want de meeste register-resources geven 304 Not Modified terug en kosten de server niets. Cache naar schijf voordat enige businesslogica de respons aanraakt. Je gaat opnieuw draaien, vaker dan je denkt.

Een nette scraper in twintig regels

import httpx
import random
import time
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential

UA = "ABN-Enrichment/1.0 (https://abn.company; info@abn.company)"

class RateLimited(Exception): pass

@retry(
    retry=retry_if_exception_type((httpx.HTTPError, RateLimited)),
    wait=wait_exponential(multiplier=2, min=4, max=120),
    stop=stop_after_attempt(5),
)
def fetch(client, url, etag=None):
    headers = {"User-Agent": UA}
    if etag:
        headers["If-None-Match"] = etag
    r = client.get(url, headers=headers, timeout=20)
    if r.status_code == 429:
        time.sleep(int(r.headers.get("Retry-After", "60")))
        raise RateLimited()
    r.raise_for_status()
    return r

def crawl(urls, store):
    with httpx.Client(http2=True) as client:
        for url in urls:
            cached_etag = store.get_etag(url)
            r = fetch(client, url, etag=cached_etag)
            if r.status_code != 304:
                store.put(url, r.text, r.headers.get("ETag"))
            time.sleep(1.2 + random.random() * 0.8)

Niets ingewikkelds. Eén worker per host, jitter, exponentiële backoff, identificatie, conditionele GET. Dit patroon overleeft elke Cloudflare-regelwijziging omdat het niet vijandig is. Het ziet eruit als wat het is: een klein programma dat publieke data langzaam leest.

Het omslagpunt tussen API en scraper

Het mentale model dat de meeste operators meebrengen is dat scrapen gratis is en de API duur, dus scrapen we. Reken het eens uit op een echte klus.

De KvK Profile API op het live-niveau kost onder de meeste contracten ongeveer twee eurocent per call. Achtduizend lookups tegen dat tarief is honderdzestig euro. Een scraper bouwen, onderhouden en opnieuw deblokkeren die de volgende Cloudflare-regelwijziging overleeft is minimaal één engineer-dag tegen een eerlijke interne kostprijs van zeshonderd tot duizend euro. De scraper wint op pure API-kosten. Hij verliest op time-to-result, juridische positie, en de tweede keer dat je de verrijking moet draaien.

Het omslagpunt is simpel. Ga je de verrijking vaker dan twee keer draaien, of voedt het resultaat iets dat klanten te zien krijgen, dan is de API goedkoper. Per-call infrastructuur is goedkoop ten opzichte van engineering-tijd, en elk finance-team dat ooit de tweede spoed-herschrijving van een scraper heeft moeten declareren zegt hetzelfde. Koop de call, niet de herbouw.

Containment voor agents die scrapen

Als wat het register aanroept een AI-agent is, richt 'm dan niet op het open internet. Zet een bemiddelende service tussen de agent en het register. De agent roept een lokaal /lookup?kvk=... endpoint aan. De bemiddelaar past de rate limit toe, de on-disk cache, de allow-list van welke paden bereikbaar zijn, en de maskering van persoonsgegevens die de belangenafweging vereist. De agent ziet nooit een 429, nooit een captcha, en besluit nooit zelf om proxy's te roteren.

Het principe is ouder dan het woord agent: de huurder kan niet meer dan wat de verhuurder doorlaat. De agent krijgt een getypeerde interface; de bemiddelaar dwingt rate, scope en field-level redaction af. Als een register zijn rate-houding verandert, pas je de bemiddelaar aan en doe je één redeploy. De agent-code verschuift niet.

Kernpunt

Behandel het register als een netwerkresource die de agent niet bezit. De rate limit, de cache en de juridische positie horen bij een service vóór de agent. De agent ziet alleen schone JSON.

Eén patroon uit een echte build

We bouwden dit voorjaar een enrichment-agent voor een Nederlands zoekbureau. Waar we tegenaan liepen was dat de KvK Search API gedeeltelijke bedrijfsrecords teruggeeft voor vrije-tekstquery's; handelsnamen, bestuurssamenstelling en SBI-detail komen pas naar boven als je per match een follow-up Profile-call doet, en Profile-calls worden gefactureerd. We losten het op door elk Profile-resultaat negentig dagen te cachen tegen een per-KvK-nummer-key, zodat de agent het register per bedrijf hoogstens vier keer per jaar betaalde in plaats van bij elk Slack-bericht. Hetzelfde model past op de meeste AI-agents die externe registers aanraken: bind de call site, cache op een stabiele key, en laat de agent twee keer gratis vragen.

Het kleinste wat je vandaag kunt doen: open een terminal en draai curl -I https://opendata.rdw.nl/resource/m9d7-ebf2.json. Lees de response headers. Zo ziet een register eruit dat zich netjes laat scrapen, en het is de benchmark waar elke andere dataset op je verrijkingslijst tegen afgemeten zou moeten worden.

Kern

Publieke Nederlandse registerdata valt in drie lagen: open, betaald-via-API, en juridisch giftig om te scrapen. Weet in welke laag je zit voordat je één request schrijft.

FAQ

Is het legaal om de publieke KvK-website te scrapen?

Open URL's zijn bereikbaar, maar de KvK-gebruiksvoorwaarden verbieden herverspreiding en de Databankenwet beschermt substantiële opvraging. Voor B2B-verrijking op schaal is de betaalde Search of Profile API de enige verdedigbare route.

Wat is de goedkoopste legitieme manier om een lijst KvK-nummers te verrijken?

De KvK Profile API op het live-niveau, negentig dagen gecached tegen het KvK-nummer. De meeste bedrijfsrecords zijn maandenlang stabiel en een per-call-prijs rond twee cent verdwijnt in de ruis zodra je de cache hergebruikt.

Waarom blokkeert Cloudflare mijn Python-script vrijwel meteen?

Edge-bescherming fingerprint de TLS-handshake, header-volgorde en request-rate, niet de URL. Een standaard requests-sessie is herkenbaar in één pakket. Schakel over op httpx met http2, identificeer jezelf, en vertraag naar één request per seconde.

Heb ik een belangenafweging nodig voor B2B-scraping?

Ja, als records persoonsgegevens bevatten zoals namen van eenmanszaken of bestuurders. Een LIA van één pagina met doel, noodzaak, balans en waarborgen, ondertekend en gedateerd vóór het scrapen, is wat de Autoriteit Persoonsgegevens verwacht.

data scrapingautomationintegrationsarchitectureoperationstooling

Iets bouwen?

Start een project