# Immoweb & Immobilier Belgique — Appartements & Maisons (MCP) (`amygraphics/belgium-immoweb-scraper`) Actor

Scrape Immoweb, Belgium's #1 property portal (immo belgique, immo belgië): apartments & houses Bruxelles, Antwerp, Vlaanderen, Wallonie — price, surface, EPC rating, GPS, photos + agency e-mail, phone & website. Monitor: only new, changed or removed listings. MCP-ready for AI agents.

- **URL**: https://apify.com/amygraphics/belgium-immoweb-scraper.md
- **Developed by:** [Zakaria el bouami](https://apify.com/amygraphics) (community)
- **Categories:** Real estate, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Immoweb & Belgium Real Estate Scraper — Immo Belgique 🇧🇪🏠 (MCP)

Scrape **Immoweb**, Belgium's #1 property portal — **≈51 000 live listings**: apartments, houses, villas, offices and land, **for sale and for rent**, across all 43 Belgian districts. The data comes from Immoweb's own public JSON search endpoint (undocumented, no browser, no proxy, no login).

This Actor does one job: turn Immoweb into a **clean, deduplicated, contact-rich feed of Belgian property** — and, when scheduled, into an **automatic watch service that reports only what is new, what changed, and what is gone**.

### Three hard problems, solved for you

**1. The ~10 000-result API cap.** Immoweb returns 30 listings per page and cuts every single query at page 333. A naive scraper silently loses 80 % of the Belgian market. This Actor detects the cap and **automatically splits the market by district** (43 district slugs, all verified against the live API) — so "every apartment for sale in Belgium" is actually complete, not just the first 10 000.

**2. Agency contacts — the data nobody else gives you.** The search grid does not expose them. With `fetchDetails` the Actor opens each listing page and extracts the **agency e-mail, phone, mobile and website**, plus the full description, EPC reference, heating type and lift / pool / flood-zone flags. Perfect for agencies, investors and lead generation.

**3. Change detection that does not lie.** `monitor` mode stores every listing it has seen (price + last modification + title fingerprint) in the Apify key-value store and returns only:

- **`isNew: true`** — a listing never seen before,
- **`isUpdated: true`** — price change or edit of a listing you already had,
- **`changeType: "closed"`** — a listing that **disappeared from the portal** (sold, rented, withdrawn), returned once and then forgotten.

Closed-detection runs **only when the scan was complete and error-free** — it never guesses. Price drops are the money signal: `oldPrice` + `price` give you the drop directly.

### What you get

| Field | Example |
|---|---|
| `immowebId` / `immowebUrl` | 21895839 / direct link |
| `title` / `propertyType` / `propertySubtype` | "…charming house" / HOUSE / HOUSE |
| `transactionType` / `price` / `oldPrice` / `priceDisplay` | FOR_SALE / 650000 / … / €650,000 |
| `bedrooms` / `rooms` / `livingSurface` / `landSurface` | 4 / 4 / 168 m² / 41 m² |
| `locality` / `postalCode` / `street` / `houseNumber` / `latitude` / `longitude` | Watermael-Boitsfort / 1170 / RUE DE LA VÉNERIE / 21 / GPS |
| `province` / `region` / `district` / `country` | Brussels / Brussels / Brussels / Belgium |
| `epcLabel` / `epcReference` / `heatingType` | G / 20260701-… / gas |
| `agencyName` / `agencyLogo` | VICTOIRE JUNOT - Uccle Vente |
| **`agencyEmail` / `agencyPhone` / `agencyMobile` / `agencyWebsite`** | venteuccle@apilead.be / +32 2 375 10 10 / … / victoire.be |
| `description` / `hasLift` / `hasSwimmingPool` / `floodZoneType` | full text / false / false / flood zone |
| `flag` / `hasVirtualTour` / `has360Tour` / `hasVideo` / `pictures` | new / … / up to 15 hi-res URLs |
| `lastModificationDate` | 2026-10-07T09:30:47Z |
| `isNew` / `isUpdated` / `changeType` / `firstSeenAt` / `closedAt` | monitor flags |

### Filters

Property type (house / apartment / villa / office / land) · sale or rent · **43 districts** (brussels, antwerp, gent, liege, brugge, charleroi, leuven, namur, hasselt, kortrijk, oostende…) · price range (`minPrice` / `maxPrice`) · site language (EN / FR / NL — Belgium is bilingual).

### Automation recipes (no code required)

- **Daily market watch** — save as a **task** with a daily **schedule**, `mode: monitor` + `onlyChanges`: every run returns only new, price-changed and removed listings. A Brussels-wide watch costs a few cents a day.
- **One task per search** — "apartments Brussels under €400k", "houses Gent for rent": each task keeps its **own watch history** (state key derived from the scope).
- **Price-drop hunting** — watch a district with `monitor` and read `oldPrice` vs `price`.
- **Push it anywhere** — add a **webhook** (Apify Console → Integrations) to send each run to Slack, Google Sheets, n8n, Make or Zapier, or to your own CRM for lead follow-up.

### Use from AI agents (MCP)

This Actor is exposed as a tool through **Apify's MCP server** — any MCP client (Claude, Cursor, VS Code / Copilot, Codex, custom agents) can discover and run it without code:

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=amygraphics/belgium-immoweb-scraper" }
  }
}
```

Then simply ask: *"Use amygraphics/belgium-immoweb-scraper to list apartments for sale in Brussels under €400 000 with the agency e-mail."*

Agents can also pay autonomously — this Actor is **whitelisted for agentic payments** (Direct x402 / Apify AGI prepaid token), so a run does not require an Apify account.

### Pricing

Pay-per-event: a small start fee plus **$4 per 1 000 listings** extracted. In monitor mode you only pay for listings that are new, changed or closed — a daily watch costs a few cents.

### Notes & limitations

- Source: Immoweb's public search JSON + listing pages. Data reused as published.
- Immoweb caps each single query at ~10 000 results (30/page × 333 pages); the automatic district split removes that limit, at the cost of more requests.
- District slugs are validated against a verified list of 43 Belgian districts; unknown slugs are ignored with a warning (the Actor never guesses).
- Some listings are sold by private owners — then `agencyEmail`/`agencyPhone` are empty, which is itself a useful signal.

***

**Français —** Scrapez **Immoweb**, le portail immobilier n°1 en Belgique (≈51 000 annonces : appartements, maisons, villas, bureaux, terrains — vente et location, 43 arrondissements). Le plafond de ~10 000 résultats par requête est **contourné automatiquement** par découpage par arrondissement ; l'enrichissement `fetchDetails` ajoute **e-mail, téléphone et site de l'agence**, description complète, référence PEB, chauffage, ascenseur/piscine/zone inondable. Le mode **monitor** ne renvoie que les annonces nouvelles, modifiées (baisse de prix) ou **disparues** (`changeType: "closed"`). Tarif : **4 $ les 1 000 annonces**, démarrage $0.001.

**Agents IA (MCP) :** cet Actor est exposé comme outil du serveur MCP d'Apify — branchez `https://mcp.apify.com?tools=amygraphics/belgium-immoweb-scraper` dans Claude, Cursor, VS Code ou Codex. Les agents peuvent payer seuls (x402 / AGI) : l'Actor est *whitelisted* pour les paiements agentiques.

