# Immowelt Scraper (`crawlerbros/immowelt-scraper`) Actor

Scrape Immowelt.de - one of Germany's largest real-estate portals. Search rental and for-sale apartments and houses by city, price, rooms, living space, and amenities, or scrape a specific Immowelt search-results URL.

- **URL**: https://apify.com/crawlerbros/immowelt-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, Lead generation, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Immowelt Scraper

Scrape real-estate listings from [Immowelt.de](https://www.immowelt.de) — one of Germany's largest property portals. Search rental and for-sale apartments and houses by city, price range, rooms, living space, and amenities, or scrape any Immowelt search-results URL directly. No login, cookies, or API key required.

### What this actor does

- **Search by city** — free-text German city/town name (e.g. `Berlin`, `München`, `Hamburg`, `Köln`, `Frankfurt am Main`), combined with property type (apartment, house, plot/land, temporary/furnished living, a commercial category — office, retail, storage/production, gastronomy & hospitality — or agricultural/forestry land), rent/buy, price range, room count, living space, and amenity filters
- **Scrape a specific URL** — paste any `immowelt.de/suche/...` search-results URL (built with any filter combination on the Immowelt website) and get every listing on that page as structured data
- **Rich per-listing data** — price, rooms, living space, floor, full address, description, photo gallery, energy class, listing provider (agency or private), and more
- **Real Germany-wide coverage** — works for any German city or town Immowelt indexes, not just a fixed list

### Output per listing

- `classifiedId`, `legacyId` — Immowelt listing IDs
- `title`, `headline`, `description`
- `distributionType` (`mieten`/`kaufen`), `estateType` (`wohnung`/`haus`/`grundstueck`/`buero`/`einzelhandel`/`lager_produktion`/`gastronomie`/`landwirtschaft`), `estateSubType` (human-readable label, e.g. "Villa", "Penthouse", "Maisonette", "Office space", "Restaurant", "Forestry" — only present when Immowelt discloses it for that listing)
- `isTemporaryLiving` — present (`true`) only when found via the "Wohnen auf Zeit" temporary/furnished-living search
- `priceCold` (net/base rent — "Kaltmiete" for residential, "Nettomiete" for commercial), `priceWarm` (rent including utilities, when disclosed), `purchasePrice` (for-sale), `currency`
- `pricePerSqm` — computed from the listing's own price ÷ living space (or plot size for `grundstueck`), in €/m²
- `quotedPricePerSqm` — Immowelt's own quoted €/m² rate, only present for commercial listings (office/retail/storage-production/gastronomy) that disclose solely a per-m² rent rate instead of a total monthly price (common for divisible office/industrial space) — distinct from the computed `pricePerSqm` since no total price exists to derive it from
- `marketAvgPricePerSqm`, `marketPriceRangeLowPerSqm`, `marketPriceRangeHighPerSqm`, `marketPriceTrend1YearPct`, `marketPriceDate` — Immowelt's own city/region average €/m² benchmark (rent or purchase, matching the searched `distributionType`) for the searched property category, with its 1-year trend — useful to compare a listing's `pricePerSqm` against the local market; not available for `estateType=grundstueck` or `estateType=landwirtschaft` (no plot/land benchmark published)
- `rooms`, `livingSpaceSqm`, `plotSqm`, `floor`
- `availableFrom` — when the property becomes available (e.g. "Available immediately", "Available from 01.10.2026"), only present when Immowelt discloses it for that listing
- `street`, `district` (neighborhood), `borough` (larger administrative subdivision within the city, e.g. a Berlin/Hamburg/Leipzig/Köln/Dresden/Stuttgart borough — only present for cities that have one), `city` (the city you searched, or resolved from the search URL), `zipCode`, `state` (German federal state / Bundesland), `country`
- `energyClass` (`A+`–`H`), `buildingState` (e.g. "Fully renovated", "Mint condition")
- `isNewListing`, `has3DVisit`, `hasBrokerageFee`, `isForeclosureAuction` (compulsory-auction plot listings)
- `hasFloorPlan`, `hasVirtualTour` — whether a floor-plan image / virtual (3D) tour is available
- `providerName`, `providerType` (`professional`/`private`), `providerContactPerson` (agent/seller name shown on the listing), `providerWebsite`, `providerPhone`, `providerAddress`, `providerProfileUrl`, `providerImprintUrl` (legal imprint/Impressum page), `providerLogoUrl`, `providerBadge` (e.g. "Platin Partner"), `providerBadgeImageUrl`
- `providerRating` (0–5 star rating aggregated from Immowelt user reviews), `providerReviewCount`, `providerRatingUrl` — only present when the agency/provider has published reviews on Immowelt
- `providerFeeScheduleUrl` — link to the agency's published brokerage-fee/price-list document, when disclosed
- `projectUrl` — link to the parent new-build development/project page on Immowelt, only present when the listing is part of an active construction project
- `images[]` (`url`, `caption`, `type` — AI-classified scene type such as "Kitchen", "Living room", "Exterior view", when Immowelt discloses one), `primaryImageUrl`, `imageCount`
- `publishedAt`, `updatedAt`
- `sourceUrl` — direct link to the listing on immowelt.de
- `recordType: "listing"`, `scrapedAt`

Only fields Immowelt actually publishes for a given listing are included — no placeholder or `N/A` values.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` (by city + filters) or `byUrl` (scrape a specific search URL) |
| `city` | string | `Berlin` | German city/town name (mode=search) |
| `distributionType` | string | `mieten` | `mieten` (for rent) or `kaufen` (for sale) |
| `estateType` | string | `wohnung` | `wohnung` (apartment), `haus` (house), `grundstueck` (plot / land), `wohnen_auf_zeit` (temporary / furnished living — rent-only), `landwirtschaft` (agricultural / forestry land), or a commercial category: `buero` (office / practice space), `einzelhandel` (retail / shop space), `lager_produktion` (storage / production / industrial), `gastronomie` (gastronomy & hospitality) |
| `estateSubType` | string | – | Optional narrower sub-type (loft, penthouse, villa, bungalow, leisure/commercial land, etc.) — must match `estateType` (e.g. `villa` requires `estateType=haus`); not applicable when `estateType=wohnen_auf_zeit`, `estateType=landwirtschaft`, or any commercial category |
| `minPrice` / `maxPrice` | int | – | Price range in EUR (cold rent or purchase price) |
| `minRooms` / `maxRooms` | int | – | Room count range (set both to the same value for an exact room count) |
| `minArea` / `maxArea` | int | – | Living space (or plot / land size for `grundstueck` and `landwirtschaft`, which can run into the millions of m² for large forest tracts) range in m² |
| `excludeGroundFloor` | bool | `false` | Skip ground-floor listings |
| `features` | array | – | One amenity/feature to require (only the first is applied per run): balcony/terrace, garden, cellar, parking, pool, new build, private offers only, needs renovation, fitted kitchen, furnished, wheelchair accessible, WBS required, shared flat, swap apartment, top floor, ground floor; not applicable when `estateType=wohnen_auf_zeit` or `estateType=landwirtschaft` |
| `energyClass` | array | – | Only keep listings with one of the selected German energy efficiency classes (`A+` most efficient – `H` least efficient). Enforced client-side against each listing's disclosed energy class; listings that don't disclose one are dropped when this filter is set |
| `sortBy` | string | – | Re-order the returned listings: relevance (default), price low-to-high / high-to-low, living space (or plot size) low-to-high / high-to-low, price per m² low-to-high / high-to-low, rooms fewest-to-most / most-to-fewest, or newest first |
| `searchUrl` | string | – | A full `immowelt.de/suche/...` URL (mode=byUrl) |
| `maxItems` | int | `20` | Hard cap on emitted records (1–40) |

**Note on filters:** Immowelt's search pages return up to ~30-40 listings per query (its first results page). When `estateType=wohnen_auf_zeit`, the temporary-living category always takes priority for the site query. Otherwise property sub-type takes priority (most selective), then an exact room count (only when `minRooms` equals `maxRooms`), then ground floor / feature. Price range, room count (range or exact), living space/plot size, ground-floor, and energy class filters are always enforced against every fetched listing (client-side, on top of the site query — Immowelt has no reliable server-side URL filter for price or energy class), so results always match those filters exactly.

**Note on `estateSubType`:** the sub-type must belong to the selected `estateType` (apartment sub-types with `wohnung`, house sub-types with `haus`, plot sub-types with `grundstueck`) — Immowelt silently ignores a mismatched sub-type/property-type pairing and returns the unfiltered property-type results instead of erroring, so the actor detects this and the run's status message will say the results are NOT filtered by sub-type when that happens. When the pairing is valid, the sub-type is applied server-side, but Immowelt doesn't disclose a machine-readable sub-type on every matching listing, so `estateSubType` in the output is populated whenever Immowelt discloses it and omitted otherwise (never enforced as a hard client-side filter, unlike price/rooms/area).

**Note on `estateType=wohnen_auf_zeit`:** "Wohnen auf Zeit" is Immowelt's own category for temporary/furnished short- and mid-term rentals, spanning both apartments and houses. It's rent-only — `distributionType` is automatically switched to `mieten` if `kaufen` was requested (the run's status message will say so), and `estateSubType`/`features` aren't applied server-side for it. Price, room count, and area filters still work normally.

**Note on commercial property types (`buero`, `einzelhandel`, `lager_produktion`, `gastronomie`):** these are Immowelt's business real-estate ("Gewerbeimmobilien") categories — office/practice space, retail/shop space, storage/production/industrial space, and gastronomy & hospitality — available for both `mieten` and `kaufen`. `estateSubType` and `features` (both residential-only option sets) aren't applied for these; price, room count, and living-space filters still work normally wherever the listing discloses that data. `priceCold` for these categories reflects whatever net-rent label Immowelt discloses for the listing ("Nettomiete" for commercial vs. "Kaltmiete" for residential).

**Note on `estateType=landwirtschaft`:** Immowelt's separate category for agricultural and forestry land — farmland, forest tracts, riding/equestrian land, and agricultural businesses — available for both `mieten` (renting) and `kaufen` (buying), though rental inventory is much sparser than for-sale. This is a genuinely low-volume category (typically single digits to a couple dozen listings per city, vs. dozens to thousands for `wohnung`/`haus`) since farmland and forestry parcels are inherently rural — a search returning few or zero results for a given city is expected site behavior, not a scraper error. `estateSubType` and `features` aren't applied for it; price and area filters still work normally (area is reported as `plotSqm`, never `livingSpaceSqm`, and can run from a few hundred to several million m² for large forest tracts).

**Note on `features`:** unlike the other filters, a feature can't be double-checked against listing data (Immowelt doesn't expose it in the result payload), so it relies entirely on the site accepting it as a URL filter for the given property type — verified live across multiple German cities. `Balcony / terrace`, `Garden`, and `Furnished` are reliably supported for Apartment and House. `Parking space` is reliably supported for House only. `Fitted kitchen`, `Wheelchair accessible`, `WBS required`, `Shared flat`, `Swap apartment`, `Top floor`, and `Ground floor` are reliably supported for Apartment only. `Cellar / basement`, `Pool`, `Private offers only`, `New build`, and `Needs renovation` are not currently supported reliably by Immowelt for any property type. None of the features are supported for Plot / land or Agricultural / forestry land. When a requested feature isn't supported for the chosen property type, Immowelt silently ignores the filter and the run's status message will say the results are not filtered by that feature.

**Note on `sortBy`:** ordering is applied to the fetched page of listings (useful when `maxItems` is lower than the number of matches on that page); it does not change which ~30-40 listings Immowelt returns for the query.

#### Example: 2-room apartments for rent in Berlin, €500–€2000

```json
{
  "mode": "search",
  "city": "Berlin",
  "distributionType": "mieten",
  "estateType": "wohnung",
  "minRooms": 2,
  "maxRooms": 2,
  "minPrice": 500,
  "maxPrice": 2000,
  "maxItems": 20
}
```

#### Example: houses for sale in München

```json
{
  "mode": "search",
  "city": "München",
  "distributionType": "kaufen",
  "estateType": "haus",
  "maxItems": 20
}
```

#### Example: commercial building plots for sale in Berlin

```json
{
  "mode": "search",
  "city": "Berlin",
  "distributionType": "kaufen",
  "estateType": "grundstueck",
  "estateSubType": "commercial_land",
  "maxItems": 20
}
```

#### Example: office space for rent in Frankfurt am Main, €1000–€5000

```json
{
  "mode": "search",
  "city": "Frankfurt am Main",
  "distributionType": "mieten",
  "estateType": "buero",
  "minPrice": 1000,
  "maxPrice": 5000,
  "maxItems": 20
}
```

#### Example: furnished temporary-living rentals in Leipzig

```json
{
  "mode": "search",
  "city": "Leipzig",
  "estateType": "wohnen_auf_zeit",
  "minRooms": 1,
  "maxRooms": 3,
  "maxItems": 20
}
```

#### Example: agricultural / forestry land for sale near München

```json
{
  "mode": "search",
  "city": "München",
  "distributionType": "kaufen",
  "estateType": "landwirtschaft",
  "maxItems": 20
}
```

#### Example: scrape a specific Immowelt search URL

```json
{
  "mode": "byUrl",
  "searchUrl": "https://www.immowelt.de/suche/mieten/wohnung/berlin/berlin-10115/ad08de8634",
  "maxItems": 20
}
```

### FAQs

**Do I need an Immowelt account or API key?**
No. This actor reads only public search-results pages that require no login, cookies, or API key.

**Which cities are supported?**
Any German city or town that Immowelt indexes — just enter its name (umlauts like ä/ö/ü/ß are handled automatically).

**Why does the actor return fewer than `maxItems` records?**
Immowelt search-results pages surface up to ~30-40 listings per query. Narrow your city or widen your filters to see different listings; the `maxItems` cap reflects this page-1 limit.

**Can I get every filter combination Immowelt supports?**
Yes — use `mode=byUrl` with any URL built on immowelt.de's own filter UI, and the actor will extract structured data from it.

**Does this actor use proxies?**
No. Immowelt's public search pages are reachable directly, keeping runs fast and free of proxy costs.

**Why does `sourceUrl` (or `providerProfileUrl`, `providerRatingUrl`, `projectUrl`) sometimes show a verification page when I open it directly (e.g. via `curl`)?**
These are the real, correctly-formed links to the listing/agency-profile/project page on immowelt.de. Immowelt's bot-protection can show a one-time verification challenge to anonymous/automated direct requests to individual pages (this does not affect the search-results data this actor extracts). Opening the same link in a normal web browser works as expected.

# Actor input Schema

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

How to find listings.

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

German city or town name, e.g. `Berlin`, `München`, `Hamburg`, `Köln`, `Frankfurt am Main`.

## `distributionType` (type: `string`):

Whether to search rental or for-sale listings.

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

Broad property category to search. 'Temporary / furnished living (Wohnen auf Zeit)' is immowelt's furnished short-/mid-term rental category (spans apartments and houses) — it is rent-only, so Rent or buy is automatically switched to 'For rent' for it. The four commercial categories (Office, Retail, Storage / production, Gastronomy & hospitality) are immowelt's business real-estate ('Gewerbeimmobilien') categories. 'Agricultural / forestry land' is a separate, low-volume category (typically a handful of listings per city, since farmland/forestry parcels are rural) for both buying and (rarely) renting land- and forestry-use plots. Property sub-type / Additional features are not applied server-side for 'Temporary / furnished living', any commercial category, or 'Agricultural / forestry land' (price/rooms/area filters still apply).

## `estateSubType` (type: `string`):

Optional narrower property sub-type. Apartment sub-types only apply when Property type = Apartment; house sub-types only apply when Property type = House; plot sub-types only apply when Property type = Plot / land. Not applicable when Property type = Temporary / furnished living, any commercial category (Office, Retail, Storage / production, Gastronomy & hospitality), or Agricultural / forestry land.

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

Minimum monthly cold rent (mieten) or purchase price (kaufen) in EUR.

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

Maximum monthly cold rent (mieten) or purchase price (kaufen) in EUR.

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

Only listings with at least this many rooms. Set equal to Max rooms for an exact room count.

## `maxRooms` (type: `integer`):

Only listings with at most this many rooms.

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

Drop listings smaller than this, in square meters (living space for apartments/houses, usable floor area for commercial categories, plot size for Grundstück / Agricultural / forestry land — the latter can run into the millions of m² for large forest tracts).

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

Drop listings larger than this, in square meters (living space for apartments/houses, usable floor area for commercial categories, plot size for Grundstück / Agricultural / forestry land — the latter can run into the millions of m² for large forest tracts).

## `energyClass` (type: `array`):

Only keep listings with one of the selected German energy efficiency classes (A+ = most efficient, H = least efficient). Applied client-side against the energy class immowelt.de publishes for each listing — not every listing discloses one, so this filter drops listings with no disclosed energy class.

## `excludeGroundFloor` (type: `boolean`):

Skip apartments/houses located on the ground floor. Has no effect for property types that don't disclose a floor (Plot / land, commercial categories).

## `features` (type: `array`):

Extra amenity/feature to require (only one is applied per run — see note below, and the run's status message will say when the requested feature isn't filterable for the chosen property type). Verified reliable combinations: `Balcony / terrace`, `Garden`, and `Furnished` work for Apartment and House; `Parking space` only for House; `Fitted kitchen`, `Wheelchair accessible`, `WBS required`, `Shared flat (WG)`, `Swap apartment`, `Top floor`, and `Ground floor` only for Apartment. `Cellar / basement`, `Pool`, `Private offers only`, and `Needs renovation` are not reliably filterable by immowelt.de for any property type currently. None are supported for Plot / land, Temporary / furnished living, Agricultural / forestry land, or any commercial category.

## `sortBy` (type: `string`):

Re-order the returned listings. Immowelt only returns its first results page over plain HTTP, so this sorts within that page (useful when Max items is lower than the page size) rather than re-querying the whole site.

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

A full Immowelt search-results URL, e.g. `https://www.immowelt.de/suche/mieten/wohnung/berlin/berlin-10115/ad08de8634`. Build one on immowelt.de using any filter combination, then paste it here.

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

Hard cap on emitted records. Immowelt search-result pages return up to ~30-40 listings per query (varies by city/property type), so values above 40 have no additional effect.

## Actor input object example

```json
{
  "mode": "search",
  "city": "Berlin",
  "distributionType": "mieten",
  "estateType": "wohnung",
  "estateSubType": "",
  "energyClass": [],
  "excludeGroundFloor": false,
  "features": [],
  "sortBy": "",
  "maxItems": 20
}
```

# Actor output Schema

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

Dataset containing all scraped Immowelt real-estate listings.

# 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 = {
    "mode": "search",
    "city": "Berlin",
    "distributionType": "mieten",
    "estateType": "wohnung",
    "estateSubType": "",
    "energyClass": [],
    "excludeGroundFloor": false,
    "features": [],
    "sortBy": "",
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/immowelt-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 = {
    "mode": "search",
    "city": "Berlin",
    "distributionType": "mieten",
    "estateType": "wohnung",
    "estateSubType": "",
    "energyClass": [],
    "excludeGroundFloor": False,
    "features": [],
    "sortBy": "",
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/immowelt-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 '{
  "mode": "search",
  "city": "Berlin",
  "distributionType": "mieten",
  "estateType": "wohnung",
  "estateSubType": "",
  "energyClass": [],
  "excludeGroundFloor": false,
  "features": [],
  "sortBy": "",
  "maxItems": 20
}' |
apify call crawlerbros/immowelt-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/immowelt-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/A4e8UQ8CC5url0Sy6/builds/LGcfr99JUhDqF6mia/openapi.json
