# Immowelt.de Scraper: rent & buy flats, houses (search + alerts) (`datahamster/immowelt-listings`) Actor

Immowelt scraper for immowelt.de: flats and houses to rent or buy in any German city, by filters or any immowelt search URL. Rows with price and price type (Kaltmiete, Kaufpreis), rooms, living and plot area, street, district, city, postcode, agency and photos. Monitor mode alerts on new listings.

- **URL**: https://apify.com/datahamster/immowelt-listings.md
- **Developed by:** [Viktor Dubnytskiy](https://apify.com/datahamster) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 result items

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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.de Scraper: rent & buy flats, houses (search + alerts)

An Immowelt scraper for **immowelt.de**, Germany's second-largest property portal: flats (Wohnungen) and houses (Häuser) to rent or buy, searched by city with price, room and area filters — or by pasting any immowelt search URL. Every exposé comes back as one flat row, and monitor mode on a saved Task returns only the listings that are new or changed price since your last run.

### What you get — example output

One row per exposé. Real values from the example dataset (`locations: ["Berlin"]`, rent, apartment):

| Field | Example value | What it is |
|---|---|---|
| `listingId` | `264C9FHP3E4H` | Stable immowelt listing id (`url` leads to the exposé) |
| `title` | `Wohnung zur Miete auf Zeit - Erstbezug` | Listing headline |
| `price` / `priceType` | `1799` / `Kaltmiete` | Numeric price in EUR and which price it is (`Kaltmiete`, `Warmmiete`, `Kaufpreis`) |
| `rooms` / `livingArea` | `3` / `87.7` | Rooms and living area in m² (`plotArea` for houses) |
| `address` | `Archenholdstraße 21, Friedrichsfelde, Lichtenberg (10315)` | Full address line, also split into `street`, `district`, `city`, `postcode` |
| `availability` | `frei ab sofort` | When the flat is free, as immowelt states it |
| `agencyName` | `STRATEGIS AG` | Agency publishing the listing |
| `imagesCount` | `27` | How many photos the exposé has (`images[]` holds the URLs) |
| `isNew` / `isNewBuild` | `false` / `false` | Freshly posted flag and Neubau flag |

Full field list: `listingId`, `url`, `title`, `price`, `currency`, `priceLabel`, `priceType`, `rooms`, `livingArea`, `plotArea`, `availability`, `keyFacts[]`, `address`, `street`, `district`, `city`, `postcode`, `agencyName`, `contactName`, `images[]`, `imagesCount`, `isNew`, `isNewBuild`, `tags[]`, `query`, `page`, `rank`, `scrapedAt`.

### Use cases

- **Relocation and flat hunting**: watch a city or district with a price ceiling and get alerted the moment a matching flat is listed.
- **Investors and market research**: collect price, area, rooms and postcode across districts to compute €/m² by area.
- **Proptech pipelines**: feed a normalised German listing stream into your own dashboard without writing a parser.

### How it works

The actor requests immowelt result pages and reads the structured listing data embedded in them. immowelt serves complete pages only to German residential IPs, so traffic is routed through Apify's residential proxy pinned to Germany — you configure nothing, but Apify bills that traffic on top (about 1.2 MB per page). Either give `locations` plus filters, or paste `searchUrls` straight from the site. In `monitor` mode the current result set is compared with the previous run of the same Task and only new or re-priced listings are pushed, with an optional webhook or Telegram summary.

### Input

| Field | Meaning | Default |
|---|---|---|
| `distribution` | `rent` (Mieten) or `buy` (Kaufen) | `rent` |
| `estate` | `apartment` (Wohnung) or `house` (Haus) | `apartment` |
| `locations` | German city names, e.g. `["Berlin"]` | `["Berlin"]` |
| `priceTo` | Maximum Kaltmiete or Kaufpreis in EUR, e.g. `1500` | — |
| `roomsFrom` | Minimum rooms, e.g. `3` | — |
| `areaFrom` | Minimum living area in m², e.g. `60` | — |
| `searchUrls` | Any `immowelt.de/suche/...` URLs; overrides the fields above | — |
| `maxPages` | Result pages per search (see Limits) | `1` |
| `maxItems` | Stop after this many listings | `20` |
| `mode` | `scrape`, or `monitor` for new/changed only | `scrape` |
| `webhookUrl`, `telegramBotToken`, `telegramChatId` | Where monitor-mode change summaries are sent | — |

### Pricing

| Event | Price |
|---|---|
| result | $0.001 per listing ($1 per 1,000) |
| monitor-check | $0.005 per monitor run |
| change | $0.001 per new/changed listing |

Charged only for listings actually pushed — an empty run costs you nothing. immowelt serves full pages to German residential IPs; Apify bills that proxy traffic on top (about 1.2 MB per page).

### Why this actor

- German exits are pinned automatically, so immowelt returns full result pages instead of a stub.
- Price *type* is kept (`Kaltmiete` vs `Warmmiete` vs `Kaufpreis`), which is what makes German rent data comparable at all.
- Address is both raw and split into street, district, city and postcode — ready to group by area.
- Any immowelt search URL can be pasted in, so filters you trust on the site are preserved.
- Monitor mode with new-listing and price-change alerts, delivered by webhook or Telegram.

### Limits

- **32 listings per search.** immowelt loads further result pages client-side; run several narrower searches (district URLs, price bands, room counts) — duplicates across searches are never charged.
- Exposé pages are not opened: full description, energy data and contact phone are not included (`url` leads to the exposé).
- Residential traffic is billed by Apify on top of the per-result price.

### FAQ

**Can I monitor new listings for a search?** Yes. Save the input as a Task, set `mode: monitor` and schedule it — each run returns only listings that appeared or changed price since the previous run, and can POST a summary to `webhookUrl` or a Telegram chat.

**Why do I only get 32 listings per search?** immowelt renders just the first 32 results server-side and loads the rest in the browser. Split the search instead: one run per district URL, price band or room count. Duplicates across those searches are never charged.

**Can I use my own immowelt filter URL?** Yes — put one or more `immowelt.de/suche/...` URLs into `searchUrls` and they override `distribution`, `estate` and `locations`, keeping every filter you set on the site.

### Changelog

- 0.1: initial release.

***

If this actor saved you time, please leave a review on its Store page — it is the main way other buyers find it. Bug reports and field requests are welcome in the **Issues** tab.

# Actor input Schema

## `distribution` (type: `string`):

Whether to search rentals or properties for sale. Accepted values: rent (Mieten), buy (Kaufen). Example: "rent".

## `estate` (type: `string`):

Property type. Accepted values: apartment (Wohnung), house (Haus). Example: "apartment".

## `locations` (type: `array`):

German city names as written on immowelt, one per search; each is searched and paginated separately. Example: \["Berlin", "München"]. Ignored when searchUrls is set.

## `priceTo` (type: `integer`):

Maximum price in euros: monthly Kaltmiete when distribution is rent, Kaufpreis when it is buy. Example: 1500. Optional.

## `roomsFrom` (type: `integer`):

Minimum number of rooms (Zimmer), whole number. Example: 3. Optional.

## `areaFrom` (type: `integer`):

Minimum living area in square metres. Example: 60. Optional.

## `searchUrls` (type: `array`):

Any immowelt.de/suche/... result URLs with the filters you set on the site; overrides distribution, estate and locations. Example: \["https://www.immowelt.de/suche/berlin/wohnungen/mieten"]. Optional.

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

Result pages per search. immowelt renders only the first 32 results server-side; further pages load client-side and usually repeat page 1 (duplicates are never charged), so keep this at 1 and use narrower searches - districts, price bands, room counts - to go deeper. Example: 1.

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

Stop the run after this many listings; you are charged only for listings actually pushed. Example: 20.

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

scrape = return all matching listings; monitor = return only listings that are new or changed price since the previous run of this saved Task. Example: "scrape".

## `monitorKey` (type: `string`):

Optional state key for monitor mode when the actor is started directly instead of from a saved Task; any stable string, e.g. "berlin-wohnung-miete".

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

Optional HTTPS URL that receives a POST with the monitor-mode change summary (JSON). Example: "https://hooks.example.com/immowelt".

## `telegramBotToken` (type: `string`):

Optional Telegram bot token used to send monitor-mode change summaries. Format: "123456789:AA...".

## `telegramChatId` (type: `string`):

Optional Telegram chat id that receives monitor-mode summaries. Example: "-1001234567890".

## Actor input object example

```json
{
  "distribution": "rent",
  "estate": "apartment",
  "locations": [
    "Berlin"
  ],
  "maxPages": 1,
  "maxItems": 20,
  "mode": "scrape"
}
```

# Actor output Schema

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

All pushed rows (dataset, JSON)

## `resultsTable` (type: `string`):

Dataset in the Console viewer

## `runSummary` (type: `string`):

RUN\_SUMMARY record (pushed, skipped, emptyReason)

# 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 = {
    "distribution": "rent",
    "estate": "apartment",
    "locations": [
        "Berlin"
    ],
    "maxItems": 20,
    "mode": "scrape"
};

// Run the Actor and wait for it to finish
const run = await client.actor("datahamster/immowelt-listings").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 = {
    "distribution": "rent",
    "estate": "apartment",
    "locations": ["Berlin"],
    "maxItems": 20,
    "mode": "scrape",
}

# Run the Actor and wait for it to finish
run = client.actor("datahamster/immowelt-listings").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 '{
  "distribution": "rent",
  "estate": "apartment",
  "locations": [
    "Berlin"
  ],
  "maxItems": 20,
  "mode": "scrape"
}' |
apify call datahamster/immowelt-listings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datahamster/immowelt-listings"
        }
    }
}
```

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/LzZwzLJptuSQOxb3i/builds/P2vpxCjPUsFz40Plm/openapi.json
