# Immowelt.de Scraper - Rent & Buy Listings, New Listings Monitor (`aidnurs/immowelt-scraper`) Actor

Scrape immowelt.de apartments and houses for rent or sale by city: cold and warm rent, price, m², rooms, €/m², address, district, floor, energy class, agency, photos. Only-new mode for alerts. Duplicates are free.

- **URL**: https://apify.com/aidnurs/immowelt-scraper.md
- **Developed by:** [Nursultan Imanov](https://apify.com/aidnurs) (community)
- **Categories:** Real estate, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.90 / 1,000 listings

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

### What does Immowelt.de Scraper do?

**Immowelt.de Scraper** extracts real estate listings from [immowelt.de](https://www.immowelt.de), one of Germany's largest property portals. Enter a city, choose **rent or buy** and **apartments or houses**, and get clean, structured data: **cold rent and warm rent, purchase price, living area (m²), rooms, price per m², address, postal code, city and district, floor, energy class, building condition, agency name, photos, listing date** and a short description.

Use it as an **immowelt API** for rental market research, price per m² analysis, flat-hunting alerts, investment screening or lead lists of real estate agencies across German cities.

### Why use this immowelt scraper?

- 🔔 **Only new listings since the last run.** Turn on *Only new listings*, schedule the Actor (for example hourly) and get just the listings you have not seen yet. Ideal for flat alerts and market monitoring.
- 🏙️ **City and district coverage.** Each city page links its districts (Berlin: 25). The Actor reads them too, so one input like `Berlin` covers Mitte, Kreuzberg, Pankow, Charlottenburg and the rest.
- 💶 **Numbers, not text.** `"1.382 €"` becomes `1382.22`; cold rent, warm rent, living area, rooms and price per m² are separate numeric fields, ready for spreadsheets.
- 🎯 **Filters that work.** Min/max price, rooms and living area, agencies or private owners only.
- 🔁 **No duplicates charged.** A listing that appears in the city page and in a district page is saved and billed once.
- 🛡️ **Privacy by default.** Names of private owners and contact persons are never collected, private owners' street addresses are left out, phone numbers and e-mails in text are masked.
- ⚡ **Fast and cheap.** Plain HTTP, no browser: 1,000 listings in about 90 seconds.

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

You pay **$0.90 per 1,000 listings** saved to the dataset. Duplicates, filtered-out listings and searches with no results are free. Cap spending with *Max listings* and the run's maximum cost.

### How to scrape immowelt.de

1. Enter one or more **Locations** (`Berlin`, `München`, `Frankfurt am Main`, `Halle (Saale)`).
2. Choose **Rent or buy** and **Property type** (apartment or house).
3. Optionally set filters (price, rooms, living area, seller type) and **Max listings**.
4. For alerts: turn on **Only new listings**, give the search its own **Memory key** and add a schedule.
5. Click **Start** and download JSON, CSV or Excel, or use the Apify API.

You can also paste immowelt **search URLs** (`https://www.immowelt.de/suche/mieten/wohnung/berlin/berlin-10115/ad08de8634`, district or feature pages like `…/suche/mieten/wohnung/balkon-terrasse/…`).

**How much does one search return?** immowelt shows the first 30-40 listings of each search page to automated visitors; the next pages are reached through URLs that immowelt's robots.txt closes, and this Actor respects robots.txt. That is why it reads the city page plus every district page (and, optionally, the 1- to 6-room pages): a large city gives several hundred to over a thousand listings per run, and *Only new listings* picks up what changes between runs.

### Input example

```json
{
  "locations": ["Berlin", "Hamburg"],
  "dealType": "rent",
  "propertyType": "apartment",
  "maxItems": 500,
  "maxPrice": 1500,
  "minRooms": 2,
  "includeDistricts": true,
  "onlyNewListings": true,
  "stateKey": "berlin-hamburg-2rooms"
}
```

### Output example

```json
{
  "id": "2617G1NCXU87",
  "url": "https://www.immowelt.de/expose/43d4582e-affa-4ee4-9082-0146a35e8a0a",
  "title": "Wohnung zur Miete",
  "headline": "Traumhafter Erstbezug, Stilvolle Altbauperle mit hochwertiger Einbauküche & Vollausstattung",
  "descriptionSnippet": "DE - Möbliertes Altbau-Apartment (Erstbezug nach Kernsanierung) - Befristete Vermietung in Prenzlauer Berg ...",
  "dealType": "rent",
  "propertyType": "apartment",
  "price": 1062,
  "priceLabel": "Kaltmiete",
  "coldRent": 1062,
  "warmRent": 1290,
  "pricePerSqm": 26.55,
  "currency": "EUR",
  "livingArea": 40,
  "rooms": 1,
  "floor": 3,
  "floorText": "3. Geschoss",
  "street": "Krügerstraße 9",
  "postalCode": "10439",
  "city": "Berlin",
  "district": "Prenzlauer Berg",
  "state": "Berlin",
  "energyClass": "D",
  "buildingCondition": "FULLY_RENOVATED",
  "sellerType": "agency",
  "agencyName": "OhneMakler GmbH",
  "agencyProfileUrl": "https://www.immowelt.de/profil/5ba347d760714ccc97f0f7d38252a296",
  "agencyWebsite": "https://www.ohne-makler.net/",
  "isProject": false,
  "hasBrokerageFee": false,
  "isNew": true,
  "hasFloorPlan": true,
  "imageUrl": "https://mms.immowelt.de/3/1/f/9/31f9a479-19e0-44e3-92cb-815f15a0a958.jpg",
  "images": ["https://mms.immowelt.de/3/1/f/9/31f9a479-19e0-44e3-92cb-815f15a0a958.jpg", "..."],
  "imageCount": 27,
  "createdAt": "2026-10-07T09:48:08.187Z",
  "updatedAt": "2026-10-09T13:03:17.942Z",
  "source": "immowelt.de",
  "searchUrl": "https://www.immowelt.de/liste/berlin/wohnungen/mieten",
  "pageUrl": "https://www.immowelt.de/suche/mieten/wohnung/berlin/berlin-10115/ad08de8634",
  "scrapedAt": "2026-10-12T21:10:27.144825+00:00"
}
```

### Fields you get

| Field | Description |
|---|---|
| `id`, `legacyId`, `url` | immowelt listing ID and link to the listing |
| `title`, `headline`, `descriptionSnippet` | Listing type line, the agency's headline and the short description shown in results |
| `dealType`, `propertyType`, `isProject` | `rent` / `buy`, `apartment` / `house`, new-build project flag |
| `price`, `priceLabel` | Main price as a number: cold rent (Kaltmiete) for rentals, purchase price (Kaufpreis) for sales |
| `coldRent`, `warmRent` | Kaltmiete and Warmmiete in EUR (rentals) |
| `pricePerSqm` | EUR per m² (immowelt's own value for sales, calculated from price and area for rentals) |
| `livingArea`, `plotArea`, `rooms` | m², plot m² (houses), number of rooms |
| `floor`, `floorText`, `availableFrom` | Floor as a number (ground floor = 0) and as shown, availability |
| `street`, `postalCode`, `city`, `district`, `state` | Address of the property (street only when the agency published it) |
| `energyClass`, `buildingCondition` | Energy efficiency class (A+ to H) and condition (first-time use, renovated, ...) |
| `sellerType`, `agencyName`, `agencyProfileUrl`, `agencyWebsite` | Agency or private owner; agency (business) name and links |
| `hasBrokerageFee`, `isNew`, `hasFloorPlan`, `hasVirtualTour`, `hasVideo`, `labels` | Flags from the listing card |
| `imageUrl`, `images`, `imageCount` | Main photo, all photo URLs and their count |
| `createdAt`, `updatedAt` | When the listing was created and last updated on immowelt |
| `source`, `searchUrl`, `pageUrl`, `scrapedAt` | Where and when the record was collected |

### Known gaps

- **Year of construction** and the full description are only on the listing page itself, which immowelt protects with a captcha. This Actor never solves captchas, so those fields are not included; the results-page data above is complete for every listing.
- **Street** is empty when the agency did not publish the exact address, and always for private owners.
- **One page per search.** See "How much does one search return?" above.

### FAQ

**Is it legal to scrape immowelt?** The Actor reads only public search pages that immowelt's robots.txt allows, without logging in and without bypassing any protection. It does not collect names or phone numbers of private persons. You are responsible for using the data in line with GDPR and immowelt's terms.

**Do I need a proxy?** Usually not. If immowelt rate-limits the run (HTTP 429), the Actor switches to Apify Proxy when it is enabled in the input. If immowelt shows a captcha, the run stops and tells you, it does not try to get around it.

**How do I get only new listings?** Turn on *Only new listings* and schedule the Actor. Use a different *Memory key* for each saved search.

**Can I search postcodes or districts directly?** Paste the district's immowelt search URL into *Search URLs*, or use the city and keep *Include district pages* on.

**Something broke or a field is missing?** Open an issue on the Actor's Issues tab with the input you used. We usually reply within a day.

# Actor input Schema

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

German cities or towns to search, one per line ("Berlin", "München", "Frankfurt am Main"). Each city page also links its districts, which the Actor reads too (see below).

## `dealType` (type: `string`):

Listings for rent (Miete) or for sale (Kauf). Applies to 'Locations'.

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

Apartments (Wohnungen) or houses (Häuser). Applies to 'Locations'.

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

Optional: immowelt.de search pages such as https://www.immowelt.de/suche/mieten/wohnung/berlin/berlin-10115/ad08de8634 or a district / feature page (…/suche/mieten/wohnung/balkon-terrasse/…). Filtered result URLs with 'classified-search?' are closed by immowelt's robots.txt and are skipped: use the filters below instead.

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

Maximum number of listings to save in this run. You only pay for listings saved to the dataset.

## `includeDistricts` (type: `boolean`):

Also read the district pages linked from each city page (Berlin: 25 districts). Each search page shows up to 30 listings, so districts multiply coverage.

## `includeRoomVariants` (type: `boolean`):

Also read the 1- to 6-room pages linked from each city page for more coverage.

## `onlyNewListings` (type: `boolean`):

Remember the listings you already received and return only new ones. Schedule the Actor (e.g. hourly) for flat alerts.

## `stateKey` (type: `string`):

Separate memory per alert: use a different key for each saved search.

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

Applied by the Actor to the main price (cold rent or purchase price). Listings without a price are skipped when set.

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

Applied by the Actor to the main price (cold rent or purchase price).

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

Applied by the Actor.

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

Applied by the Actor.

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

Applied by the Actor.

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

Applied by the Actor.

## `sellerType` (type: `string`):

Agencies, private owners or both.

## `keepContactsInDescription` (type: `boolean`):

By default phone numbers and personal e-mails in the description snippet are masked. Enable only with a lawful basis (GDPR). Names of private owners and contact persons are never collected.

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

Usually not needed. If immowelt rate-limits the run (HTTP 429), the Actor switches to Apify Proxy when it is enabled here.

## Actor input object example

```json
{
  "locations": [
    "Berlin"
  ],
  "dealType": "rent",
  "propertyType": "apartment",
  "maxItems": 20,
  "includeDistricts": true,
  "includeRoomVariants": false,
  "onlyNewListings": false,
  "stateKey": "default",
  "sellerType": "all",
  "keepContactsInDescription": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Scraped listings (table view).

## `listingsFull` (type: `string`):

All fields including photos, description snippet and agency.

# 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 = {
    "locations": [
        "Berlin"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("aidnurs/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 = {
    "locations": ["Berlin"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("aidnurs/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 '{
  "locations": [
    "Berlin"
  ],
  "maxItems": 20
}' |
apify call aidnurs/immowelt-scraper --silent --output-dataset

```

## MCP server setup

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