# Immowelt, willhaben & Kleinanzeigen New Listing Monitor (`mocha_tassel/real-estate-listing-monitor`) Actor

Tracks German and Austrian property portals and returns only listings that appeared since your last run. Schedule it daily and get webhook alerts instead of the same list over and over.

- **URL**: https://apify.com/mocha\_tassel/real-estate-listing-monitor.md
- **Developed by:** [Niko T.](https://apify.com/mocha_tassel) (community)
- **Categories:** Real estate, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 new listings

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?

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

## Immowelt, willhaben & Kleinanzeigen — New Listing Monitor

![real-estate-listing-monitor](https://api.apify.com/v2/key-value-stores/yfnvtbimUC1yUnStx/records/real-estate-listing-monitor.png?signature=1Yc1jhnzpDWkXQTfvUMOO)

Most property scrapers hand you the same list every time you run them. This one remembers what it has already seen and returns **only listings that appeared since your last run**.

Point it at a search, put it on a daily or hourly schedule, and you get the new arrivals — nothing else. In hot markets the good properties are gone within hours, so being first to see them is the whole game.

### Supported portals

| Portal | Country | Notes |
| --- | --- | --- |
| Immowelt | Germany | All German cities, resolved automatically from the city name |
| willhaben | Austria | Towns and federal states; smaller municipalities matched to their state |
| Kleinanzeigen | Germany | The place where private, commission-free offers appear |

### What you get

Every new listing comes back as a normalised record, identical in shape across all three portals:

```json
{
  "id": "immowelt:15872866-cd7d-473d-903d-f8f3a39878b1",
  "portal": "immowelt",
  "url": "https://www.immowelt.de/expose/15872866-cd7d-473d-903d-f8f3a39878b1",
  "title": "Maisonette-Wohnung mit Dachterrasse",
  "price": 289000,
  "currency": "EUR",
  "livingArea": 88.6,
  "rooms": 3,
  "pricePerSqm": 3261.85,
  "postalCode": "04177",
  "city": "Leipzig",
  "district": "Lindenau",
  "floor": "3. Geschoss",
  "energyClass": "C",
  "imageUrl": "https://mms.immowelt.de/example/photo.jpg",
  "images": ["https://mms.immowelt.de/example/photo.jpg"],
  "isPrivate": false,
  "advertiser": { "name": "Beispiel Immobilien GmbH" },
  "isNew": true,
  "detectedAt": "2026-08-10T15:19:04.812Z"
}
```

Because the shape is the same everywhere, you can watch Germany and Austria side by side and treat the results as one stream. The Output tab shows a **photo thumbnail** for every listing (`imageUrl`), not just a wall of numbers.

### Getting alerts

Set **Webhook for alerts** to any HTTPS endpoint and each run with new findings sends a JSON POST containing the new listings. This works with Zapier, Make, n8n, Slack workflows, or your own service — no polling required on your side.

You can also use Apify's built-in integrations to push results into Google Sheets, Slack, or a database.

### How to set it up

1. Choose a portal and a location, or paste a **Search URL** copied straight from the portal. The URL route supports every filter the portal offers, including ones not exposed as fields here.
2. Optionally narrow things down with price, area and room filters.
3. Set a **Monitor name** if you want to watch several searches in parallel — each name keeps its own history.
4. Run it once to record the baseline, then put it on a schedule.

The first run returns the current stock so you can see what the data looks like; every run after that returns only genuinely new listings. If you are wiring up the webhook straight away and do not want a first alert containing hundreds of existing listings, turn **Return everything on the first run** off — then the first run stays silent and only records the baseline.

### Pricing

You pay a small fee per listing checked, plus a larger fee for each genuinely new listing returned. A run that finds nothing therefore costs very little.

This split exists because the work happens whether or not there is news: to find the three new apartments in Leipzig this morning, the Actor still has to look at the current stock.

### Proxies

German and Austrian property portals reject requests coming from data centres with HTTP 403. The Actor therefore uses Apify residential proxies from the matching country by default, and you can supply your own instead. Residential traffic is the main cost driver of every run, which is why the **Listings to check per run** setting matters — keep it just high enough to cover a day's new arrivals.

### Limitations

- Data comes from public search result pages. Fields the portal does not show in the results list (full description, all photos, contact phone numbers) are not included.
- Kleinanzeigen needs a location slug and code from the URL, because it has no name-based lookup.
- Portals change their markup from time to time. If a run reports that no result cards were found, that is the signal — please report it and it will be fixed.

### Legal note

The Actor reads publicly accessible listing pages. It does not log in, does not bypass paywalls, and does not collect personal data of private individuals beyond what the portal itself shows publicly. You are responsible for how you use the results, in particular under the GDPR when contacting private sellers.

***

### Auf Deutsch

Dieser Actor überwacht Immowelt, willhaben und Kleinanzeigen und liefert bei jedem Lauf **ausschließlich die Inserate, die seit dem letzten Lauf neu erschienen sind**. Er merkt sich zwischen den Läufen, was er schon gesehen hat.

Gedacht ist er für den täglichen oder stündlichen Zeitplan: Sie richten eine Suche ein, hinterlegen optional einen Webhook, und bekommen die Neuzugänge gemeldet, statt jedes Mal dieselbe Liste durchzusehen. In angespannten Märkten entscheidet genau das.

**Einrichtung:** Portal und Ort wählen — oder einfach die Such-URL aus dem Portal einfügen, dann gelten alle dort gesetzten Filter. Wer mehrere Suchen parallel beobachtet, vergibt je Suche einen eigenen Namen im Feld „Monitor name", damit sich die Verläufe nicht vermischen.

**Der erste Lauf gibt den aktuellen Bestand aus**, damit Sie sehen, wie die Daten aussehen; ab dem zweiten Lauf kommen nur noch echte Neuzugänge. Wer den Webhook sofort scharf schaltet und keine Benachrichtigung über hunderte Bestandsobjekte will, schaltet „Return everything on the first run" ab — dann bleibt der erste Lauf still und legt nur den Grundbestand an.

**Kosten:** Sie zahlen einen kleinen Betrag je geprüftem Inserat und einen höheren je tatsächlich neuem Treffer. Ein Lauf ohne Neuzugänge kostet damit fast nichts. Die Aufteilung ist nötig, weil die Arbeit auch dann anfällt, wenn es nichts Neues gibt.

**Proxys:** Die Portale sperren Anfragen aus Rechenzentren mit HTTP 403. Voreingestellt sind deshalb Apify-Residential-Proxys aus dem passenden Land; eigene Proxys lassen sich hinterlegen.

# Actor input Schema

## `portal` (type: `string`):

Which portal to watch.

## `searchUrl` (type: `string`):

Paste any search URL from the portal. If set, the location and category fields below are ignored. Search and filter on the portal itself, then copy the address — this works for every location and every filter combination, including ones this Actor does not expose as fields.

## `location` (type: `string`):

For Immowelt, a German city name such as Berlin, Munich (München) or Leipzig. For willhaben, an Austrian town or state such as Wien, Graz or Steiermark; smaller municipalities are matched to their state automatically.

## `offerType` (type: `string`):

Whether to watch properties for sale or for rent.

## `estateType` (type: `string`):

Type of property. Applies to Immowelt only; willhaben and Kleinanzeigen use the category field instead.

## `category` (type: `string`):

For willhaben: eigentumswohnung, mietwohnung, haus-kaufen, haus-mieten, grundstuecke. For Kleinanzeigen: mietwohnung, eigentumswohnung, haus-mieten, haus-kaufen, grundstueck.

## `locationSlug` (type: `string`):

The location exactly as it appears in the Kleinanzeigen URL, for example karlsruhe.

## `locationCode` (type: `string`):

The code starting with l from the Kleinanzeigen URL, for example l9186 for Karlsruhe.

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

How many listings the monitor scans each run. Higher is more thorough but slower and more expensive. Portals show newest first, so 200 is enough to catch a full day in most cities.

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

Listings below this price are ignored.

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

Listings above this price are ignored.

## `minArea` (type: `integer`):

Listings with less living space are ignored.

## `maxArea` (type: `integer`):

Listings with more living space are ignored.

## `minRooms` (type: `integer`):

Listings with fewer rooms are ignored.

## `onlyPrivate` (type: `boolean`):

Hides commercial agencies. Most effective on Kleinanzeigen, where commission-free offers from owners appear.

## `webhookUrl` (type: `string`):

New listings are sent as a JSON POST to this address. Works with Zapier, Make, n8n, Slack workflows or your own endpoint.

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

If you watch several searches in parallel, give each one its own name so their histories stay separate.

## `emitAllOnFirstRun` (type: `boolean`):

On by default, so your very first run returns the current stock and you can see what the data looks like. Every run after that returns only genuinely new listings. Turn this off if you would rather have the first run stay silent and only build the baseline — useful when you wire the webhook up straight away and do not want an alert containing hundreds of existing listings.

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

Property portals block requests from data centres. The default uses Apify residential proxies from the matching country — without them runs fail with HTTP 403. Your own proxies work too.

## Actor input object example

```json
{
  "portal": "immowelt",
  "searchUrl": "https://www.immowelt.de/classified-search?distributionTypes=Buy&estateTypes=Apartment&locations=AD08DE8634",
  "location": "berlin",
  "offerType": "buy",
  "estateType": "apartment",
  "category": "eigentumswohnung",
  "maxItems": 200,
  "onlyPrivate": false,
  "stateKey": "default",
  "emitAllOnFirstRun": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Listings that appeared since the previous run (or the current stock on the first run).

## `summary` (type: `string`):

How many listings were checked and how many were new.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("mocha_tassel/real-estate-listing-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("mocha_tassel/real-estate-listing-monitor").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 '{}' |
apify call mocha_tassel/real-estate-listing-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mocha_tassel/real-estate-listing-monitor"
        }
    }
}

```

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/Vhu6SPmzjh6wJVUPf/builds/9p7J29qDir7ahm9Hw/openapi.json
