# ImmobilienScout24 Listings Scraper · No login · $0.4/1K (`mlg14/immobilienscout24-scraper`) Actor

Get German property listings from ImmobilienScout24 search URLs for rental and sales market research, investment screening and lead generation. Returns price, rooms, living space, address, coordinates, agent and photos. $0.4/1K listings.

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

## Pricing

$0.40 / 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

## ImmobilienScout24 Listings Scraper

Extract German property listings from ImmobilienScout24 search pages and individual expose URLs into a structured dataset. This offers a public data API alternative: paste a supported property search URL, set a result cap, and export the dataset as JSON, CSV, or Excel.

**Verified search run:** A remote golden run on 26 September 2026 stored 30 Berlin rental listings and passed every required fill check. Search and direct detail JSON routes both returned populated data in remote probes. The validated run used search results only (`includeDetails: false`); detail enrichment and other search locations still need separate live validation.

### What data can you extract from ImmobilienScout24?

The output is one row per property. Fields are emitted in every row, while nullable values remain empty when the source does not publish them. The search endpoint supplied 50 records on each of the first two Berlin pages probed. The golden run stored the first 30 after applying `maxItems: 30`. Detail requests can add descriptions, more images, and building or advertiser fields when the expose response includes them.

| Field | Description | Expected shape |
|---|---|---|
| `listingId` | Listing identifier from an expose link or embedded record | String of digits |
| `url` | Direct expose link assembled from the identifier | HTTPS URL |
| `searchUrl` | Search URL that found the listing; empty for a direct expose input | HTTPS URL or null |
| `title` | Advertised listing headline | Text or null |
| `description` | Description from embedded data or a page summary | Text or null |
| `price` | Advertised price or rent, as a number | Number or null |
| `currency` | Price currency | `EUR` for German listings |
| `livingSpace` | Living area in square metres | Number or null |
| `rooms` | Number of rooms | Number or null |
| `propertyType` | Property category when stated by the page | Text or null |
| `street` | Published street | Text or null |
| `postalCode` | Published postal code | Text or null |
| `city` | Published city | Text or null |
| `district` | Published local district | Text or null |
| `latitude` | Published latitude | Number or null |
| `longitude` | Published longitude | Number or null |
| `imageUrls` | Public image addresses | Array of URLs |
| `photosCount` | Number of image URLs retained | Number |
| `agentName` | Published advertiser name | Text or null |
| `agentCompany` | Published advertiser company | Text or null |
| `agentPhone` | Public advertiser phone | Text or null |
| `energyClass` | Published building energy class | Text or null |
| `yearBuilt` | Published construction year | Number or null |
| `bathrooms` | Published bathroom count | Number or null |
| `floor` | Published floor description | Text or null |
| `isPrivate` | Search flag for a private listing | Boolean or null |
| `isNew` | Search flag for a newly marked listing | Boolean or null |
| `isProject` | Search flag for a development unit | Boolean or null |
| `isExclusive` | Search flag for a listing exclusive to this site | Boolean or null |
| `listingType` | Search placement type | Text or null |
| `publishedLabel` | Relative publication text displayed in search | Text or null |
| `publishedAt` | Reserved absolute publication date; currently null | Text or null |
| `updatedAt` | Reserved absolute modification date; currently null | Text or null |
| `scrapedAt` | Time this actor produced the row | UTC timestamp |

The actor does not infer undisclosed addresses, invent coordinates, or substitute area averages for a listing price. Search coordinates are copied as published; the endpoint does not label their precision, so consumers should not assume rooftop accuracy. A null value means that the extractor did not find a value in the supported response fields. Search and detail data are merged by listing identifier. If an optional detail request fails, the actor keeps the search row and logs the failure.

### How to scrape ImmobilienScout24

1. Open a public German property search and select the desired location, property type, transaction type, and price or size filters.
2. Copy the resulting URL into `startUrls`. Individual `https://www.immobilienscout24.de/expose/…` links are also accepted.
3. Set `maxItems` to the number of properties you need. Set `maxPages` if you need to limit how far a search advances. Leave `includeDetails` enabled for richer rows when expose pages are reachable.
4. Start the actor and check that the run succeeds and the dataset contains the expected number of rows. Download the default dataset in the format you need. A failed or partial run should never be interpreted as an empty market.

Only German region search and expose URLs are accepted by this version. The input validator rejects unsupported domains and URL patterns early. Price, room count, living space, sorting, and price type filters encoded in a search URL are carried to the JSON search request. Other URL filters are not yet supported. The actor requests later pages, deduplicates identifiers across URLs, and stops at the global item cap or the returned page count. When a page produces no new identifiers, the search ends.

### Input

| Parameter | Type | Default | Description |
|---|---|---|---|
| `startUrls` | Array of URL objects | Required | German region search or expose URLs. Supported URL filters are carried to the search request. |
| `maxItems` | Integer | `100` | Global number of listing rows to store. `0` removes the item cap. |
| `maxPages` | Integer | `20` | Maximum result pages requested per search URL. |
| `includeDetails` | Boolean | `true` | Request expose pages to seek richer property data. |
| `proxyConfiguration` | Proxy settings | Platform default | Proxy configuration for remote requests. |

