# Immoweb Scraper (`automation-lab/immoweb-belgium-property-listings-scraper`) Actor

Extract public Immoweb Belgium sale and rental listings with prices, property attributes, locations, images, dates, and advertiser context for monitoring.

- **URL**: https://apify.com/automation-lab/immoweb-belgium-property-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Immoweb Scraper

Export public Immoweb Belgium sale and rental listings from the search URLs you already use. The Actor follows those filters, opens each listing, and returns stable IDs, prices, property attributes, location, images, publication dates, and publicly displayed advertiser context.

Use the resulting dataset for one-time comparable-property research or schedule the same input to monitor Belgium inventory and asking-price changes. English, French, and Dutch Immoweb search and classified URLs are supported.

### What does the Immoweb Scraper do?

The Actor turns an Immoweb search into structured records:

1. It validates every supplied URL as an Immoweb search or classified URL.
2. It preserves the location, transaction, property-type, price, and other filters encoded in that URL.
3. It follows ordinary Immoweb result pagination.
4. It bootstraps a coherent Belgium browser session and reuses it for lightweight detail requests, with browser navigation as a fallback.
5. It normalizes the embedded listing data and deduplicates records by Immoweb ID.
6. It stores accepted records in the run's default dataset.

You can also supply a classified URL when you need one specific property rather than a whole search.

### Who is this Actor for?

- **Property investors** comparing asking prices and available inventory.
- **Brokers and agencies** tracking new supply in a territory.
- **Market analysts** building repeatable Belgian property datasets.
- **Proptech teams** enriching internal search, valuation, or inventory pipelines.
- **Researchers** comparing sale and rental characteristics by municipality.

The Actor extracts public records. It does not log in, send contact forms, estimate values, or determine whether an asking price is fair.

### Why use it for Immoweb Belgium data?

- Supply the same filtered URLs that work in your browser.
- Receive stable `listingId` values for change detection and joins.
- Get detail-page attributes instead of only summary-card text.
- Collect sale and rental records in one consistent output shape.
- Keep source provenance in `sourceUrl` for multi-search runs.
- Use bounded pagination and `maxItems` to control scope.
- Run with a Belgium residential session by default for cloud reliability.
- Block non-document browser assets and use HTTP-first detail fetches while retaining image URLs in output.

### What Immoweb data can I extract?

| Group | Example fields |
| --- | --- |
| Identity | `listingId`, `url`, `sourceUrl` |
| Transaction | `transactionType`, `price`, `priceDisplay`, `oldPrice`, `currency` |
| Property | `propertyType`, `propertySubtype`, `title`, `description` |
| Dimensions | `bedrooms`, `bathrooms`, `habitableSurface`, `landSurface` |
| Features | construction year, condition, furnishing, garden, terrace, parking |
| Energy | `energyScore`, `energyConsumption` |
| Location | locality, postal code, street, province, district, coordinates |
| Media | `imageUrls` |
| Advertiser | public name, type, phone, and website when displayed |
| Timeline | publication, modification, availability, and scrape timestamps |
| Engagement | public view and bookmark counts when available |

Fields are omitted when Immoweb does not publish them for a particular listing. Exact addresses or advertiser contacts may therefore be absent.

### Getting started

1. Open Immoweb and apply the property filters you need.
2. Copy the resulting search URL.
3. Create a Task from this Actor.
4. Paste the URL into **Immoweb search or classified URLs**.
5. Set **Maximum listings** to the number of records you need.
6. Keep the default Belgium residential proxy unless you have verified direct access.
7. Start the run and open the **Listings** dataset when it finishes.
8. Export JSON, CSV, Excel, XML, or RSS from Apify, or consume the dataset API.

A useful first input is:

```json
{
  "startUrls": [
    {
      "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest"
    }
  ],
  "maxItems": 25
}
```

### Input parameters

#### `startUrls`

Required array of Immoweb URLs. Accepted paths are:

- `https://www.immoweb.be/en/search/...`
- `https://www.immoweb.be/fr/search/...`
- `https://www.immoweb.be/nl/search/...`
- public classified URLs under the same three language prefixes.

URLs on other hosts and unrelated Immoweb pages fail validation rather than being fetched.

#### `maxItems`

Maximum unique listings stored across all start URLs. The default is 25 and the supported range is 1–1,000. The Actor stops scheduling details when this limit is reached.

#### `maxPagesPerSearch`

Safety limit for each search URL. The default is 20 and the supported range is 1–333. Natural result exhaustion or `maxItems` can stop pagination earlier.

#### `proxyConfiguration`

