# Funda Scraper: Netherlands Property Listings (`reventadata/funda-scraper`) Actor

Scrape funda.nl property listings: homes for sale and for rent by area, with price, address, living area, rooms, energy label, agency and photos. Filter by property type and price. Optional coordinates, description and features. Export JSON, CSV or Excel. Unofficial.

- **URL**: https://apify.com/reventadata/funda-scraper.md
- **Developed by:** [reventadata](https://apify.com/reventadata) (community)
- **Categories:** Real estate, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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

## Funda Scraper

> **Unofficial.** This Actor is not affiliated with, authorised by, endorsed by, or in any
> way officially connected to Funda. "Funda" is a trademark of its respective owner and is
> used here only to describe what this tool reads. It reads the same public pages a browser does.

Funda Scraper extracts Dutch property listings from funda.nl: homes for sale and for rent, by area, with
price, address, living area, rooms, energy label, agency and photos, and optionally coordinates, the full
description and the feature table.

### What does it do?

Runs the same searches as the funda.nl search page (buy or rent, one or more areas, house or apartment, a price range,
sort order) and returns one structured row per listing, 15 listings per page. Optionally it opens each listing's own
page for latitude, longitude, the Dutch description and the feature table.

### Why use it?

- **Structured columns:** price, address parts (street, number, postal code, city, district, neighbourhood), living and plot area, rooms, bedrooms, energy label, publication time, open-house windows, photo links.
- **Agency data:** agency name, profile link, association, and the named broker where Funda shows one (public business data).
- **Several areas in one run**, deduplicated by listing id.
- **Honest limits:** Funda serves at most 9,990 results (666 pages of 15) for any one search. When a search is larger, the run says so in the log; add a price range, a property type or a smaller area to reach the rest.

### How to use it

1. Choose buy or rent and list the areas as they appear in a funda.nl search URL (`selected_area`), for example `amsterdam` or `utrecht`. Leave the list empty to search the whole Netherlands.
2. Optionally set property type, a maximum price (with a minimum price, sent to Funda as the price range), sort order, and living-area or bedroom minimums.
3. Set `maxItems` (across all areas) and `maxPages` (per area). A result page took 18 to 51 seconds in our measurements, because Funda blocks plain HTTP clients and the pages are fetched through an unblocking service.
4. Run the Actor and download the dataset as JSON, CSV, Excel or NDJSON.

#### Input

| Field | Type | Description |
|---|---|---|
| `offering` | string | `sale` or `rent`. |
| `areas` | array | Funda area slugs, one search per area. Empty = whole Netherlands. |
| `objectType` | string | `house`, `apartment`, or empty for every type. |
| `sort` | string | `relevance` or `newest`. |
| `maxPrice`, `minPrice` | integer | Price range in EUR (monthly rent for rent). `maxPrice` is sent to Funda; `minPrice` alone is applied after fetching. |
| `minLivingAreaM2`, `maxLivingAreaM2`, `minBedrooms` | integer | Applied after fetching. |
| `includeDetails` | boolean | One extra request per row for coordinates, description and features. Each detail page used in a returned row is charged as a separate `detail-page` event. |
| `maxItems` | integer | Cap on rows. |
| `maxPages` | integer | Cap on pages per area (15 listings each, at most 666). |

#### Sample input

```json
{
    "offering": "rent",
    "areas": ["utrecht"],
    "maxPrice": 1500,
    "sort": "newest",
    "maxItems": 3,
    "maxPages": 1,
    "includeDetails": true
}
```

#### Output

Each dataset item contains the fields below (see the dataset schema for types). The detail fields are `null` unless `includeDetails` is on.

| Field | Description |
|---|---|
| `id`, `url`, `listingType` | Funda listing id, link, `sale` or `rent`. |
| `price`, `priceType`, `priceCondition`, `currency` | Asking price (monthly rent for rent) and how Funda labels it (for example `kosten_koper` on sale, `per_month` on rent). |
| `propertyType`, `constructionType`, `status` | As published: `apartment`, `house`, `parking`, ...; `resale` / `newly_built`; availability. |
| `street`, `houseNumber`, `houseNumberSuffix`, `postalCode`, `city`, `municipality`, `province`, `district`, `neighbourhood` | Address. |
| `livingAreaM2`, `plotAreaM2`, `rooms`, `bedrooms`, `energyLabel` | Size and energy label. |
| `publishedAt`, `promoText`, `imageUrls`, `openHouses` | Publication time, agency promo line, photo links, open-house windows. |
| `agencyName`, `agencyUrl`, `agencyId`, `agencyAssociation`, `brokerName` | Listing agency and named broker. |
| `searchUrl` | First-page search URL the row came from. |
| `latitude`, `longitude`, `description`, `features` | Only with `includeDetails`. |

#### Sample output

A real row from a run (rent, Utrecht, with details). Shortened here: `description` cut, `features` cut to two entries, and `municipality`, `province`, `imageUrls`, `agencyId`, `searchUrl` and the empty fields left out:

```json
{
    "id": "8218793",
    "url": "https://www.funda.nl/detail/huur/utrecht/appartement-tussenbusstraat-88/44663156/",
    "listingType": "rent",
    "price": 986,
    "priceType": "regular",
    "priceCondition": "per_month",
    "currency": "EUR",
    "propertyType": "apartment",
    "constructionType": "resale",
    "status": "none",
    "street": "Tussenbusstraat",
    "houseNumber": "88",
    "postalCode": "3534BM",
    "city": "Utrecht",
    "district": "Wijk 01 West",
    "neighbourhood": "Schepenbuurt, Cartesiusweg e.o.",
    "livingAreaM2": 55,
    "rooms": 2,
    "bedrooms": 1,
    "energyLabel": "unknown",
    "publishedAt": "2026-11-05T16:35:42.5198789+01:00",
    "agencyName": "MVGM Wonen",
    "agencyUrl": "https://www.funda.nl/makelaar/70043-mvgm-wonen/",
    "agencyAssociation": "VGM",
    "latitude": 52.10169,
    "longitude": 5.090589,
    "description": "Modern appartement aan de Tussenbusstraat 90 in Utrecht ...",
    "features": {"Aangeboden sinds": "November 5, 2026", "Huurprijs": "€ 986 per maand (servicekosten onbekend)"}
}
```

### Pricing

Detail pages (`includeDetails`) are charged as a separate event, one per detail page used in a returned row; a detail page that cannot be read is not charged. For current pricing and details, see the Store page.

### Privacy & Compliance

This Actor reads only publicly accessible pages and returns the public data they show, including agency names and
the broker named on a listing. It sends no login. Funda's terms of use restrict automated collection; check that your use
of the data complies with them and with applicable law, including data-protection rules for any personal data.

# Actor input Schema

## `offering` (type: `string`):

Homes for sale (funda /zoeken/koop) or for rent (funda /zoeken/huur).

## `areas` (type: `array`):

Funda area slugs, one search per area, as they appear in selected_area of a funda.nl search URL, for example "amsterdam" or "utrecht". Leave empty to search the whole Netherlands (more than 100,000 homes; funda serves at most 9,990 results per search, so add a price range or object type). An unknown slug returns no results.

## `objectType` (type: `string`):

Only houses or only apartments. Leave empty for every type (the site also lists parking spaces, storage and plots).

## `sort` (type: `string`):

Funda's default relevance order, or newest listings first.

## `maxPrice` (type: `integer`):

Optional. Sent to funda as the price range, together with the minimum price when one is set. On a rent search this is the monthly rent.

## `minPrice` (type: `integer`):

Optional. Rows below this price, or with no price, are dropped. Funda is asked for the range only when a maximum price is set too, so on its own this filter is applied after fetching and does not reduce the number of pages fetched.

## `minLivingAreaM2` (type: `integer`):

Optional. Rows with a smaller living area, or none, are dropped after fetching.

## `maxLivingAreaM2` (type: `integer`):

Optional. Rows with a larger living area, or none, are dropped after fetching.

## `minBedrooms` (type: `integer`):

Optional. Rows with fewer bedrooms, or no bedroom count, are dropped after fetching.

## `includeDetails` (type: `boolean`):

Open each listing's own page for latitude, longitude, the full description and the feature table. This is one extra unblocking request per row, so a run takes about as many extra requests as it has rows and is much slower. Each detail page used in a returned row is charged as a separate pay-per-event "detail-page" event (see the Store page for pricing); a detail page that cannot be read is not charged.

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

Hard cap on rows pushed across all areas.

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

Cap on result pages fetched per area (15 listings each, at most 666 pages). Each page is one paid unblocking request on the Actor's side and takes about 18 to 51 seconds (measured). The run also stops at the last page the site reports.

## Actor input object example

```json
{
  "offering": "sale",
  "areas": [
    "amsterdam"
  ],
  "objectType": "",
  "sort": "relevance",
  "includeDetails": false,
  "maxItems": 15,
  "maxPages": 1
}
```

# Actor output Schema

## `listings` (type: `string`):

Property listings: price, address, living area, rooms, energy label, agency and a link to the ad.

# 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 = {
    "areas": [
        "amsterdam"
    ],
    "maxItems": 15,
    "maxPages": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("reventadata/funda-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 = {
    "areas": ["amsterdam"],
    "maxItems": 15,
    "maxPages": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("reventadata/funda-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 '{
  "areas": [
    "amsterdam"
  ],
  "maxItems": 15,
  "maxPages": 1
}' |
apify call reventadata/funda-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reventadata/funda-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/6EtPtlXfmPJz2SP65/builds/Do83jqXmmKj6bfyUi/openapi.json
