# ImmoScout24 (AT) Real Estate Scraper (`artsiom_k/immoscout24-at-scraper`) Actor

Austria real estate scraper for ImmoScout24.at — apartments, houses, land & commercial, rent or sale — same output schema as willhaben-scraper, plus delta mode.

- **URL**: https://apify.com/artsiom\_k/immoscout24-at-scraper.md
- **Developed by:** [Artsiom Kunitsyn](https://apify.com/artsiom_k) (community)
- **Categories:** Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 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/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

## immoscout24-at-scraper

Scrapes real estate listings from [ImmoScout24.at](https://www.immobilienscout24.at) (apartments,
houses, land, commercial — rent or sale) via ImmoScout24's own internal Apollo GraphQL API, not
HTML scraping.

### Contents

- [Key features](#key-features)
- [Output](#output)
- [Input](#input)
- [Input examples](#input-examples)
- [Incremental (delta) mode](#incremental-delta-mode)
- [How to scrape ImmoScout24.at](#how-to-scrape-immoscout24at)
- [How much does it cost](#how-much-does-it-cost)
- [You might also like](#you-might-also-like)
- [FAQ](#faq)

### 🔑 Key features

- **Real-estate specialist, not a jack-of-all-trades.** Built for ImmoScout24's real-estate
  categories specifically, rather than spreading thin across cars, jobs, and marketplace listings
  too.
- **No URL wrangling.** Pick a category and an Austrian province from a dropdown — no need to know
  ImmoScout24's internal region codes yourself.
- **Delta mode built in.** Every run classifies each listing as `new`, `changed`, `unchanged`, or
  `delisted` against a persisted baseline. Run it on a schedule and you're not paying for a full
  re-scrape every time — just what actually changed. It's also what makes price-history tracking
  possible at all.
- **Full category coverage, including the categories many scrapers skip** — land/plots and
  commercial, not just apartments.
- **Same output schema as [willhaben-scraper](https://apify.com/artsiom_k/willhaben-scraper)** — a
  second, independent Austrian real-estate source with field names and value spellings that match,
  so results from both actors merge or cross-check directly instead of needing a reconciliation
  step.
- **Privacy-aware by construction.** `agency_name` is populated for professional listings only
  (company name) and left `null` for private sellers — no private-seller personal data leaves the
  dataset.

### 📋 Output

One dataset item per listing, with a normalized schema (see
[`.actor/dataset_schema.json`](.actor/dataset_schema.json) for the full field list) plus a
`change_type` field: `new`, `changed`, `unchanged`, or `delisted` (see
[Incremental mode](#incremental-delta-mode) below).

**Example job record:**

```json
{
  "source": "immoscout24_at",
  "external_id": "123456789",
  "url": "https://www.immobilienscout24.at/expose/123456789",
  "country": "AT",
  "state": "Wien",
  "district": "Donaustadt",
  "city": "Wien",
  "postal_code": "1220",
  "latitude": 48.2333,
  "longitude": 16.4667,
  "price_eur": 1390.0,
  "price_per_sqm_eur": 18.53,
  "living_area_sqm": 75.0,
  "estate_size_sqm": 75.0,
  "rooms": 3,
  "listing_type": "rent",
  "property_class": "apartment",
  "agency_name": "Muster Immobilien GmbH",
  "published_at": "2026-08-01T09:12:00.000Z",
  "change_type": "new"
}
```

Every record also carries `property_type`, `condition`, `year_built`, `energy_class`,
`has_balcony`, `has_garden`, and `has_parking` — omitted from the example above because they're
currently always `null` (see known gaps below). `district` is populated for Vienna listings only
(as shown); elsewhere it's `null` for now.

Results can be downloaded as JSON, CSV, or Excel from the Console's Output tab, or pulled via the
Apify API/dataset endpoint.

**Known gaps:** `property_type`, `condition`, `year_built`, `energy_class`, `has_balcony`,
`has_garden`, and `has_parking` are always `null` — ImmoScout24's search hit doesn't carry them.
`district` is populated for Vienna only (postcode heuristic); elsewhere it needs a
region-code-to-name gazetteer that doesn't exist yet.

### ⚙️ Input

See [`.actor/input_schema.json`](.actor/input_schema.json) for the full JSON schema. Key
parameters:

| Parameter | Type | Default | Description |
|---|---|---|---|
| `category` | String (required) | — | One of 7 categories: `apartment_rent`, `apartment_sale`, `house_rent`, `house_sale`, `land`, `commercial_rent`, `commercial_sale` — the same 7 keys `willhaben-scraper` uses. |
| `region` | String | `all` | One of Austria's 9 provinces (e.g. `wien`, `steiermark`), or `all` for the whole country. Changing this between runs starts a fresh incremental-mode baseline for the category. |
| `mode` | String | `auto` | `auto` (recommended): full scan on the first run, incremental after. `full`: always push every listing and refresh the baseline. `incremental`: always push only new/changed listings. See [Incremental mode](#incremental-delta-mode). |
| `maxItems` | Integer | `50` | Stop after pushing this many dataset items. Defaults to a fast, cheap preview (also what keeps an unconfigured run within Apify's automated 5-minute QA check). Raise it or clear it (set to `null`) for a full run — note a capped run never updates the incremental baseline (see below), so any real tracking run needs this cleared. |
| `maxPages` | Integer | *(none)* | Safety cap on search pages fetched **per internal region slice** (an all-Austria run is automatically split into per-province slices under the hood). A capped run can't detect delistings and won't update the incremental baseline. |
| `delaySeconds` | Number | `3` | Politeness delay between consecutive search-page requests. |
| `proxyConfiguration` | Object | `{"useApifyProxy": false}` | Apify Proxy config. Off by default — confirmed by testing that ImmoScout24 returns HTTP 401 to Apify's shared datacenter proxy pool on the very first request, while a direct connection works fine. |
| `impersonate` | String | *(none)* | Optional curl\_cffi TLS-impersonation target (e.g. `chrome124`), used only if ImmoScout24 starts blocking plain requests. |

### 🧪 Input examples

**Quick preview** — Wien apartments for rent, default 50-item cap:

```json
{
  "category": "apartment_rent",
  "region": "wien"
}
```

**Full-country scan** — every land/plot listing in Austria, capped for a quick preview:

```json
{
  "category": "land",
  "region": "all",
  "maxItems": 100
}
```

**Scheduled tracking run** — full, uncapped run (`maxItems` cleared — required for the baseline to
save and delistings to be detected, see [Incremental mode](#incremental-delta-mode)):

```json
{
  "category": "apartment_rent",
  "region": "wien",
  "mode": "incremental",
  "maxItems": null
}
```

### 🔄 Incremental (delta) mode

Every run classifies each listing as `new`, `changed` (price moved), `unchanged`, or `delisted`,
using a state baseline persisted in a named Apify Key-Value Store scoped to `category` + `region`.

- `mode: auto` (default) — first run for a scope pushes everything (`full`); later runs push only
  `new`/`changed`/`delisted` (`incremental`).
- A **province-scoped** run (anything but "All of Austria") is not a full scan and never updates
  the baseline or reports delistings — same rule as a page-capped run. Recommended setup: schedule
  a frequent Task on `auto` with `region: all`, plus a periodic (e.g. weekly) Task explicitly on
  `mode: full` to force a full resync.

Full design: [`docs/incremental-mode.md`](../../docs/incremental-mode.md).

### 🚀 How to scrape ImmoScout24.at

1. Open the ImmoScout24 (AT) Real Estate Scraper in Apify Console and go to the **Input** tab.
2. Pick a **category** (e.g. "Apartments — rent") and a **region** (e.g. "Wien"), or leave region
   at "All of Austria" for full country coverage.
3. `maxItems` defaults to 50 (a quick preview) — clear it (set to `null`) along with `maxPages` for
   a full, uncapped run.
4. Click **Start**.
5. When the run finishes, browse results in the **Output** tab, or download as JSON/CSV/Excel, or
   fetch them via the API.
6. To track a market over time instead of scraping once: create a **Schedule** with `mode: auto`
   and `region: all` — the first run does a full scan, every run after only bills the listings
   that actually changed.

### 💰 How much does it cost

Pricing: **$3.50 per 1,000 results** (pay-per-result), plus a fixed, negligible per-run start fee.
No cost for pages that return zero results.

**Example:** Wien's apartment-rent inventory is confirmed at roughly 3,685 listings (live-verified
against the site). A full scan of that scope costs roughly **$12.90**. Because of built-in delta
mode, a scheduled re-run afterwards only bills for listings that are actually `new`, `changed`, or
`delisted` — not the full ~3,685 again — so ongoing monitoring costs a fraction of the initial scan
once the baseline is established.

### 🔗 You might also like

- **[Willhaben.at Real Estate Scraper](https://apify.com/artsiom_k/willhaben-scraper)** — a second,
  independent Austrian real-estate source with the identical output schema by design, so results
  from both merge directly for cross-checking coverage or de-duplicating listings posted on both
  sites.

### ❓ FAQ

**Is it legal to scrape ImmoScout24.at?** It's legal to collect publicly available listing data
such as prices, descriptions, and locations. Results may contain limited personal data (agency
names for professional listings only — see [Key features](#key-features)); scrape it only with a
legitimate purpose under GDPR.

**How do I get only new/changed listings?** Use `mode: auto` (or `incremental`) on a schedule — see
[Incremental mode](#incremental-delta-mode).

**Why are some fields always `null`?** `property_type`, `condition`, `year_built`, `energy_class`,
and the `has_balcony`/`has_garden`/`has_parking` flags are always `null`, and `district` is
Vienna-only — see [Output](#output) for why.

### Search keywords

immoscout24 scraper, immoscout24.at scraper, immobilienscout24 austria scraper, austria real
estate scraper, austrian property scraper, immobilien scraper wien, apartment scraper austria,
house scraper austria, immoscout24 api, immoscout24 graphql scraper, real estate delta scraper,
real estate price tracking, austria property data feed

# Actor input Schema

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

Which ImmoScout24 real-estate search category to scrape.

## `region` (type: `string`):

Restrict the search to one Austrian province, or scrape the whole country. Note: changing this between runs starts a fresh incremental-mode baseline for this category. A province-scoped run is not a full scan (it won't report delistings) — see the README.

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

Safety cap on the number of search pages fetched per internal region slice. Leave empty to paginate every slice to completion. Note: a capped run cannot detect delisted listings and does not update the incremental-mode baseline (see docs/incremental-mode.md).

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

Stop after pushing this many dataset items. Defaults to 50 — a fast, cheap preview, and what keeps an unconfigured run within Apify's automated 5-minute QA check. Raise it or clear it (set to null) for a full run.

## `delaySeconds` (type: `number`):

Politeness delay between consecutive search-page requests.

## `impersonate` (type: `string`):

Optional curl\_cffi browser TLS-impersonation target (e.g. "chrome124"), used if ImmoScout24 starts blocking plain requests. Leave empty for plain requests.

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

"auto" (recommended): full scan on the first run for a given category/region, incremental (new/changed only) afterwards. "full": always push every listing and refresh the baseline — schedule this periodically to catch delistings. "incremental": always push only new/changed listings. See docs/incremental-mode.md.

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

Apify Proxy configuration. Off by default — confirmed by testing that ImmoScout24 returns HTTP 401 to Apify's shared datacenter proxy pool on the very first request, while a direct connection works fine.

## Actor input object example

```json
{
  "category": "apartment_rent",
  "region": "all",
  "maxItems": 50,
  "delaySeconds": 3,
  "mode": "auto",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

// Run the Actor and wait for it to finish
const run = await client.actor("artsiom_k/immoscout24-at-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("artsiom_k/immoscout24-at-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 '{}' |
apify call artsiom_k/immoscout24-at-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,artsiom_k/immoscout24-at-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/tQVF7hJHyaA7jwyJm/builds/cSl3vAjeEaQJTakN7/openapi.json