The default uses Apify residential proxy traffic with Belgium geography. A single proxy identity and browser context are kept together for the run. Custom proxy URLs and explicit proxy disabling are supported through the standard proxy editor.

Direct mode can be cheaper, but it depends on the network running the Actor. A challenged page is treated as an error, never as a valid empty result.

### Output example

A representative record has this shape (values shortened for readability):

```json
{
  "listingId": "21760024",
  "url": "https://www.immoweb.be/en/classified/house/for-sale/bruxelles/1000/21760024",
  "sourceUrl": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest",
  "transactionType": "FOR_SALE",
  "propertyType": "HOUSE",
  "propertySubtype": "HOUSE",
  "title": "Single-family house near the European district",
  "price": 630000,
  "priceDisplay": "€630,000",
  "currency": "EUR",
  "bedrooms": 5,
  "bathrooms": 4,
  "habitableSurface": 190,
  "hasGarden": true,
  "gardenSurface": 20,
  "energyScore": "E",
  "locality": "BRUXELLES",
  "postalCode": "1000",
  "province": "Brussels",
  "imageUrls": [
    "https://media-resize.immowebstatic.be/classifieds/.../736x736/photo.jpg"
  ],
  "advertiserType": "AGENCY",
  "publicationDate": "2026-08-11T12:00:00.000Z",
  "scrapedAt": "2026-08-12T06:30:00.000Z"
}
```

The dataset schema contains the complete field list and an overview table for the most useful columns.

### How much does it cost to extract Immoweb property listings?

Pricing uses one `start` event per run plus one `item` event for each listing saved. The per-listing event has volume tiers, so the active Apify pricing panel is the source of truth for your account tier.

At the initial BRONZE reference price of **$0.006 per run plus $0.002 per listing**:

| Listings | Calculated BRONZE event total (USD) |
| ---: | ---: |
| 1 | 0.008 |
| 10 | 0.026 |
| 25 | 0.056 |
| 100 | 0.206 |

Apify platform compute and proxy usage are handled inside the Actor run; customers are billed through the active pay-per-event configuration. A failed detail fetch is not charged as an item.

### Monitor new listings and price changes

The Actor returns source identifiers and timestamps that work well in a scheduled workflow:

1. Save a filtered Immoweb URL in an Apify Task.
2. Schedule it daily or weekly.
3. Export each dataset to your warehouse or spreadsheet.
4. Join records on `listingId`.
5. Compare `price`, `oldPrice`, `lastModifiedDate`, and presence between runs.

The Actor does not itself persist a historical database or emit alerts. Apify schedules, webhooks, dataset integrations, and your downstream system provide those workflow steps.

### Use multiple search URLs

One run can combine cities, transaction types, or property categories:

```json
{
  "startUrls": [
    { "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&orderBy=newest" },
    { "url": "https://www.immoweb.be/en/search/apartment/for-rent?countries=BE&postalCodes=2000&orderBy=newest" }
  ],
  "maxItems": 100,
  "maxPagesPerSearch": 5
}
```

`sourceUrl` records which input produced each item. If the same listing occurs more than once, the Actor stores it once per run.

### Export to spreadsheets and data pipelines

From the dataset you can:

- download CSV or Excel for manual analysis;
- connect Google Sheets through Apify integrations;
- send the dataset ID through a webhook;
- load JSON into a warehouse or BI tool;
- use Make, Zapier, n8n, or custom code after each scheduled run;
- compare current and previous datasets by `listingId`.

For large recurring jobs, keep filtered searches narrow and use a deliberate schedule rather than repeatedly extracting the full Belgian market.

### Run with the Apify API using cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~immoweb-belgium-property-listings-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [{"url":"https://www.immoweb.be/en/search/apartment/for-rent?countries=BE&postalCodes=1000&orderBy=newest"}],
    "maxItems": 25
  }'
```

Keep tokens in environment variables or secret stores, not source code.

### Run with JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/immoweb-belgium-property-listings-scraper').call({
  startUrls: [
    { url: 'https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&orderBy=newest' },
  ],
  maxItems: 25,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Run with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("automation-lab/immoweb-belgium-property-listings-scraper").call(
    run_input={
        "startUrls": [
            {
                "url": "https://www.immoweb.be/en/search/apartment/for-sale?countries=BE&postalCodes=2000&orderBy=newest"
            }
        ],
        "maxItems": 25,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use this Actor through MCP

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/immoweb-belgium-property-listings-scraper"
```

#### Claude Desktop, Cursor, and VS Code MCP setup

Claude Desktop, Cursor, and VS Code can use this equivalent HTTP MCP configuration:

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

Example prompts:

