# Vastgoeddossier XXL — PDOK + CBS + Klimaat + Fundering (`codeclouds/vastgoed-xxl`) Actor

Samengesteld Nederlands vastgoed-indicatiedossier per adres: PDOK-geocoding, CBS-buurtcijfers, overstromingsrisico (RWS) en funderingsaandachtsgebied (RVO) in één genormaliseerd JSON-record. Combineert meerdere officiële open-databronnen die los al gratis zijn, maar nooit samen worden aangeboden.

- **URL**: https://apify.com/codeclouds/vastgoed-xxl.md
- **Developed by:** [Dennis](https://apify.com/codeclouds) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 dossier-resultaats

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/platform/actors/running/actors-in-store#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

## Vastgoeddossier XXL — PDOK + CBS + Klimaat + Fundering 🇳🇱🏠

One request per Dutch address → a combined property-risk dossier assembled from **four official open
data sources**: PDOK geocoding, CBS neighbourhood statistics, RWS flood-risk zoning, and RVO foundation
(subsidence) attention areas. Every single source is free and public on its own — nobody combines,
normalizes and caches them into one address-level record. That combination is the product.

### When should an AI agent use this?

- "Build a property dossier for this address: location, neighbourhood stats, flood risk, foundation risk."
- "Screen these 10 candidate properties for climate and foundation risk before a viewing."
- "Does this address sit in a flood-risk zone or a pre-1970 foundation attention area?"
- "Get CBS neighbourhood income/density figures plus climate risk for this postcode in one call."
- "Pre-screen a shortlist of addresses for a real estate due-diligence report."

### What this Actor does

- Geocodes your input (address, postcode, place name, or `"lat,lon"`) via the official **PDOK
  Locatieserver**, matched to `adres`-level by default so postcode/neighbourhood codes are always present
- Enriches with **CBS Kerncijfers Wijken en Buurten** (population, income, households, urbanity) for the
  matched neighbourhood/district/municipality
- Checks **RWS Richtlijn Overstromingsrisico (ROR)** via a live PDOK WMS lookup: does this point sit in a
  flood-risk zone, and what's the qualitative classification/description
- Checks the **RVO "Indicatieve aandachtsgebieden funderingsproblematiek"** layer via PDOK WMS: what
  percentage of buildings in this address's postcode area (PC6) were built before 1970 — the standard
  proxy for wooden pile-foundation vulnerability
- Adds a **`samenvatting`** (summary) block per dossier: a plain risk-level rollup (`laag`/`middel`/`hoog`/
  `onbekend`) so you don't have to inspect three nested objects to see whether an address stands out
- Processes multiple addresses per run, in parallel, fail-safe per item (one bad address never blocks the
  batch), with a free `RUN_SUMMARY` written to the key-value store at the end of every run
- Honest about lookup failures: if a flood-risk or foundation-area check itself fails (e.g. a temporary WMS
  outage), the field is `null` with `_status: "onbekend"` — never silently reported as "no risk found"

### Who is this for?

- **Real estate agents & buyers** — a fast, structured first-pass dossier before a viewing or offer
- **Mortgage advisors & insurers** — a documented, source-attributed risk signal to support (not replace)
  formal underwriting
- **Property developers & investors** — batch-screen a shortlist of addresses in one run
- **AI agents** — structured JSON, ideal as a due-diligence tool in an agentic property-research workflow

### Input

| Field | Type | Description |
|---|---|---|
| `query` / `queries` | string / array | Address(es), postcode(s), place name(s), or `"lat,lon"` — see the [input schema](.actor/input_schema.json) for the full bulk-item format |
| `lookupId` / `datasetId` | string | Direct PDOK object-ID lookup, or a linked Apify dataset as bulk input |
| `mode` | string | `auto` (default), `forward`, `reverse`, or `lookup` |
| `typeFilter` | string | PDOK object-type filter for forward search, default `adres` (recommended — see FAQ) |
| `enrichWithCbs` | boolean | Add CBS neighbourhood statistics (default `true`) |
| `includeKlimaat` | boolean | Add the flood-risk-zone check (default `true`) |
| `includeFundering` | boolean | Add the foundation-attention-area check (default `true`) |
| `concurrency` | integer | Addresses processed in parallel, 1-20 (default `5`) |

### Output

One dossier per input address:

```json
{
  "input_query": "Damrak 1 Amsterdam",
  "mode": "forward",
  "basis": {
    "weergavenaam": "Damrak 1, 1012LG Amsterdam",
    "postcode": "1012LG",
    "gemeente": "Amsterdam",
    "provincie": "Noord-Holland",
    "lat": 52.37714446,
    "lon": 4.89803846,
    "buurtcode": "BU0363AD03",
    "match_kwaliteit": "hoog",
    "_bron": "PDOK Locatieserver (Kadaster/BZK)"
  },
  "cbs": {
    "_bron": "CBS StatLine 86165NED (CC-BY, cbs.nl)",
    "aantal inwoners": 934526,
    "gemiddeld inkomen per inwoner": null,
    "huishoudens totaal": 516096,
    "stedelijkheid": 1
  },
  "klimaat": {
    "overstroming_risicogebied": true,
    "overstroming_classificatie": "Area of Potential Significant Flood Risk",
    "overstroming_omschrijving": "Rijn type B - beschermd langs hoofdwatersysteem",
    "_status": "ok",
    "_bron": "RWS Richtlijn Overstromingsrisico (ROR) WMS, service.pdok.nl/rws/overstromingen-risicogebied"
  },
  "fundering": {
    "funderingsaandachtsgebied": true,
    "funderingsgebied_postcode": "1012JS",
    "funderingsgebied_gemeente": "Amsterdam",
    "funderingsgebied_percentage_voor_1970": 75,
    "funderingsgebied_aantal_panden": 8,
    "_status": "ok",
    "_bron": "RVO Indicatieve aandachtsgebieden funderingsproblematiek WMS, service.pdok.nl/rvo/indgebfunderingsproblematiek",
    "_disclaimer": "Percentage/aantal panden geldt voor het POSTCODEGEBIED, niet per se voor dit specifieke pand. Grove indicatie, geen funderingsonderzoek."
  },
  "samenvatting": {
    "risiconiveau": "hoog",
    "omschrijving": "Let op: overstromingsrisicogebied, funderingsaandachtsgebied.",
    "aantal_aandachtspunten": 2,
    "aandachtspunten": ["overstromingsrisicogebied", "funderingsaandachtsgebied"],
    "onvolledige_modules": [],
    "_disclaimer": "Grove, opgetelde indicatie op basis van het aantal gevonden aandachtspunten — geen gewogen of gevalideerd risicomodel."
  },
  "_dossier_disclaimer": "Dit dossier combineert publieke open-databronnen (PDOK, CBS, RWS, RVO) en is uitsluitend indicatief. Het is geen taxatie, bouwkundige keuring of formeel risico-advies, en schept geen aansprakelijkheid voor koop-, verhuur-, verzekerings- of financieringsbeslissingen. Raadpleeg voor dergelijke beslissingen altijd een professioneel adviseur.",
  "error": null
}
```

- **`samenvatting.risiconiveau`** is a coarse, purely additive rollup (0 flags → `laag`, 1 → `middel`, 2 →
  `hoog`; `onbekend` if a module's lookup failed and there are otherwise no flags; `niet gecontroleerd` if
  both `includeKlimaat`/`includeFundering` are disabled, so nothing was actually checked) — not a weighted
  or validated risk model. Always check the underlying `klimaat`/`fundering` fields for detail.

- **`_status: "onbekend"`** on `klimaat`/`fundering` means the underlying WMS lookup itself failed (e.g. a
  temporary outage) — the corresponding boolean field is `null` in that case, never a silent `false`. This
  is different from a confirmed "no risk found" (`_status: "ok"`, field `false`).

- A free **`RUN_SUMMARY`** object is written to the run's key-value store at the end of every run
  (batch totals, counts per flag, average pre-1970 percentage, risk-level distribution) — no extra charge,
  useful when screening many addresses in one run.

- **`match_kwaliteit`** (`"hoog"` / `"middel"` / `"laag"` / `"onbekend"`) is a best-effort classification
  based on PDOK's own relevance score — not a guarantee. A `"laag"` value means "verify this address
  manually", not "this address doesn't exist".

- **`klimaat`/`fundering` are `null`** if the corresponding `include*` flag is off, or if no coordinate was
  available (failed geocoding).

- **`funderingsgebied_*` fields are area-level (per PC6 postcode), not building-specific** — there is no
  landelijk (nationwide) foundation register at the individual-address level. This is stated explicitly in
  `_disclaimer` on every result and must not be read as a building-specific foundation assessment.

- **`klimaat` has no water-depth scenario** — despite being commonly shown on flood-risk maps, there is no
  single nationwide PDOK layer with per-coordinate water-depth scenarios (verified live 2026-07-18); only a
  qualitative risk-zone classification exists at national scale.

- If geocoding fails entirely, `basis` is `null` and `error` explains why — no charge is made for that item.

### Use cases

**Single-address dossier before a viewing:**

```json
{ "query": "Damrak 1 Amsterdam" }
```

**Batch-screen a shortlist:**

```json
{ "queries": ["Damrak 1 Amsterdam", "Markt 1, Gouda", "52.3789,4.8952"] }
```

### Pricing

This Actor uses Apify's Pay-Per-Event (PPE) pricing model.

- **Actor Start:** $0.00005 (Apify default)
- **`dossier-resultaat`:** $0.02 per address successfully geocoded and assembled into a dossier (includes
  the PDOK basis, flood-risk check, and foundation-area check — the core combined value of this actor).
  Failed geocodes are never charged.
- **`cbs-verrijking`:** $0.006 extra, only when `enrichWithCbs` is enabled and CBS data was actually found
  for the matched neighbourhood/municipality.

### Legal

- Data sources: **PDOK Locatieserver** (Kadaster/BZK), **CBS StatLine** (CC-BY), **RWS Richtlijn
  Overstromingsrisico** (public PDOK WMS service), and **RVO Indicatieve aandachtsgebieden
  funderingsproblematiek** (public PDOK WMS service). All public government data, no authentication
  required, no personal data involved (property/area-level only).
- **This is not a valuation, structural survey, or formal risk assessment.** It combines public
  indicators into one convenient lookup and is explicitly disclaimed as such on every single result
  (`_dossier_disclaimer`). It creates no liability for property, rental, insurance, or financing
  decisions — always consult a qualified professional (taxateur, bouwkundig inspecteur, hypotheekadviseur)
  before acting on this data.
- Foundation-risk data is area-level (postcode), not building-specific, and flood-risk data is a
  qualitative zone classification, not a water-depth or probability forecast — both are stated explicitly
  in the output, never silently implied to be more precise than they are.

### FAQ

**Q: Why does `typeFilter` default to `"adres"` instead of matching anything?**
A: PDOK's `/free` endpoint does fuzzy, best-effort matching across all object types. For a query like
"Damrak 1 Amsterdam", it can match the *street* ("weg") instead of the specific address if no type filter
is applied — that loses postcode/neighbourhood data and degrades the whole dossier. Filtering to `adres`
(the default) fixes this; clear the field only if you deliberately want postcode/place-level dossiers.

**Q: Why is there no flood water-depth or probability percentage?**
A: There is no single nationwide PDOK layer that publishes water-depth scenarios per coordinate — that data
exists only fragmented per waterschap/dijkring (e.g. via LIWO), not as one generically queryable service.
This actor only reports the confirmed, nationwide Richtlijn Overstromingsrisico zone classification.

**Q: My address shows `funderingsaandachtsgebied: true` — does that mean this specific house has a bad
foundation?**
A: No. It means the postcode area (PC6) this address falls in has a documented share of pre-1970
buildings, a known risk factor for wooden pile foundations. It is not a per-building assessment — order an
actual funderingsonderzoek before making any decision based on this.

### Related Actors

- **[PDOK Adres Geocoding & Buurtdata](https://apify.com/codeclouds/pdok-locatieserver)** — the same
  official PDOK Locatieserver this actor uses internally, useful for broader geocoding/CBS use cases beyond
  a full property dossier.
- **[NL Bodemrisico Check](https://apify.com/codeclouds/nl-bodemrisico-check)** — a complementary
  due-diligence check: soil contamination (Bodemloket/Wbb) risk on the same kind of address input.

***

*Zoekwoorden: vastgoeddossier, woningdossier, funderingsrisico check, overstromingsrisico per adres,
klimaatrisico vastgoed, taxatie-ondersteuningsrapport, CBS buurtdata woning, due diligence vastgoed.*

### Keywords

vastgoeddossier, property dossier, funderingsrisico, flood risk, foundation risk, netherlands real estate,
due diligence, cbs buurtdata, climate risk property

### Changelog

#### 0.3.0

- Fixed: the within-run WMS lookup cache rounded coordinates to ~11m, which could theoretically merge
  results for two adjacent-but-distinct addresses straddling a flood-risk-zone boundary (e.g. along a
  dike/street). Tightened to ~1.1m — still deduplicates units within the same building, no longer close
  enough to merge different buildings.
- Fixed: if both `includeKlimaat` and `includeFundering` are disabled, `samenvatting.risiconiveau`
  previously showed `"laag"` — indistinguishable from "we checked, nothing found". It now shows
  `"niet gecontroleerd"` when no module was actually checked.
- Added: `samenvatting.omschrijving` — one plain-language sentence summarizing the result, useful when
  exporting to a spreadsheet/CRM where nobody inspects nested JSON fields.

#### 0.2.0

- Fixed: a failed flood-risk/foundation-area WMS lookup was previously indistinguishable from a confirmed
  "no risk found" result. Both modules now report `_status: "onbekend"` and `null` fields on a lookup
  failure, never a silent `false`.
- Fixed: a run no longer fails entirely just because one address (in a single- or multi-address run)
  had no PDOK match — that's a legitimate per-item result (already reflected in `error`), not a system
  failure. Only an empty input, or literally zero processed items, fails the run.
- Added: `samenvatting` block per dossier — a coarse `risiconiveau` (laag/middel/hoog/onbekend) rollup of
  all flagged modules, so a user doesn't need to inspect three nested objects to spot a notable address.
- Added: free `RUN_SUMMARY` written to the run's key-value store (batch totals, flagged counts, average
  pre-1970 percentage, risk-level distribution) — no extra charge.
- Performance: WMS lookups are now deduplicated within a run for addresses sharing the same rounded
  coordinate (e.g. multiple units in one building), reducing redundant external calls.

#### 0.1.0

- Initial release (fase 1 per de gefaseerde bouwspec): PDOK-basis (adres-gefilterd), CBS-buurtverrijking,
  RWS-overstromingsrisicozone en RVO-funderingsaandachtsgebied in één dossier per adres.

# Actor input Schema

## `mode` (type: `string`):

Welke geocoding-modus gebruiken. 'auto' detecteert op basis van input: coördinaten → reverse, lookupId → lookup, anders → forward (adres/postcode/plaatsnaam).

## `query` (type: `string`):

Eén adres, postcode, plaatsnaam of 'lat,lon'-coördinaat voor een los dossier. Voor meerdere adressen in één run: gebruik 'queries' (array) of koppel een dataset.

## `queries` (type: `array`):

Array van adressen/coördinaten/lookup-ID's voor bulk-verwerking (één dossier per item). Elk element is óf een plain string óf een object {"query": string, "lookupId": string, "mode": "auto"|"forward"|"reverse"|"lookup"} — meng beide vormen niet door elkaar. Wordt genegeerd als leeg.

## `lookupId` (type: `string`):

PDOK object-ID (bv. 'adr-8f4d573be765b4c80dd635ba73747903') om een specifiek adres direct op te halen. Alleen relevant bij mode='lookup' of auto-detectie.

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

ID of naam van een bestaand Apify-dataset waaruit adressen gelezen worden (velden 'query'/'address'/'adres', optioneel 'lookupId'/'mode'). Alleen gebruikt als 'query' en 'queries' beide leeg zijn.

## `typeFilter` (type: `string`):

Beperkt forward-zoekopdrachten tot dit PDOK-objecttype. Standaard 'adres' — zonder filter matcht PDOK bij 'Straatnaam 1, Plaats'-achtige queries soms op straatniveau i.p.v. het exacte adres, waardoor postcode/buurtcode ontbreken. Leeg maken staat elk type toe (bv. voor postcode- of plaatsnaam-dossiers).

## `concurrency` (type: `integer`):

Aantal adressen dat tegelijk verwerkt wordt bij bulk-input. Hoger = sneller, maar meer load op de onderliggende overheidsservices. Max 20.

## `enrichWithCbs` (type: `boolean`):

Voeg CBS Kerncijfers Wijken en Buurten (inwoners, inkomen, huishoudens, stedelijkheid) toe. Staat standaard aan — dit is onderdeel van het kern-dossier. Uitzetten bespaart het 'cbs-verrijking'-event bij een resultaat.

## `cbsTableId` (type: `string`):

CBS StatLine-tabel voor de buurtverrijking. Standaard de meest recente 'Kerncijfers Wijken en Buurten'-tabel.

## `cbsFields` (type: `array`):

Zoektermen die dynamisch tegen de kolomtitels van de gekozen CBS-tabel gematcht worden. CBS wijzigt kolomcodes per jaargang, dus matching gebeurt op zoekterm i.p.v. hardcoded kolomcode.

## `includeKlimaat` (type: `boolean`):

Voeg een overstromingsrisico-indicatie toe (RWS Richtlijn Overstromingsrisico, via PDOK WMS): ligt het adres in een risicogebied + kwalitatieve classificatie. Bevat GEEN waterdiepte-scenario's — die bestaan niet als landelijke, generiek bevraagbare laag.

## `includeFundering` (type: `boolean`):

Voeg een funderingsaandachtsgebied-indicatie toe (RVO, via PDOK WMS): percentage panden gebouwd vóór 1970 in het POSTCODEGEBIED van dit adres. Grove, gebiedsgerichte indicatie, geen funderingsonderzoek per pand.

## Actor input object example

```json
{
  "mode": "auto",
  "query": "Damrak 1 Amsterdam",
  "queries": [
    "Damrak 1 Amsterdam",
    "Markt 1, Gouda"
  ],
  "typeFilter": "adres",
  "concurrency": 5,
  "enrichWithCbs": true,
  "cbsTableId": "86165NED",
  "cbsFields": [
    "aantal inwoners",
    "gemiddeld inkomen per inwoner",
    "huishoudens totaal",
    "stedelijkheid"
  ],
  "includeKlimaat": true,
  "includeFundering": true
}
```

# Actor output Schema

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

Alle vastgoeddossiers in het default dataset.

# 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 = {
    "query": "Damrak 1 Amsterdam",
    "queries": [
        "Damrak 1 Amsterdam",
        "Markt 1, Gouda"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("codeclouds/vastgoed-xxl").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 = {
    "query": "Damrak 1 Amsterdam",
    "queries": [
        "Damrak 1 Amsterdam",
        "Markt 1, Gouda",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("codeclouds/vastgoed-xxl").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "Damrak 1 Amsterdam",
  "queries": [
    "Damrak 1 Amsterdam",
    "Markt 1, Gouda"
  ]
}' |
apify call codeclouds/vastgoed-xxl --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=codeclouds/vastgoed-xxl",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/E0AllrtnsfgD5I29A/builds/PZygzfpbRem02Bx13/openapi.json
