# ImmoScout24 Scraper – Germany Real Estate (`ninhothedev/immoscout24-scraper`) Actor

Export German ImmoScout24 apartment, house and plot listings with prices, size and location. Start with a city or search URL and a bounded result limit.

- **URL**: https://apify.com/ninhothedev/immoscout24-scraper.md
- **Developed by:** [ninhothedev](https://apify.com/ninhothedev) (community)
- **Categories:** Real estate
- **Stats:** 35 total users, 3 monthly users, 98.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.35 / 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?

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

## ImmoScout24 Scraper — German property listings

Export apartment, house and plot listings from **immobilienscout24.de** into JSON or CSV. Collect source prices, living area, rooms and location for a spreadsheet or recurring property feed. Start with five listings; optional detail requests are off by default.

### Quick start

In the Console, leave **City** as Berlin and use the small prefills: **Maximum unique listings: 5**, **Max. Result Pages: 1**. For the API, copy:

```json
{"city":"Berlin","propertyType":"wohnung","priceType":"kauf","maxItems":5,"maxPages":1}
```

Open **Listings (JSON)** or **Download CSV**. **Run outcome and limits** explains why collection stopped and whether optional detail requests failed.

#### Use the exact search you configured on the website

```json
{
  "searchUrl":"https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-kaufen?price=200000-600000",
  "maxItems":5,
  "maxPages":1,
  "enrichDetails":false
}
```

`searchUrl` accepts German HTTPS `/Suche/de/…/wohnung-kaufen`, `wohnung-mieten`, `haus-kaufen`, `haus-mieten` and corresponding `grundstueck` search paths. Its source filters replace the form's city/type/price/room/area filters; they are not combined. Pagination starts at page one even when the pasted URL contains another page number. Austria, Switzerland, arbitrary websites and individual expose URLs are not supported search inputs.

### Input

| Field | API default | Meaning |
|---|---|---|
| `searchUrl` | Empty | Exact supported German search URL; takes precedence over form filters. |
| `city` | `Berlin` | German city, or empty for nationwide. Prefer a website URL for an exact location or a city path that cannot be resolved. |
| `propertyType` | `wohnung` | `wohnung`, `haus`, `grundstueck`. |
| `priceType` | `kauf` | `kauf` or `miete`. |
| `priceMin`, `priceMax` | Unset | Source price range in EUR. |
| `roomsMin`, `roomsMax` | Unset | Room range; half rooms supported. |
| `areaMin`, `areaMax` | Unset | Living area range in m². |
| `maxItems` | `2000` | Global unique-listing cap, 1–2000; Console prefill is 5. |
| `maxPages` | `5` | Source-page cap, 1–50; Console prefill is 1. |
| `enrichDetails` | `false` | Attempt each selected expose page for additional fields, description and photos. |
| `proxyConfiguration` | Direct connection | Optional Apify Proxy configuration. No proxy guarantees access. |

The API defaults preserve existing page-based workflows; send explicit small limits for a first run. Actual results can be below either limit. Page sizes vary; 50 is this Actor's allowed page limit, not a claim about the portal's maximum. Invalid types/ranges fail before collection. Previously advertised but unwired fields such as `zipCode`, `radiusKm` and `hasBalcony` are rejected explicitly; configure these filters on the website and use `searchUrl`.

### Output and data quality

Each row has a stable `external_id`, `source: "immoscout24"`, the expose `url` and a UTC `scraped_at`. Existing field names are retained. `detail_status` is `not_requested`, `enriched` or `failed`.

Example values are illustrative:

```json
{
  "external_id":"123456789",
  "source":"immoscout24",
  "url":"https://www.immobilienscout24.de/expose/123456789",
  "title":"Example apartment",
  "price":449500,
  "price_type":"kauf",
  "property_type":"wohnung",
  "area_sqm":62,
  "rooms":3,
  "city":"Berlin",
  "zip_code":"10785",
  "has_balcony":true,
  "energy_kwh":null,
  "detail_status":"not_requested",
  "scraped_at":"2026-09-05T19:00:00+00:00"
}
```

Additional fields, when available: `plot_sqm`, `additional_costs`, `heating_costs`, `deposit`, `total_rent`, `address`, `district`, `state`, `latitude`, `longitude`, `floor`, `total_floors`, `year_built`, `energy_rating`, `heating_type`, `condition`, `has_garden`, `has_garage`, `has_elevator`, `has_cellar`, `is_commission_free`, `commission`, `thumbnail_url`, `images`, `description`.

Money fields are EUR; area fields are m²; `energy_kwh` represents source energy consumption in kWh/m²/year, not a boolean about warm water. Missing numbers and boolean features remain null. Legacy string fields may be empty. Missing state is not guessed from a city name. Coordinates and full addresses are only returned when exposed by the source. Search rows may already contain photos or a short description.

Detail mode makes additional requests before delivering each selected row. It can add description text up to 20,000 characters and available images/fields. `enriched` means a recognized detail page was parsed, not that every optional field exists. A failed detail attempt retains the valid search record with `detail_status: "failed"`; the run summary counts these failures. No completeness, full-gallery or source-access guarantee is made.

Recommendations stored separately as `similarObjects` are excluded because they may not match the search. IDs are deduplicated before detail requests and billing. Unrecognized or blocked search pages fail the run rather than masquerading as a successful empty search; previously delivered rows remain accessible. A recognized source response reporting zero hits can legitimately succeed with zero rows. A returned page number that differs from the requested page fails explicitly.

### Pricing and spending control

As checked on September 5, 2026, published event pricing is **$0.00135 per delivered listing ($1.35 per 1,000)** plus **$0.00005 per start** at the default 1 GB memory. The Store pricing preview is authoritative; this release does not change pricing.

| Scenario | Actor event fees |
|---|---:|
| 5 listings, one run | $0.00680 |
| 100 listings, one run | $0.13505 |
| 1,000 listings, one run | $1.35005 |

These are event fees, not an estimate of your whole Apify subscription, applicable tax or separately enabled platform usage. Higher run memory changes the start-event count. Detail mode has no new custom charge in this release but increases requests and runtime. A valid search record with a failed optional detail request remains a delivered listing.

Set **maximum cost per run** in Apify as well as `maxItems` and `maxPages`. Before fetching another page, the Actor checks the SDK's remaining result budget; it caps selected unique rows before details. Each row is saved immediately. Apify charges the synthetic dataset event automatically; the Actor never adds a duplicate manual charge.

A resurrected run skips IDs already in its dataset. Start a new run for fresh prices. Cookie sharing across separate runs, built-in historical change tracking and notifications are not provided. For monitoring, schedule new runs and compare stable IDs in your downstream workflow.

### Troubleshooting

- **Blocked/unavailable search:** the source may restrict this client. A bounded HTTP retry and browser fallback are attempted. No access guarantee follows from a user-agent or robots.txt statement.
- **Unexpected city:** use the exact supported URL from the website; location names alone can be ambiguous.
- **Fewer rows:** inspect the result/page/cost caps, deduplication and `RUN_SUMMARY`.
- **Missing detail fields:** inspect `detail_status`; optional source fields can remain unavailable even after enrichment.

Report bugs through the Actor's Issues tab with a run ID and minimal example input. Remove sensitive parameters before sharing. This Actor does not send alerts or messages itself.

### Development

Use Python 3.11+ and install `requirements.txt` (includes the pinned `requirements.lock`) plus `requirements-dev.txt`. Run `python -m pytest -q` and `apify validate-schema`. For the local Actor runtime, place the quick-start JSON into `storage/key_value_stores/default/INPUT.json`, activate the virtual environment, and run `apify run`. Local storage is not uploaded to Apify. Browser fallback requires the pinned Playwright Chromium installation.

# Actor input Schema

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

Paste a German https://www.immobilienscout24.de/Suche/de/... search URL. When supplied, its source filters replace city/type/price/room/area form filters. Starts from page one.

## `city` (type: `string`):

German city name. Defaults to Berlin; ignored when searchUrl is supplied. For an exact location or an unsupported city path, paste the website search URL.

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

Global limit after deduplication and before optional details. Console prefill 5 for a small first run; API default 2000 preserves existing maxPages workflows.

## `propertyType` (type: `string`):

Type of property to search for: apartment (Wohnung), house (Haus), or plot (Grundstück).

## `priceType` (type: `string`):

Whether to search for properties for sale (Kaufen) or for rent (Mieten).

## `priceMin` (type: `integer`):

Minimum price in Euro. Leave empty for no lower price limit.

## `priceMax` (type: `integer`):

Maximum price in Euro. Leave empty for no upper price limit.

## `roomsMin` (type: `number`):

Minimum number of rooms (supports half rooms, e.g. 2.5). Leave empty for no lower limit.

## `roomsMax` (type: `number`):

Maximum number of rooms (supports half rooms, e.g. 4.5). Leave empty for no upper limit.

## `areaMin` (type: `integer`):

Minimum living area in square meters. Leave empty for no lower limit.

## `areaMax` (type: `integer`):

Maximum living area in square meters. Leave empty for no upper limit.

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

Maximum source pages to request (1–50). The scraper stops earlier at maxItems, the run budget or the last source page. Listing counts per page can vary.

## `enrichDetails` (type: `boolean`):

Attempt each selected listing page to add description (up to 20,000 characters) and photos/fields when exposed. Adds requests and runtime. See detail\_status per row; failed detail attempts retain the valid search record.

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

Optional Apify Proxy settings. Configured proxy failures stop the run instead of silently using a direct connection. A proxy does not guarantee access.

## Actor input object example

```json
{
  "city": "Berlin",
  "maxItems": 5,
  "propertyType": "wohnung",
  "priceType": "kauf",
  "priceMin": 100000,
  "priceMax": 600000,
  "roomsMin": 2,
  "roomsMax": 6,
  "areaMin": 50,
  "areaMax": 200,
  "maxPages": 1,
  "enrichDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `csv` (type: `string`):

No description

## `summary` (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 = {
    "city": "Berlin",
    "maxItems": 5,
    "maxPages": 1,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ninhothedev/immoscout24-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 = {
    "city": "Berlin",
    "maxItems": 5,
    "maxPages": 1,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("ninhothedev/immoscout24-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 '{
  "city": "Berlin",
  "maxItems": 5,
  "maxPages": 1,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call ninhothedev/immoscout24-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ninhothedev/immoscout24-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/0cVdSNkIXxFeUBcD6/builds/1OQZrabXjLeErtTjj/openapi.json