Example search input:

```json
{
  "startUrls": [
    {"url": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"}
  ],
  "maxItems": 100,
  "maxPages": 5,
  "includeDetails": true
}
```

Example direct expose input:

```json
{
  "startUrls": [
    {"url": "https://www.immobilienscout24.de/expose/170200364"}
  ],
  "maxItems": 1
}
```

The direct expose ID above returned populated detail JSON during source discovery. Listings can disappear, so an ID may later become unavailable. An unavailable direct expose response causes the run to fail rather than emitting invented property fields.

### Output example

This is a real row from the 30-item golden dataset, recorded on 26 September 2026. It comes from a search page with detail requests disabled, so fields that require detail data are null. The image URL is omitted here for readability; the stored row contains one public image URL.

```json
{
  "listingId": "170931643",
  "url": "https://www.immobilienscout24.de/expose/170931643",
  "searchUrl": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten",
  "title": "Historic charm meets modern living",
  "description": null,
  "price": 770,
  "currency": "EUR",
  "livingSpace": 30.46,
  "rooms": 2,
  "propertyType": "apartmentrent",
  "street": "Meta-London-Straße 6",
  "postalCode": "13587",
  "city": "Berlin",
  "district": "Spandau",
  "latitude": 52.55683,
  "longitude": 13.21075,
  "photosCount": 1,
  "energyClass": "D",
  "isPrivate": false,
  "isNew": false,
  "isProject": false,
  "isExclusive": false,
  "listingType": "XL",
  "publishedLabel": "9 days ago",
  "scrapedAt": "2026-09-26T16:09:19.720353+00:00"
}
```

The `publishedLabel` is relative text from the source, not an absolute publication timestamp. The actor leaves `publishedAt` null instead of converting that label into an unverified date.

### Use cases

- **Rental market snapshots:** Collect the same city search at regular intervals to compare available rents, sizes, and room counts. Store each run separately when you need a history; this version does not calculate changes between runs.
- **Purchase inventory:** Use a sale search URL and compare asking prices by district or living area. Any calculated price per square metre should be based only on rows with both valid price and area values.
- **Property research:** Gather links and public descriptions for listings that meet a filtered search. A direct expose URL can be used when the property is already known.
- **Listing quality checks:** Inspect which listings publish an image, a description, or a usable location. Empty optional fields should be interpreted as missing source information or incomplete extraction, not as negative facts about the property.
- **Data export:** Download the validated default dataset for further analysis or an internal reporting workflow.

The current golden run confirms the basic Berlin rental search flow. These wider uses still require a suitable search URL and a complete run for the particular location or transaction type.

### How much does it cost to scrape ImmobilienScout24?

The configured result price at validation was **$0.40 per 1,000 dataset rows**. At that rate, 100 rows cost $0.04, 1,000 rows cost $0.40, and 5,000 rows cost $2.00 for result events. The validated run delivered 30 rows. Check the displayed pricing before a later run, because configuration may change. The number of results actually reachable depends on the search and any site limits.

The item cap controls output volume, not the number of requests needed to reach it. A search with detail requests enabled can request one expose page for each candidate listing. The golden run disabled details and needed only one search page to store 30 rows. Review the platform cost estimate and run log when using larger limits or detail enrichment.

### Tips for best results

Start with a regional search URL that clearly identifies the place, property type, and rent or purchase mode. Berlin apartment rentals are the validated example. Keep `maxItems` and `maxPages` small for an initial run, then increase them after checking the first dataset. Search responses report a page size of 50 in the probed Berlin query.

If your goal is only to collect identifiers and search-level facts, set `includeDetails` to `false`. This avoids the extra expose request per listing. If you need descriptions and image links, enable it and inspect field coverage in the resulting dataset. Detail enrichment cannot fill data that the public listing does not supply.

The actor deduplicates listing IDs across every supplied URL. Overlapping city and district searches therefore do not create duplicate rows. The `searchUrl` field records the first search that encountered a listing. The global `maxItems` limit applies after deduplication; duplicates do not spend a result slot.

Use the URL generated by the website for unusual search filters. This version does not offer independent input controls for price bounds, room bounds, map polygons, or radius. Such filters may be present in a pasted URL, but their behavior has not been verified in a live run.

### Limits

The ordinary website search and expose HTML pages returned HTTP 401 in remote probes. The public developer API required authorization, and sitemap, alternate host, AMP, and feed variants did not provide listings. The successful source is a separate public mobile JSON route: search uses a POST result list, and detail uses an expose JSON response. The validated Berlin search reported 4,290 results across 86 pages with 50 entries per page. That is an observed response, not a guarantee of unrestricted paging for every query.

Only German region search and expose URLs are implemented. Agent profiles, other country domains, map polygon and radius searches, monitoring, price history, and removed-listing detection are outside this version. The actor does not claim coverage for those surfaces. The first two Berlin result pages returned distinct populated JSON, confirming the `pagenumber` parameter for the tested query.

