# Kleinanzeigen Immobilien Scraper (`nice_dev/kleinanzeigen-immobilien-scraper`) Actor

Scrape real-estate listings from Kleinanzeigen.de: apartments and houses to rent or buy, shared flats, plots, commercial. Cold and warm rent, costs, deposit, area, rooms, floor, year, amenities, agency and phone. Export JSON, CSV or Excel.

- **URL**: https://apify.com/nice\_dev/kleinanzeigen-immobilien-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Real estate, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.32 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 is Kleinanzeigen Immobilien Scraper?

**Kleinanzeigen Immobilien Scraper** extracts the **real-estate listings of [Kleinanzeigen.de](https://www.kleinanzeigen.de)** (formerly eBay Kleinanzeigen) — **apartments and houses to rent or to buy, shared flats and temporary rentals, plots, commercial properties, holiday homes, garages** — as clean, flat rows: **cold rent, warm rent, additional and heating costs, deposit, purchase price, price per m², living area, rooms, floor, construction year, availability, amenities, energy class, GPS position, agency, and the seller's name and phone number** when published.

Pick a **property type** and a **city**, set the filters you need (area, rooms, floor, year, balcony…), or paste a Kleinanzeigen search URL, click **Start**, and download the listings in JSON, CSV or Excel. No login, nothing to set up: every real-estate field is already on the results page, so a list-only run reads **100 listings per request**.

### 📋 What data can you extract from Kleinanzeigen Immobilien?

One item per listing, 126 fields:

| Category | What you get |
| --- | --- |
| 🏠 **Property** | property type (apartment, house, shared flat, plot, commercial…), rent or buy, apartment / house / commercial / plot type — `apartment-rent`, `erdgeschosswohnung` |
| 📐 **Size and layout** | living area, floor area, plot area, rooms, bedrooms, bathrooms, floor, number of floors — `100 m², 3 rooms, ground floor` |
| 💶 **Rent and price** | rent, cold rent (Kaltmiete), warm rent (Warmmiete), additional costs, heating costs, deposit, housing fee (Hausgeld), purchase price, price per m², commission or not — `550 € cold, 800 € warm, 1,500 € deposit` |
| 🛋️ **Amenities** | balcony, terrace, garden, cellar, built-in kitchen, parking, elevator, guest WC, furnished, pets allowed, step-free, floor heating, old / new building… as yes / no columns and as a list |
| ⚡ **Building and energy** | construction year, available from, energy class, heating type, energy source (read in the description) — `1998`, `2026-10`, `C`, `Fernwärme` |
| 👥 **Shared flats** | room or whole place, fixed-term or open-ended, flatmates, smoking |
| 📍 **Location** | town (even where the site shows the district), district, postcode, federal state, street when given, GPS position — `Hilchenbach, 57271` |
| 👤 **Seller and agency** | private or commercial, agency name and logo, name, member since, rating and badges, link to all their listings |
| ☎️ **Contacts** | phone number, e-mail and legal notice (imprint) of agencies, when published |
| 📝 **Listing** | title, link, full description, every photo, PDFs, publication time to the second, days online, last edit, top ad, labels |
| 👁️ **Views** | how many times the listing was viewed (option) |
| 🔎 **Search** | which search found the listing and at which position, when it was read, the site's untouched data (option) |

Every field, with an example, is listed in the **Output** section below.

Fields marked **(detail)** in the Output tab (`description` in full, `sellerName`, `sellerPhone`, `sellerEmail`, `sellerImprint`, `street`, `offerPossible`, `securePaymentPossible`, seller badges, `lastEditedAt`, `documents`, `energyClass`, `heatingType`, `energySource`) are filled only when **Extract details** is on (off by default). Every real-estate field is on the results page: leave details off for a much faster, cheaper run, and tick it when you need the seller's contact or the full description.

### ✅ Why use Kleinanzeigen Immobilien Scraper?

- 🏘️ **All of Immobilien**: the 11 property types of the site in one run — rentals, sales, shared flats, plots, commercial, holiday homes, garages, containers — or just the ones you pick.
- 💶 **The rent split, as columns**: cold rent, warm rent, additional costs, heating costs, deposit, housing fee — plus the price per m², ready for a spreadsheet.
- 🎛️ **Filters sent to the site**: area, rooms, plot, floor, construction year, availability, apartment / house / commercial / plot type, 26 amenities, commission-free, price, radius, private or commercial — a listing they drop is never downloaded.
- 📚 **Deep**: up to **10,000 listings per search**, eight times what the website itself pages through.
- ☎️ **Agency contacts**: the phone number the seller published, the e-mail and legal notice of agencies, their name, rating and badges.
- 🔔 **Monitoring built in**: tick **Only new listings**, schedule the Actor, and each run returns (and charges) only what it has never delivered before — the publication time is exact to the second.
- 🗂️ **Several searches in one run**: property types × keywords × cities, agencies to follow, single listing ids — each with its own cap.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Kleinanzeigen Immobilien

1. Create a free Apify account.
2. Open **Kleinanzeigen Immobilien Scraper** and choose one or more **Property types** (e.g. Apartments for rent).
3. Type a **Location** (`Berlin`, `10115`) and a **Radius**, and the filters you need: rent or buy, area, rooms, amenities, price.
4. Or paste your own Kleinanzeigen real-estate URLs into **Start URLs**: any search results page (the filters and the sort written in the URL are kept, pagination is automatic), a seller page, or single listing pages.
5. Set **Max listings** (100 by default, 0 = no limit) — and **Max listings per search** when you run several searches — then click **Start** and download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Kleinanzeigen Immobilien?

This Actor uses **pay per event** pricing: you pay for each listing, and for each option only when you turn it on — except that a listing you ask for by id or by URL is always read on its own page, so it is charged Details too.

Prices per 1,000 events, by Apify subscription plan:

| Event | Free | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| Listing (every row of the dataset) | **$1.35** | $1.34 | $1.33 | $1.32 |
| + Details: the listing's own page (full description, seller name, phone, e-mail and imprint, badges) — only when **Extract details** is ticked | $0.63 | $0.54 | $0.45 | $0.44 |
| + View count (`includeViewCount`) | $0.09 | $0.09 | $0.08 | $0.08 |
| Filtered listing: a listing's own page read, then dropped by **With phone number only** (`requirePhone`) | $0.18 | $0.17 | $0.16 | $0.15 |
| Filter check: a listing checked on its results page by a filter of this Actor (dates, excluded keywords, minimum photos, price types, maximum warm rent, a property filter the site ignores), kept or not | $0.05 | $0.04 | $0.03 | $0.02 |

Plus **$0.005 per run start** (50 cents per 100 runs), whatever the plan.

- 1,000 listings, list only (default, details off) on the Free plan ≈ **$1.35**; with **Extract details** on ≈ **$1.98** ($1.35 + $0.63).
- A daily monitor of 100 new listings with details ≈ **$0.20** a day on the Free plan.

An option is charged only on the listings that got it (a view counter that failed is not charged). Each listing checked by one of the Actor's own filters is charged as a *Filter check*, kept or not; the property filters the site applies itself (area, rooms, amenities… — see **Property filters**) cost nothing extra. With **With phone number only** (`requirePhone`) the phone is only on the listing's own page, so each page read and then dropped for lack of a phone is charged as a *Filtered listing*. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

```json
{
    "propertyTypes": ["apartment-rent"],
    "location": "Berlin",
    "radiusKm": 10,
    "minArea": 50,
    "minRooms": 2,
    "maxPrice": 1500,
    "amenities": ["balcony", "builtInKitchen"],
    "maxItems": 200
}
```

Houses and apartments for sale in several cities, a cap per search, only the new ones:

```json
{
    "propertyTypes": ["apartment-buy", "house-buy"],
    "locations": ["Berlin", "Hamburg"],
    "minConstructionYear": 2000,
    "noCommission": true,
    "maxItemsPerQuery": 50,
    "postedAfter": "24 hours",
    "onlyNew": true,
    "stateKey": "buy-berlin-hamburg"
}
```

Everything an agency lists, with its phone number:

```json
{
    "sellerIds": ["123745993"],
    "sellerType": "COMMERCIAL",
    "extractDetails": true,
    "requirePhone": true,
    "maxItems": 300
}
```

Or with your own URLs and single listings:

```json
{
    "startUrls": [
        { "url": "https://www.kleinanzeigen.de/s-wohnung-mieten/berlin/c203l3331" },
        { "url": "https://www.kleinanzeigen.de/s-bestandsliste.html?userId=123745993" },
        { "url": "https://www.kleinanzeigen.de/s-anzeige/wohnung-zu-vermieten/3524110453-203-1468" }
    ],
    "listingIds": ["3524096620"],
    "maxItems": 500
}
```

| Field | Notes |
| --- | --- |
| `propertyTypes` | `apartment-rent`, `apartment-buy`, `house-rent`, `house-buy`, `shared-temporary` (Auf Zeit & WG), `land-garden`, `commercial`, `holiday-abroad`, `garage-parking`, `container`, `other`. One search per type; empty = all 11. |
| `offerType` | `ANY`, `RENT` or `BUY`: the types of the other deal are skipped; plots, commercial, holiday homes, garages and containers are filtered by the site. |
| `query`, `searchQueries` | Free-text search inside the property types (`altbau`, `balkon`); `searchQueries` adds more keywords (one search each). Ignored when `startUrls` is set. |
| `location`, `locations` | City, district or postcode (`Berlin`, `10115`), or the site's own id (`l3331`); `locations` adds more places. Every keyword is searched in every location for every type (max 500 searches per run for each type). |
| `radiusKm` | Kilometres around each location; ignored without a location. |
| `sellerIds`, `storeIds` | Every real-estate listing of these sellers (`123745993`) or agency shops; each one is a search of its own, per property type. |
| `listingIds` | Fetch single listings by id (`3524096620`) or URL, whatever the searches. Each is read on its own page: charged Details even with `extractDetails` off. |
| `startUrls` | Real-estate search results pages (the filters and the sort in the URL are kept, pagination is automatic), seller pages or single listing pages; a search outside Immobilien is refused. When set, the search fields and the site and property filters (`propertyTypes`, `offerType`, `location`, `sellerIds`, `storeIds`, `radiusKm`, `minPrice`, `maxPrice`, `sellerType`, `adType`, `pictureRequired`, `sortBy`, `attributeFilters`, `minArea` … `noCommission`) are ignored, and the log says which: set them on the site before copying the URL. |
| `extractDetails` | Open each listing for the full description, the seller's name, phone and imprint (default off; `requirePhone` needs it on). |
| `maxItems`, `maxItemsPerQuery` | Stop after this many listings for the whole run (`0` = unlimited) / for EACH search. |
| `minPrice`, `maxPrice` | Price range in euros: the cold rent of apartments and houses to rent, the warm rent of shared flats, the purchase price of sales. Either one set, even `0`, drops the listings that show no amount. |
| `sellerType` | `ANY`, `PRIVATE` or `COMMERCIAL`. |
| `adType` | `ANY`, offers (`OFFERED`) or wanted ads, the site's Gesuche (`WANTED`). |
| `pictureRequired` | With a photo only. |
| `sortBy` | Order of the results: `DATE_DESCENDING`, `PRICE_ASCENDING`, `PRICE_DESCENDING`, `DISTANCE_ASCENDING`. |
| `attributeFilters` | Any other filter of the site, as an object of attribute names and values (`wohnung_mieten.swap`: `nein`); the names are those of `attributeList` in the output. |
| `minRooms`, `maxRooms`, `minArea`, `maxArea` | Rooms, half rooms as `2.5`; living area of apartments, houses and rooms (floor area of commercial and containers), in m². |
| `minPlotArea`, `maxPlotArea`, `minFloor`, `maxFloor`, `minConstructionYear`, `maxConstructionYear` | Plot of a house or a plot / garden (m²); floor (`0` = ground floor); year built. |
| `availableFrom`, `availableTo` | Available from (Verfügbar ab) between these months, written `2026-11`. |
| `apartmentType` | Apartments only, one type (`penthouse`, `maisonette`, `dachgeschosswohnung`…); other property types are skipped. |
| `houseType` | Houses only (`einfamilienhaus`, `reihenhaus`, `villa`…). |
| `commercialType` | Commercial only (`bueros_praxen`, `gastronomie_hotels`…). |
| `plotType` | Plots and gardens only (`baugrundstueck`, `garten`…). |
| `accommodationType` | Auf Zeit & WG only: `entire_accommodation`, `private_room` or `shared_room`. |
| `amenities`, `noCommission` | Listings with ALL these amenities (`balcony`, `elevator`, `petsAllowed`…); commission-free only. A property type that cannot have one is skipped. |
| `postedAfter`, `postedBefore` | Publication date range: `2026-09-01`, or a period before now (`24 hours`, `7 days`, `2 weeks`, `1 month`). |
| `excludeKeywords`, `maxWarmRent`, `requirePhone`, `minImages` | Drop the listings whose title or description holds one of these words (also inside a longer word); drop the rentals whose warm rent is unknown or above this amount (sales are kept), keep only the listings with a phone number, or with at least N photos. |
| `priceTypes` | Keep only some kinds of price: `SPECIFIED_AMOUNT` (fixed), `PLEASE_CONTACT` (negotiable, VB), `FREE` (Zu verschenken). |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the listings never delivered under this memory key; `resetState` forgets the memory. |
| `includeViewCount`, `includeRaw` | Add the number of views of each listing (1 extra request per listing, and it counts as one visit); add the untouched site data. |
| Advanced | `proxyConfiguration` (Apify proxy by default, included in the price; the residential proxy is not available), `maxConcurrency`, `maxRequestsPerMinute` (with `includeViewCount`, a listing page and its view counter count as one request), `maxRequestRetries`, `debugLog`. |

### 📦 Output

One real item of a run, shortened (126 fields in the dataset):

```json
{
    "id": "3524110453",
    "url": "https://www.kleinanzeigen.de/s-anzeige/wohnung-zu-vermieten/3524110453-203-1468",
    "title": "Wohnung zu vermieten",
    "price": 550,
    "currency": "EUR",
    "priceType": "SPECIFIED_AMOUNT",
    "publishedAt": "2026-09-26T20:29:22.000Z",
    "category": "Mietwohnungen",
    "city": "Hilchenbach",
    "postalCode": "57271",
    "latitude": 51.001842,
    "longitude": 8.119404,
    "sellerName": "Gabriel",
    "sellerType": "PRIVATE",
    "images": [
        "https://img.kleinanzeigen.de/api/v1/prod-ads/images/c4/c4078f3c-3812-4c83-a44f-49edc18b8b33?rule=$_57.JPG"
    ],
    "imageCount": 13,
    "propertyType": "apartment-rent",
    "offerType": "rent",
    "livingArea": 100,
    "rooms": 3,
    "bedrooms": 2,
    "bathrooms": 1,
    "floor": 0,
    "apartmentType": "erdgeschosswohnung",
    "availableFrom": "2026-10",
    "rent": 550,
    "coldRent": 550,
    "warmRent": 800,
    "additionalCosts": 150,
    "heatingCosts": 100,
    "deposit": 1500,
    "pricePerSqm": 5.5,
    "amenities": [
        "balcony",
        "terrace",
        "garden",
        "cellar",
        "builtInKitchen",
        "parking",
        "furnished"
    ],
    "hasBalcony": true,
    "hasElevator": false,
    "isFurnished": true,
    "companyName": null,
    "daysOnline": 0,
    "searchRank": 12,
    "scrapedAt": "2026-09-26T20:36:39.472Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 126 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `title` | `3524110453`, `https://www.kleinanzeigen.de/s-anzeige/...`, `Wohnung zu vermieten` |
| `description`, `descriptionHtml`, `descriptionFormat`, `isDescriptionTruncated` | full text, the seller's own line breaks, `text/markdown`, `false` |
| `price`, `currency`, `priceType`, `priceText`, `isNegotiable` | `550`, `EUR`, `SPECIFIED_AMOUNT` / `PLEASE_CONTACT` / `FREE`, `550 €`, `false` |
| `adType`, `status`, `isTopAd`, `features`, `labels` | `OFFERED`, `ACTIVE`, `false`, `["TOPAD"]`, `["Von Privat"]` |
| `publishedAt`, `lastEditedAt`, `daysOnline` | `2026-09-26T20:29:22.000Z` (exact to the second — what the date filters read), `null`, `0` |
| `categoryId`, `category`, `categorySlug`, `parentCategoryId` | `203`, `Mietwohnungen`, `Wohnung_mieten`, `195` |
| `location`, `city`, `district`, `postalCode`, `state`, `street` | `57271 Hilchenbach`, `Hilchenbach` (the town, even where the site prints the district), the district of a big city, `57271`, `Nordrhein-Westfalen`, the street when given |
| `latitude`, `longitude`, `locationRadiusKm`, `locationId` | `51.001842`, `8.119404`, `8.53`, `1468` |
| `sellerId`, `sellerName`, `sellerType`, `sellerAccountType` | `54151128`, `Gabriel`, `PRIVATE`, `PRIVATE` / `COMMERCIAL` |
| `sellerPhone`, `sellerEmail`, `sellerImprint`, `sellerProfileUrl` | the phone number the seller published, the e-mail of an agency's legal notice, that legal notice, the page listing all their listings |
| `sellerActiveSince`, `sellerRating`, `sellerBadges` | `2022-10-21T13:55:03.000Z`, `0.978`, `[{ "name": "rating", "level": 2 }]` |
| `sellerRatingLevel`, `sellerFriendlinessLevel`, `sellerReliabilityLevel` | `2`, `3`, `1` |
| `storeId`, `storeTitle`, `storeUrl`, `companyName`, `companyLogoUrl` | an agency's shop number, name and link; the agency shown on the listing and its logo |
| `images`, `imageUrl`, `imageCount`, `documents` | photo URLs in full size, the first one, `13`, PDFs attached by the seller (floor plans, exposés) |
| `attributes`, `attributeList` | every field the seller filled in, as the site labels it (`{ "Wohnfläche": "100 m²" }`), and the same with machine names, units and types |
| `propertyType`, `offerType` | `apartment-rent`, `apartment-buy`, `house-rent`, `house-buy`, `shared-temporary`, `land-garden`, `commercial`, `holiday-abroad`, `garage-parking`, `container`, `other`; `rent` / `buy` |
| `livingArea`, `usableArea`, `plotArea` | `100` (m²), the floor area of a commercial property or a container, the plot of a house or the size of a plot / garden |
| `rooms`, `bedrooms`, `bathrooms`, `floor`, `numberOfFloors` | `3`, `2`, `1`, `0` (ground floor), `2` |
| `apartmentType`, `houseType`, `commercialType`, `plotType`, `parkingType` | the site's values: `erdgeschosswohnung`, `einfamilienhaus`, `bueros_praxen`, `baugrundstueck`, `type_garage` |
| `accommodationType`, `rentalPeriod`, `flatmates`, `smoking` | shared flats: `private_room`, `unbefristet`, `2`, `unerwuenscht` |
| `constructionYear`, `availableFrom` | `1998`, `2026-10` (or a year alone) |
| `energyClass`, `heatingType`, `energySource` | `C`, `Zentralheizung`, `Gas` — read in the full description, with details (the site has no field for them; sale ads state them more often than rentals) |
| `rent`, `coldRent`, `warmRent` | `550` (the rent of a rental offer), `550` (Kaltmiete of apartments and houses), `800` (Warmmiete, as the seller typed it; the price of a shared flat) |
| `additionalCosts`, `heatingCosts`, `deposit`, `housingFee`, `totalRent` | `150`, `100`, `1500`, the Hausgeld of an owner-occupied apartment, the total rent of a commercial property (euros) |
| `purchasePrice`, `pricePerSqm`, `commission` | the price of a sale, `5.5` (the site's price per m², else price ÷ area), `true` / `false` (broker's commission) |
| `swapOffer`, `onlineViewing` | tenant swap offer, online viewing offered |
| `amenities` | `["balcony", "cellar", "builtInKitchen"]` — every equipment ticked |
| `hasBalcony`, `hasTerrace`, `hasGarden`, `hasCellar`, `hasBuiltInKitchen`, `hasParking`, `hasElevator`, `hasGuestWc`, `hasBathtub` | `true` = ticked, `false` = not ticked, `null` = the property type has no such field |
| `isFurnished`, `petsAllowed`, `isBarrierFree`, `hasFloorHeating`, `isOldBuilding`, `isNewBuilding`, `isWgSuitable`, `hasAttic`, `isHistoricBuilding`, `isCurrentlyRented`, `hasGrannyFlat` | same rule |
| `securePaymentPossible`, `offerPossible` | `false`, `false` |
| `viewCount` | `32560` (only with **View count**) |
| `searchUrl`, `searchQuery`, `searchLocation`, `searchRank` | which search returned the listing, and at which position |
| `raw`, `scrapedAt` | the untouched site data (only with **Include raw data**), ISO timestamp |

### 💡 Tips

#### How to get more results

One search returns at most 10,000 listings (the site's own limit). Beyond that, split it: by property type, by city (`locations`), or by price band (`minPrice` / `maxPrice`) — every piece is a search of its own with its own cap. Set `maxItems` to `0` to take everything a search has.

#### How to reduce costs

The price is per listing, so the levers are `maxItems`, `maxItemsPerQuery`, the filters sent to the site with the search (price, radius, seller type, property filters: a listing they drop is never downloaded — the Actor's own filters are charged per listing checked) and `onlyNew` for recurring runs (you never pay twice for the same listing). Leave `extractDetails` off (the default) when the results page is enough — every real-estate field, the price, the photos, the location and the agency are already there: the run is then much faster, with no Details charge. Tick it only for the seller's phone, e-mail and imprint or the full description. Leave `includeViewCount` off unless you need the number of views.

#### Several searches in one run

Fill `propertyTypes`, `searchQueries` and / or `locations`: the Actor runs one search per property type × keyword × city (2 types × 3 cities = 6 searches, up to 500 per run for each type). The single `query` and `location` fields still work and are added to the lists. Sellers (`sellerIds`) and shops (`storeIds`) are searches too, per property type, and the keyword and the filters still apply to them. A listing found by several searches is saved — and charged — once. Set `maxItemsPerQuery` to give every search its own cap.

#### Property filters

Area, rooms, plot, floor, year, availability, the types, the amenities and commission-free are sent to the site as the filters of each property type. A type that cannot have the field is skipped, and the log says so (rooms of a plot, a balcony on a garage); if no type is left, the run stops at once with the reason. Where the site ignores a filter — the plot and the year of houses for rent, the floor and the year of commercial properties, the year of containers — the Actor checks it on each listing instead (a listing without the value is dropped), and each listing checked is charged as a *Filter check*. `maxWarmRent` is always checked that way: the site does not filter the warm rent. The rentals of plots, commercial properties, holiday homes, garages and containers never give one: with `maxWarmRent`, only their sales are searched (none at all with `offerType` `RENT`), and the log says so.

#### Monitoring: only the new listings

Tick **Only new listings** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run skips the listings already delivered: they are not saved, not charged, and their page is not even opened. The memory lives in a named key-value store of your account (`kleinanzeigen-immobilien-scraper-seen`, up to 150,000 listings per key) and is only updated with listings that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's listings), and tick `resetState` once to start over. With the default sort (newest first), a search stops once it meets 500 listings in a row you already have (paid top ads pinned above the sort are not counted); another sort reads the search to its end or to your caps.

#### Filter by publication date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included, German time zone) or a period before now (`24 hours`, `7 days`, `2 weeks`, `1 month`; via the API also a full ISO date-time). The site gives the publication time to the second, so an hourly run with `postedAfter: "1 hour"` returns exactly the new listings. Each listing the date filter checks is charged as a *Filter check*, kept or not; filtered-out listings are not saved and do not count in `maxItems`; the run summary tells how many were filtered. With `postedAfter` and the default sort (newest first), a search stops at the first page that is entirely too old; another sort reads on, up to your caps, keeping only the recent listings.

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per real-estate listing of Kleinanzeigen.de. Actor id: `nice_dev/kleinanzeigen-immobilien-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/kleinanzeigen-immobilien-scraper`.

Smallest input, for a cheap first call:

```json
{
    "propertyTypes": ["apartment-rent"],
    "location": "Berlin",
    "maxItems": 10
}
```

Key output fields: `url`, `title`, `propertyType`, `coldRent`, `warmRent`, `purchasePrice`, `livingArea`, `rooms`, `city`, `sellerPhone`.

Cost: per listing, plus $0.005 per run start; the details (option **Extract details**, off by default), the view count and the filters cost extra, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Kleinanzeigen.de?

The Actor only reads what Kleinanzeigen.de shows publicly to any anonymous visitor. It logs in to nothing. Results can contain personal data — a landlord's or seller's name, and the phone number or imprint they chose to publish with their listing — which is protected by GDPR: do not store it without a legitimate reason, and do not use it for unsolicited marketing. You are responsible for using the data in compliance with Kleinanzeigen's Terms of Use and applicable law. This Actor is not affiliated with Kleinanzeigen.de or eBay.

#### Does it need a login or a proxy?

No login. The proxy is included in the price: leave the default setting (the residential proxy is not available). A request the site turns away is retried at once on a new proxy session (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds). Behind the Apify proxy, a request turned away on 3 proxy IPs (8 for the lookups made before the crawl) is then sent without proxy, and the run's log says so; with your own proxies in **Proxy configuration**, every request goes through them.

#### Why is the warm rent sometimes lower than the cold rent, or missing?

The seller types the warm rent, the costs and the deposit in fields of their own, and the site does not check them: the Actor gives them as typed, never recomputed. About 7 apartments to rent in 10 give a warm rent, 1 house to rent in 3; the rentals of plots, commercial properties, holiday homes, garages and containers never do. The price of an apartment or a house to rent is its cold rent (Kaltmiete); the price of a shared flat or a temporary rental is its warm rent — the site's own labels.

#### Why am I charged for listings I did not get with `requirePhone`?

A phone number is only on the listing's own page, so **With phone number only** reads that page before it knows whether the seller published one. Each page read and then dropped is charged as a *Filtered listing*: the search is paid even when it finds few listings. Never charged as a *Filtered listing*: a listing dropped on its results page (another filter, a date — a *Filter check* there), a listing a previous run already delivered (`onlyNew`), a page that failed or a listing removed meanwhile, and never twice the same listing (a retry, a resurrected run). The run stops after 300 pages in a row without a phone — private landlords rarely publish one — and never goes past your maximum cost: choose **Seller type: Commercial sellers only** to get far more phone numbers per page read.

#### Is the data safe to open in Excel or to show on a web page?

Titles and descriptions are the sellers' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a phone number, a title such as `-20% Provision`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. Every URL field holds an http(s) URL or `null`; `descriptionHtml` holds the seller's own markup, which the Actor does not sanitize. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- One search returns at most 10,000 listings: split it (see Tips) to go further.
- `description` in full, `sellerName`, `sellerPhone`, `sellerEmail`, `sellerImprint`, `street`, `offerPossible`, `securePaymentPossible` and the seller badges come from the listing's own page: they are empty (`null`, `[]`, the short preview for `description`) when `extractDetails` is off. So are `energyClass`, `heatingType` and `energySource`, read in the full description.
- A phone number is only there when the seller published one with the listing.
- A shop URL (`/pro/...`) carries no id: use the `storeId` a previous run returned.
- No price history and no removed listings: the site only shows the listings online today, with their current price.
- `onlyNew` remembers listing ids, not their content: a listing whose price changed is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new listing.
- `includeViewCount` asks the site for the counter of each listing, which counts as one visit for the seller, exactly like opening the page in a browser.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". In list-only mode with `includeViewCount`, the listings whose counter was not read yet are saved without it. Resurrect the run to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the listings already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved once a minute: resurrect the stopped run and the listings it had saved meanwhile join the memory; leave it stopped for good, and the next run may return up to a minute of them once more.

#### Something doesn't work?

The last line of the log counts the listings saved, filtered out and no longer on Kleinanzeigen.de (removed while the run was reading them), and the requests that failed after every retry. Those requests and the removed listings are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause (a search URL that does not exist says so, instead of "run it again"). A run that saved some listings fails too when at least as many requests failed for good as were read (page 1 read, the next pages or the listing pages blocked), or — with `includeViewCount` — when at least as many view counters failed as were read (most rows would lack the view count you asked for): a green run with a short dataset would hide the outage. One failed request among many is only a warning.

If Kleinanzeigen.de changes its pages, you are told instead of paying for blank rows. A results page that counts listings but gives none the Actor can read, or a listing page that comes back empty, is an error (retried, then listed in `FAILED_REQUESTS`), never a quiet "No listings found". If the first 20 listings read all lack their title, publication date, seller id, seller type, postcode, place id, category, the amount of a price typed as an amount, the type of a price that has an amount or — with `extractDetails` — description, seller name or the imprint of a professional seller — or a property column every listing of its type gives (living area of flats, houses and shared flats; rooms, except houses for sale; plot area and plot type; rent or buy where the category mixes both; rental period of temporary and shared flats; parking type) — the run saves nothing more, stops and fails, and its last message names the missing field: at most those first listings are charged. A listing that a filter drops because it lacks that field (`postedAfter` / `postedBefore` for the date, `priceTypes` for the price type) counts among those 20. The amenities, types, commission-free and rent or buy sent to the site are on every listing it answers: if none of the first 20 listings of a property type carries one of them (the site renamed it, or writes it another way), the run stops and fails the same way, naming it, instead of dropping every listing for a green run at 0. A run of fewer listings (a scheduled `onlyNew` run that finds a few new ones, a small **Max listings**) is checked at its end, from 5 listings: its listings are already saved, and the run fails with the missing field instead of ending green. If the site refuses the access of its app (HTTP 401), the run fails before reading anything and says so: running it again will not help, please report it.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which URLs failed and why.

# Actor input Schema

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

Kleinanzeigen real-estate search URLs (`https://www.kleinanzeigen.de/s-wohnung-mieten/berlin/c203l3331`, pagination is automatic), seller pages (`https://www.kleinanzeigen.de/s-bestandsliste.html?userId=123745993`) or single listing URLs. A search URL outside the Immobilien section is refused; one without a category searches all of Immobilien. A pasted search keeps the search, the filters and the sort written in its URL: when this list is not empty, the search fields below (property types, keyword, location, sellers, shops) AND the **Site filters** are ignored, like the **Property filters** (the log names them) — set those on the site before copying the URL. The other filters, the caps and monitoring still apply. Max 1 000 URLs.

## `propertyTypes` (type: `array`):

Which part of Kleinanzeigen's Immobilien section to search: one search per type (times each keyword and location below). Empty = all 11 types (the site's moving / transport services are never included).

## `offerType` (type: `string`):

Rentals only or sales only. Apartments and houses have a rent and a buy type of their own (the other one is skipped, the log says so); plots, commercial, holiday homes, garages and containers are filtered by the site on the seller's Art field. Other real estate has no such field and is skipped when this is set.

## `query` (type: `string`):

Free-text search inside the chosen property types, as typed on the site (e.g. `altbau`, `balkon`). Empty = every listing.

## `searchQueries` (type: `array`):

Several keywords in one run: one search per keyword (times each location below). Added to **Search keyword**; listings found by several searches are saved once.

## `location` (type: `string`):

City, district or postcode as typed on the site (e.g. `Berlin`, `10115`), or the site's own id (`l3331`). A name several places share gives the place of that name, the biggest one if several (`Hagen` = Hagen in NRW, not Hagen im Bremischen; `Frankfurt` = Frankfurt am Main; the log names the others — type the full name, `Frankfurt (Oder)`, to choose another). Empty = whole Germany.

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

Several locations in one run: every keyword is searched in every location (3 keywords × 4 locations = 12 searches for each property type, max 500). Added to **Location**.

## `radiusKm` (type: `integer`):

Search radius around each location. 0 = the location only (no surroundings). Ignored when no location is given.

## `sellerIds` (type: `array`):

Every real-estate ad of these sellers (an agency, a landlord): numeric user id, seller-page URL or listing URL of the seller. Each seller counts as one search per property type; keyword and filters still apply to it.

## `storeIds` (type: `array`):

Every real-estate ad of these commercial shops (agencies with a Kleinanzeigen shop): the numeric shop id this Actor returns in the storeId column. Same rules as seller ids.

## `listingIds` (type: `array`):

Fetch these listings directly, by id (`3516612023`) or by URL. Always added to the searches above; each id costs one request and is not affected by the site and property filters. Such a listing is always read on its own page, so it is charged the **Extract details** price even when that box is off.

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

Maximum number of listings to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH search (keyword × location, seller, shop or search URL), so that the first search cannot use up the whole **Max listings** budget. 0 = no per-search cap. The site itself never returns more than 10 000 listings per search.

## `extractDetails` (type: `boolean`):

Open each listing (1 extra request per listing) for the full description, the seller's name, registration date, rating and badges, the agency imprint and e-mail, the street, and the phone number when the seller published one. Every real-estate field (area, rooms, rents, amenities…) is already on the results page: off (the default) = all of them, ≈ 100 listings per request, much faster and cheaper (no Details charge). **With phone number only** needs it on.

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

Lowest price: the cold rent (Kaltmiete) for apartments and houses to rent, the warm rent for Auf Zeit & WG, the purchase price for sales. With **Min price** or **Max price** set, even `0`, the site drops the listings that show no amount.

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

Highest price (same meaning as **Min price**). Set, it drops the listings that show no amount too.

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

Keep private sellers only, commercial sellers only, or both.

## `adType` (type: `string`):

Offers, the site's Angebote — or wanted ads, its Gesuche — or both.

## `pictureRequired` (type: `boolean`):

Keep only the listings that have at least one photo.

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

Order in which the site returns the listings. Newest first is the default and the one used to stop early on **Posted after** / **Only new listings**.

## `attributeFilters` (type: `object`):

Advanced: any other attribute filter of the site, as `attribute: value` — the `name` of an entry of `attributeList` in the output (e.g. `wohnung_mieten.swap`: `nein`, `auf_zeit_wg.rauchen`: `unerwuenscht`). Ranges `min,max`, open ends allowed. Needs a property type that owns the attribute; one value per attribute; the site ignores an unknown name without error.

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

Living area of apartments, houses and rooms; floor area of commercial properties and containers.

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

Same area as **Min area**.

## `minRooms` (type: `number`):

Zimmer (half rooms count as .5): apartments, houses, Auf Zeit & WG.

## `maxRooms` (type: `number`):

Same as **Min rooms**.

## `minPlotArea` (type: `integer`):

Grundstücksfläche of a house, or the size of a plot / garden.

## `maxPlotArea` (type: `integer`):

Same as **Min plot area**.

## `minFloor` (type: `integer`):

Etage of an apartment or a commercial unit (0 = ground floor).

## `maxFloor` (type: `integer`):

Same as **Min floor**.

## `minConstructionYear` (type: `integer`):

Baujahr: apartments and houses for sale, houses for rent, commercial, containers.

## `maxConstructionYear` (type: `integer`):

Same as **Built in or after**.

## `availableFrom` (type: `string`):

Verfügbar ab on or after this month, written like `2026-11`. Filtered by the site on the month the seller typed.

## `availableTo` (type: `string`):

Verfügbar ab on or before this month, written like `2027-03`.

## `apartmentType` (type: `string`):

Wohnungstyp (apartments to rent and to buy; other types are skipped).

## `houseType` (type: `string`):

Haustyp (houses to rent and to buy; other types are skipped).

## `commercialType` (type: `string`):

Objektart of commercial properties (other types are skipped).

## `plotType` (type: `string`):

Grundstücksart of plots & gardens (other types are skipped).

## `accommodationType` (type: `string`):

Art der Unterkunft of Auf Zeit & WG (other types are skipped).

## `amenities` (type: `array`):

Keep only the listings with ALL of these (as ticked by the seller). A property type that cannot have one of them is skipped (a balcony on a garage), the log says so.

## `noCommission` (type: `boolean`):

Provisionsfrei: keep only the listings whose seller says there is no broker's commission (sales, plots, commercial, garages; rentals have no such field and are skipped).

## `postedAfter` (type: `string`):

Only listings published on or after this date: `2026-09-01`, a full ISO date-time, or a period before now such as `24 hours`, `7 days`, `2 weeks`, `1 month`. The site gives the publication time to the second, so a run every hour with `1 hour` returns exactly the new ads.

## `postedBefore` (type: `string`):

Only listings published on or before this date (the whole day is included), or older than a period such as `30 days`.

## `excludeKeywords` (type: `array`):

Drop the listings whose title or description contains one of these words (case and German umlauts ignored; a word inside a longer one counts: `rad` drops `Fahrrad`). Without **Extract details** the description is the short preview of the card.

## `maxWarmRent` (type: `integer`):

Drop the rentals whose warm rent (Warmmiete) is above this, or unknown — the site does not filter it, so it is checked on each result and charged as a filter check. Auf Zeit & WG prices are warm rents already. The rentals of plots, commercial properties, holiday homes, garages and containers have none: only their sales are searched.

## `priceTypes` (type: `array`):

Keep only these kinds of price: a fixed amount, negotiable (VB, with or without an amount), or free (Zu verschenken). Empty = all. Checked on each listing, and charged as a filter check, kept or not.

## `requirePhone` (type: `boolean`):

Keep only the listings whose seller published a phone number (≈ 69 % of commercial sellers, ≈ 4 % of private ones). Needs **Extract details**. The phone is only on the listing's own page: each page read, then dropped for lack of a phone, is charged as a *Filtered listing* event (see the pricing). On private sellers, hundreds of pages can be read for few listings: the run stops after 300 pages in a row without a phone, and never goes past your maximum cost.

## `minImages` (type: `integer`):

Drop the listings with fewer photos than this. 0 = no filter.

## `onlyNew` (type: `boolean`):

Skip the listings that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and their page is not even opened. First run = everything is new.

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

Name of the memory used by **Only new listings**. Give each schedule / task its own key (e.g. `berlin-rentals`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every listing again. Untick it afterwards.

## `includeViewCount` (type: `boolean`):

Add how many times the listing has been viewed (`viewCount`). Costs 1 extra tiny request per listing and counts as one visit on the site, exactly like opening the page in a browser.

## `includeRaw` (type: `boolean`):

Add a `raw` field with the untouched JSON of the site (listing card and detail). Useful to recover a field this Actor does not map yet; makes the dataset ≈ 5× bigger.

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

Apify Proxy or your own proxies. Keep the default: it is included in the price. The residential Apify proxy is not available in this Actor.

## `maxConcurrency` (type: `integer`):

Maximum number of requests processed in parallel.

## `maxRequestsPerMinute` (type: `integer`):

Global request rate. With **View count** on, a listing page and its view counter count as ONE request here: the counter is read together with its page, on top of this rate (up to twice as many requests to the site). In list mode (**Extract details** off) each view counter is a request of its own. Lower it if the site answers HTTP 429 / 403 in the log.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [],
  "propertyTypes": [
    "apartment-rent"
  ],
  "offerType": "ANY",
  "searchQueries": [],
  "location": "Berlin",
  "locations": [],
  "radiusKm": 0,
  "sellerIds": [],
  "storeIds": [],
  "listingIds": [],
  "maxItems": 100,
  "maxItemsPerQuery": 0,
  "extractDetails": false,
  "sellerType": "ANY",
  "adType": "ANY",
  "pictureRequired": false,
  "sortBy": "DATE_DESCENDING",
  "attributeFilters": {},
  "apartmentType": "ANY",
  "houseType": "ANY",
  "commercialType": "ANY",
  "plotType": "ANY",
  "accommodationType": "ANY",
  "amenities": [],
  "noCommission": false,
  "excludeKeywords": [],
  "priceTypes": [],
  "requirePhone": false,
  "minImages": 0,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "includeViewCount": false,
  "includeRaw": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 8,
  "maxRequestsPerMinute": 240,
  "maxRequestRetries": 5,
  "debugLog": 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 = {
    "propertyTypes": [
        "apartment-rent"
    ],
    "location": "Berlin",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/kleinanzeigen-immobilien-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 = {
    "propertyTypes": ["apartment-rent"],
    "location": "Berlin",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/kleinanzeigen-immobilien-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 '{
  "propertyTypes": [
    "apartment-rent"
  ],
  "location": "Berlin",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/kleinanzeigen-immobilien-scraper --silent --output-dataset

```

## MCP server setup

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