# Actor input Schema

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

search = return every matching listing. monitor = compare with the previous run (Apify key-value store) and return only what is new, changed or gone — ideal for a daily schedule.

## `propertyTypes` (type: `array`):

Which categories to scan. Each selected type is scanned separately. 'house' + 'apartment' = the whole residential market.

## `transactions` (type: `array`):

Sale, rent, or both.

## `districts` (type: `array`):

Verified district slugs, one per item. Examples: brussels, antwerp, gent, liege, brugge, charleroi, leuven, namur, hasselt. Leave empty to scan all of Belgium; if the country scan would hit Immoweb's ~10 000-result cap, the Actor automatically splits it by district so nothing is lost.

## `autoSplit` (type: `boolean`):

Recommended. When a country-wide scan returns the API cap (~10 000 results), the Actor re-runs the scan district by district (43 verified districts) and merges everything.

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

Optional. Only listings at or above this price.

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

Optional. Only listings at or below this price.

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

Language used for the listing pages (titles, descriptions). Belgium is bilingual: choose French or Dutch for local listings.

## `maxResults` (type: `integer`):

Global cap on rows returned (and charged). The API caps each single query at about 10 000 results; auto-split goes beyond.

## `fetchDetails` (type: `boolean`):

Opens every listing page and adds: agency e-mail, phone, mobile, website, full description, EPC reference, heating type, lift / pool / flood-zone flags. This is the data competitors do not give you. Roughly doubles the run time.

## `includePictures` (type: `boolean`):

Adds up to 15 high-resolution picture URLs per listing.

## `onlyChanges` (type: `boolean`):

Monitor mode only. Unchanged listings are not returned and not charged.

## `detectClosed` (type: `boolean`):

Monitor mode only. Listings that disappeared from the portal are returned once with changeType='closed', then forgotten. Runs only when the scan was complete and error-free.

## `stateKey` (type: `string`):

Leave empty: the state key is derived from your search scope, so each different search keeps its own history.

## Actor input object example

```json
{
  "mode": "search",
  "propertyTypes": [
    "apartment"
  ],
  "transactions": [
    "for-sale"
  ],
  "districts": [
    "brussels",
    "antwerp"
  ],
  "autoSplit": true,
  "minPrice": null,
  "maxPrice": null,
  "language": "en",
  "maxResults": 20,
  "fetchDetails": true,
  "includePictures": true,
  "onlyChanges": true,
  "detectClosed": true,
  "stateKey": ""
}
```

# Actor output Schema

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

No description

# 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 = {
    "mode": "search",
    "propertyTypes": [
        "apartment"
    ],
    "transactions": [
        "for-sale"
    ],
    "districts": [
        "brussels"
    ],
    "autoSplit": true,
    "language": "en",
    "maxResults": 20,
    "fetchDetails": false,
    "includePictures": true,
    "onlyChanges": true,
    "detectClosed": true,
    "stateKey": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("amygraphics/belgium-immoweb-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 = {
    "mode": "search",
    "propertyTypes": ["apartment"],
    "transactions": ["for-sale"],
    "districts": ["brussels"],
    "autoSplit": True,
    "language": "en",
    "maxResults": 20,
    "fetchDetails": False,
    "includePictures": True,
    "onlyChanges": True,
    "detectClosed": True,
    "stateKey": "",
}

# Run the Actor and wait for it to finish
run = client.actor("amygraphics/belgium-immoweb-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 '{
  "mode": "search",
  "propertyTypes": [
    "apartment"
  ],
  "transactions": [
    "for-sale"
  ],
  "districts": [
    "brussels"
  ],
  "autoSplit": true,
  "language": "en",
  "maxResults": 20,
  "fetchDetails": false,
  "includePictures": true,
  "onlyChanges": true,
  "detectClosed": true,
  "stateKey": ""
}' |
apify call amygraphics/belgium-immoweb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amygraphics/belgium-immoweb-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/ygTILvlbPZWAxVuEc/builds/2K9UD0Ish5IoQe5OR/openapi.json