Coordinates, exact addresses, prices, and advertiser fields may be hidden by the publisher. The parser never estimates them. Search cards in the golden run filled listing ID, URL, title, price, and city in all 30 rows. The run did not test the `includeDetails` path, even though a single expose endpoint returned populated JSON in a probe. Treat detail-only fields as less certain until a detail-enabled run is verified.

### Programmatic use

A run can be started with the same JSON input used in the web form. After a successful run, read the default dataset to obtain one object per listing. Store the source URL and run time alongside exported records so later analysis can distinguish separate searches and dates. Check run status and item count before consuming the dataset; a failed run with zero rows would not prove that a market has no listings.

For regular collection, schedule a modest search and inspect each run's status and item count. This actor does not retain prior search state. If you want to identify new, changed, or removed listings, compare datasets in your own storage only after complete and successful runs; a failed or partial run cannot support removal conclusions.

### Use with AI agents (MCP)

Add this actor to any MCP client (Claude, Cursor, VS Code, n8n and others) with one URL:

`https://mcp.apify.com?tools=mlg14/immobilienscout24-scraper`

The actor returns one dataset item per listing (price, rooms, living space, address, coordinates, agent, images). Keep results bounded with maxItems and maxPages.

**Prompt 1:** "Get 50 apartments for rent in Berlin from ImmobilienScout24."

```json
{
  "startUrls": [
    {
      "url": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
    }
  ],
  "maxItems": 50,
  "maxPages": 3,
  "includeDetails": true
}
```

**Prompt 2:** "Fetch the details of this ImmobilienScout24 expose."

```json
{
  "startUrls": [
    {
      "url": "https://www.immobilienscout24.de/expose/170200364"
    }
  ],
  "maxItems": 1,
  "includeDetails": true
}
```

**Prompt 3:** "List Berlin rental apartments from ImmobilienScout24 with prices only, fast."

```json
{
  "startUrls": [
    {
      "url": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
    }
  ],
  "maxItems": 30,
  "maxPages": 2,
  "includeDetails": false
}
```

### FAQ

**Is the actor currently working?** The remote golden run on 26 September 2026 succeeded with 30 Berlin rental listings. ID, URL, title, price, and city each had 100% fill in that sample.

**Does it support buy searches?** Yes. `wohnung-kaufen` and `haus-kaufen` URLs work; prices, living space and rooms are returned as plain numbers (for example 710000, not 710.0).

**Do I need a proxy?** Requests use the platform proxy. The successful mobile JSON probe and golden run used that setup. The ordinary website HTML remained blocked, but the actor no longer relies on it.

**Can I pass a single property URL?** Yes, a German expose URL is accepted and mapped to the public detail JSON route. A detail endpoint returned populated JSON in a remote probe; a direct expose actor run has not yet been validated.

**Can I scrape another country domain?** No. The current URL validator accepts only German search and expose links.

**Can I schedule the actor?** Yes. Check each scheduled run for success and expected item count before comparing its dataset with earlier runs. This actor does not store previous search state.

**Can I export to a spreadsheet?** Yes. The verified golden run produced 30 dataset rows that can be downloaded in spreadsheet-compatible formats.

**Why is a field empty?** The source may omit it, the search card may not include it, or the extractor may not recognize its representation. Compare the value with a reachable source page before drawing conclusions.

**Is it legal to collect this data?** Use only public data for a lawful purpose and respect applicable site terms and privacy rules. Avoid using personal details in ways that exceed your rights to collect or process them.

### Integrations

The default dataset can be read by an API client, automation workflow, or scheduled export. Run status and dataset item count should be checked together. A failed run should trigger an alert or retry policy rather than silently replacing an inventory table with zero properties.

### Support

Open an issue on the Issues tab with the failing URL pattern, run identifier, and the first relevant error line. Include whether detail enrichment was enabled so the failing request can be identified.

# Actor input Schema

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

ImmobilienScout24 search result URLs or single expose URLs, as a list of {"url": ...} objects. Example: \[{"url": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"}]. Use it to choose the city and filters; price, rooms, living space and sorting in the URL are kept.

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

Maximum listings across all URLs (integer, 0 = no cap). Example: 50. Use a small number first.

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

Maximum result pages per search URL (integer, 1 or more). Example: 5. Use it as a safety limit.

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

If true, opens each listing's expose page for description, images and extra property fields (true or false). Example: true. Use false for faster list-only runs.

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

Proxy settings for remote requests. The default uses the platform proxy.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
    }
  ],
  "maxItems": 100,
  "maxPages": 20,
  "includeDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Extracted properties in the default dataset.

# 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.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/immobilienscout24-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.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten" }] }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/immobilienscout24-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.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
    }
  ]
}' |
apify call mlg14/immobilienscout24-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mlg14/immobilienscout24-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/0NjgIdwr5UiVqqObv/builds/SBFSYVZ1AxmbTWzyo/openapi.json
