# Kleinanzeigen Scraper - Search & New Listing Alerts (`magenta_courser/kleinanzeigen-monitor`) Actor

Enter Kleinanzeigen.de keywords or search URLs and get one row per listing: title, price, price type, postcode, city, posted time, shipping, image and URL. Can return only listings that are new since the last run. No seller personal data.

- **URL**: https://apify.com/magenta_courser/kleinanzeigen-monitor.md
- **Developed by:** [SUNGHWAN CHO](https://apify.com/magenta_courser) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Kleinanzeigen Monitor - Search & New Listing Alerts

Enter Kleinanzeigen.de keywords or search URLs and get one flat row per listing - title, price, price type, postcode, city, posted time, shipping, image and URL - optionally only the listings that are new since your last run.

Kleinanzeigen (formerly eBay Kleinanzeigen) is Germany's largest classifieds marketplace. This Actor is built for **resellers, deal hunters and price watchers** who check the same searches every day or every hour.

- **Fast by design.** No browser. Results are read from the search pages themselves (25 listings per request), newest first. A typical 100-listing search finishes in seconds, not minutes.
- **New-listing monitor.** Turn on *Only new listings since the last run* and schedule the Actor. Each run returns just the listings you have not seen yet and stops paging as soon as it reaches listings it already knows.
- **No seller personal data.** Seller names, user IDs, shop names, phone numbers and other contact fields are never collected. Phone numbers and e-mail addresses inside descriptions are masked.
- **Clear stops.** Every run writes a free `RUN_SUMMARY` with the status of each search, pages read, listings saved, new listings and blocked requests.

### Input

| Field | Description |
|---|---|
| `searches` (required) | Keywords (`iphone 13`, `fahrrad`, `lego technic`) or Kleinanzeigen search URLs copied from your browser (`https://www.kleinanzeigen.de/s-fahrraeder/c217`). One per line. |
| `maxItemsPerSearch` | Listings to save per search, newest first. Default 100, max 1,250. |
| `onlyNewSinceLastRun` | Return only listings not returned by an earlier run of the same search. The first run returns the current listings and remembers them. |
| `historyStoreName` | Key-value store that remembers seen listings. Use different names for independent monitors. |
| `includeDetails` | Also open each listing page for the full description, attributes (brand, model, condition, size, ...) and shipping cost. Slower and charged as a separate event. |
| `category` | Optional category ID (`173` Smartphones, `217` Fahrräder & Zubehör, `23` Spielzeug, `161` Elektronik), URL name (`handy-telekom`) or German name. |
| `location`, `radiusKm` | Optional German postcode or city plus radius (5, 10, 20, 30, 50, 100, 150 or 200 km). |
| `minPrice`, `maxPrice` | Optional price range in EUR. |
| `proxyConfiguration` | German residential proxy by default. Kleinanzeigen often refuses datacenter IPs. |

`category`, `location`, `radiusKm`, `minPrice` and `maxPrice` apply to keyword searches. For search URLs, set the filters on kleinanzeigen.de and copy the URL.

```json
{
  "searches": ["iphone 13", "https://www.kleinanzeigen.de/s-fahrraeder/c217"],
  "location": "10115",
  "radiusKm": 20,
  "maxPrice": 400,
  "maxItemsPerSearch": 100,
  "onlyNewSinceLastRun": true,
  "historyStoreName": "my-berlin-deals"
}
```

#### Monitoring tips

- Schedule the Actor (e.g. every hour) with *Only new listings since the last run* on. Connect the dataset to e-mail, Slack, Google Sheets or a webhook for alerts.
- A search is identified by its final Kleinanzeigen URL (keyword + filters). Changing a filter starts a new history.
- Listings beyond `maxItemsPerSearch` on the pages that were read count as seen and are not returned later.
- Paid top ads rotate on every page view and are often old. In monitor mode they are returned only when posted after the previous run.

### Output

One row per listing. Every field is always present; a value the site does not show is `null`.

```json
{
  "listingId": "3538560160",
  "title": "Lego Technic 42095",
  "price": 50,
  "currency": "EUR",
  "priceType": "negotiable",
  "originalPrice": null,
  "postcode": "16766",
  "city": "Kremmen",
  "state": "Brandenburg",
  "distanceKm": null,
  "categoryId": 23,
  "category": "Spielzeug",
  "postedAt": "2026-10-12T03:49:00.000Z",
  "postedDate": "2026-10-12",
  "postedAtPrecision": "minute",
  "shippingAvailable": true,
  "buyNowEnabled": true,
  "sellerType": "private",
  "imageUrl": "https://img.kleinanzeigen.de/api/v1/prod-ads/images/d4/d48e7885-...?rule=$_59.AUTO",
  "imageCount": 2,
  "url": "https://www.kleinanzeigen.de/s-anzeige/lego-technic-42095/3538560160-23-7817",
  "isTopAd": false,
  "isNew": null,
  "description": null,
  "attributes": null,
  "shippingCost": null,
  "searchQuery": "lego technic",
  "searchUrl": "https://www.kleinanzeigen.de/s-lego-technic/k0",
  "scrapedAt": "2026-10-12T04:40:03.883Z"
}
```

| Field | Meaning |
|---|---|
| `price` | EUR. `0` for free items (*Zu verschenken*), `null` when only "VB" or nothing is shown. |
| `priceType` | `fixed`, `negotiable` (VB), `free` or `null`. |
| `originalPrice` | Crossed-out earlier price after a price drop. |
| `postedAt` | Posted (or last bumped) time in UTC. Kleinanzeigen shows minutes for today and yesterday and only the date for older listings: see `postedAtPrecision` (`minute` or `day`) and `postedDate` (German date). |
| `distanceKm` | Distance from your location (location searches only). |
| `sellerType` | `private` or `commercial`. No seller identity is included. |
| `isNew` | `true` in monitor mode, otherwise `null`. |
| `description`, `attributes`, `shippingCost` | Filled with `includeDetails`. `attributes` keeps Kleinanzeigen's German labels (`Marke`, `Modell`, `Zustand`, `Größe`, ...). |

The key-value store record `RUN_SUMMARY` (free) lists per search: `status` (`completed`, `partial`, `failed`, `budgetLimited`, `timeLimited`), `stopReason` (`maxItemsReached`, `endOfResults`, `sitePageLimit`, `reachedSeenListings`, `noResults`, `chargeLimitReached`, `timeLimit`, `blockedBySite`, `requestFailed`), `totalResults` reported by the site, pages read, listings saved, new listings, and request counts including blocked requests.

### Limits

- **Kleinanzeigen shows at most 50 pages (1,250 listings) per search.** To cover more, split a search by price range, category or location. The run summary marks such searches with `sitePageLimit`.
- If Kleinanzeigen refuses requests, the Actor backs off and retries a few times with a new proxy session, then reports the search as failed. It does not solve captchas or work around bot protection. Failed searches are not charged.
- The Actor stops starting new pages about a minute before the run timeout, so the results and summary are always saved.

### Pricing

Pay per event:

- **listing** - each listing row saved to the dataset
- **listing-details** - each listing page read successfully with `includeDetails`

The run summary, failed requests and failed searches are free. If you set a *Maximum cost per run*, the Actor stops cleanly when it is reached and says so in the status message ("Stopped at your maximum cost per run ...").

### Use cases

- **Reselling and arbitrage** - catch underpriced iPhones, consoles, bikes, LEGO sets or designer items minutes after they are posted.
- **Price research** - track asking prices and price drops for a product or model across Germany or one region.
- **Local market watch** - follow new listings within a radius of a city for car parts, furniture, rentals or tools.
- **Data for agents and dashboards** - flat English fields, numbers as numbers, ready for spreadsheets and AI agents.

### Notes

This Actor reads publicly visible listing information only and does not log in. You are responsible for using the data in line with Kleinanzeigen's terms and applicable law (including GDPR).

# Actor input Schema

## `searches` (type: `array`):

Keywords (e.g. "iphone 13", "fahrrad", "lego technic") or full Kleinanzeigen search URLs copied from your browser (e.g. https://www.kleinanzeigen.de/s-fahrraeder/c217). One per line. Each search returns at most 1,250 listings (50 pages), the site's own limit.

## `maxItemsPerSearch` (type: `integer`):

Maximum listings to save for each search, newest first. Kleinanzeigen shows at most 1,250 per search; split large searches by price, category or location to get more.

## `onlyNewSinceLastRun` (type: `boolean`):

Return only listings not returned by an earlier run of the same search (same keyword and filters). The first run returns the current listings and remembers them. Ideal for scheduled runs (e.g. every hour).

## `historyStoreName` (type: `string`):

Name of the key-value store that remembers seen listings for "Only new listings". Use different names for independent monitors. Lowercase letters, digits and dashes.

## `includeDetails` (type: `boolean`):

Open each listing page to add the full description, attributes (brand, model, condition, size, ...) and shipping cost. One extra request per listing, so slower and charged as a separate event.

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

Optional. Kleinanzeigen category ID (e.g. 173 = Smartphones, 217 = Fahrräder & Zubehör, 23 = Spielzeug, 161 = Elektronik), its URL name (e.g. handy-telekom) or German name. Applies to keyword searches only.

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

Optional. German postcode (e.g. 10115) or city name (e.g. Berlin, München). Applies to keyword searches only.

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

Search radius around the location. Kleinanzeigen supports 5, 10, 20, 30, 50, 100, 150 and 200 km; other values are rounded to the nearest one. Ignored without a location.

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

Optional. Only listings at or above this price. Applies to keyword searches only.

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

Optional. Only listings at or below this price. Applies to keyword searches only.

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

Kleinanzeigen often refuses datacenter IPs. German residential proxy is the default and recommended.

## Actor input object example

```json
{
  "searches": [
    "iphone 13"
  ],
  "maxItemsPerSearch": 100,
  "onlyNewSinceLastRun": false,
  "historyStoreName": "kleinanzeigen-monitor-history",
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}
```

# Actor output Schema

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

One row per listing (newest first per search).

## `runSummary` (type: `string`):

Per-search status, stop reason, pages, listings, new listings and blocked requests. Not charged.

# 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 = {
    "searches": [
        "iphone 13"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "DE"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/kleinanzeigen-monitor").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 = {
    "searches": ["iphone 13"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "DE",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/kleinanzeigen-monitor").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 '{
  "searches": [
    "iphone 13"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "DE"
  }
}' |
apify call magenta_courser/kleinanzeigen-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,magenta_courser/kleinanzeigen-monitor"
        }
    }
}
```

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/Q7gjyPhXlsmh53HR0/builds/gOtIudBb2o7hkD7Wq/openapi.json