- “Run the Immoweb scraper for this Brussels sale URL and summarize median asking price per square metre.”
- “Collect the latest Antwerp apartment listings and save the dataset link.”
- “Run my Brussels rental Task and identify listing IDs that are new compared with yesterday’s exported dataset.”

### Tips for reliable runs

- Use a normal filtered Immoweb search URL copied from your browser.
- Prefer a city or property segment over a country-wide unfiltered search.
- Keep the default residential proxy for Apify cloud runs.
- Start with 10–25 records when validating a new filter.
- Increase `maxPagesPerSearch` only when the item limit requires it.
- Use stable listing IDs rather than titles to compare runs.
- Expect optional fields to vary by property type and advertiser.

### Limits and failure behavior

Immoweb can change its page structure or anti-bot policy. The Actor detects blocked/challenge pages and missing embedded listing data. It fails clearly instead of silently returning an empty successful run.

Listings can expire between search discovery and detail extraction. Very broad searches are capped by the configured page and item limits. Results represent public data available during the run, not a complete historical record.

Phone numbers, exact addresses, coordinates, statistics, and energy fields are included only when publicly displayed. The Actor does not reveal gated contact details or bypass account access.

### Responsible use and legality

You are responsible for complying with Immoweb’s terms, applicable laws, contractual restrictions, and data-protection requirements. Collect only data you have a lawful purpose to process.

Avoid excessive schedules, republishing copyrighted descriptions or images, unsolicited contact, discriminatory housing decisions, and attempts to identify private individuals. Public availability does not remove intellectual-property or privacy obligations. Use listing and advertiser data proportionately and secure your exports.

### Frequently asked questions

#### Can I scrape both sale and rental listings?

Yes. Supply the corresponding Immoweb search URLs. The transaction type and applicable sale or rental price are normalized into the same record shape.

#### Can I use French or Dutch Immoweb URLs?

Yes. `/fr/search`, `/nl/search`, and their public classified paths are accepted along with English URLs.

#### Can I scrape one property URL?

Yes. Supply a public classified URL in `startUrls`. It produces one record unless the same listing has already been seen in that run.

#### Why did the run fail with a challenge message?

Confirm that the default Belgium residential proxy is enabled. Custom datacenter proxies are frequently blocked. A fresh rerun creates a new session; repeated failures may indicate a source change that needs maintenance.

#### Why is a field missing?

Immoweb does not publish every attribute for every property type. Missing optional values are omitted rather than guessed.

#### Does the Actor track history automatically?

No. Schedule an Apify Task and compare datasets by `listingId` in your preferred destination. This keeps history and alert logic under your control.

#### Does it contact advertisers?

No. It only exports public listing and advertiser context. It does not submit forms, make calls, or send messages.

### Related property data Actors

For multi-market European research, combine this Actor with other `automation-lab` property tools:

- [ImmoScout24 Property Listings Scraper](https://apify.com/automation-lab/immoscout24-property-scraper)
- [SeLoger Property Listings Scraper](https://apify.com/automation-lab/seloger-property-listings-scraper)
- [Fotocasa Property Listings Scraper](https://apify.com/automation-lab/fotocasa-property-listings-scraper)
- [Spitogatos Property Listings Scraper](https://apify.com/automation-lab/spitogatos-property-scraper)

Use each source-specific Actor independently, then normalize records downstream around source ID, location, transaction type, price, and scrape timestamp.

# Actor input Schema

## `startUrls` (type: `array`):

Immoweb URLs in English, French, or Dutch. Search URLs are paginated; classified URLs extract one specific property.

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

Maximum number of unique property records saved across all URLs.

## `maxPagesPerSearch` (type: `integer`):

Safety limit for pagination of each Immoweb search URL. The Actor may stop earlier after reaching the listing limit.

## `proxyConfiguration` (type: `object`):

Belgium residential Apify Proxy is used by default to handle Immoweb protection. Keep one proxy identity per run for a coherent browser session. Disable proxying only when your network can open Immoweb directly.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest"
    }
  ],
  "maxItems": 20,
  "maxPagesPerSearch": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `dataset` (type: `string`):

Normalized sale and rental property records.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/immoweb-belgium-property-listings-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 = {
    "startUrls": [{ "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest" }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/immoweb-belgium-property-listings-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 '{
  "startUrls": [
    {
      "url": "https://www.immoweb.be/en/search/house/for-sale?countries=BE&postalCodes=1000&page=1&orderBy=newest"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/immoweb-belgium-property-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/immoweb-belgium-property-listings-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/m05f18VYAwn9LmTzt/builds/Es5NkybYKLiP11uKM/openapi.json
