# Kleinanzeigen $1💰 URL Search | Keyword Filtering | Rich Output (`abotapi/kleinanzeigen-de-scraper`) Actor

From $1/1K. Scrape Kleinanzeigen.de ads by keyword, category, location, price, seller type, or search URL. Extract descriptions, photos, attributes, seller details, posted dates, category paths, and imprint data. Returns flat JSON records ready for spreadsheets or databases.

- **URL**: https://apify.com/abotapi/kleinanzeigen-de-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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

## Kleinanzeigen.de Scraper

Scrape classified ads from Kleinanzeigen.de (formerly eBay Kleinanzeigen) by keyword, category, location, price, offer type and seller type, or just paste ready-made search URLs. The scraper walks result pages automatically and, when you want it, opens each ad's detail page to pull the full description, every photo, the attribute list, the seller profile, the posted date, the category path and the commercial-seller imprint. Output is one flat JSON record per ad, ready for spreadsheets, dashboards or databases.

### Why this scraper

- Two ways in: a filter builder (keyword, category id, location with radius, min/max price, offered vs wanted, private vs commercial, sort) and a paste-URLs mode that accepts any Kleinanzeigen.de search or category URL.
- Automatic forward pagination, 27 ads per page, up to the site's page limit.
- Up to ~35 fields per ad including full description, all photo URLs, attribute key/value pairs, category path, seller name/type/registration date, and the commercial-seller legal imprint (name, address, email) where the seller is a registered trader.
- Fast to run on Residential proxy (the default), or on Datacenter for free-tier users.
- Lean toggle: turn off detail fetching for a quicker, cheaper run that still returns ~14 fields straight from the search results.
- Deduplicates promoted listings that repeat across pages, so you do not pay for the same ad twice.

### Data you get

> Sample shape, values are illustrative placeholders, not from a live ad.

| Field | Example |
| --- | --- |
| `adId` | `"3400000001"` |
| `url` | `"https://www.kleinanzeigen.de/s-anzeige/sample-listing-title/3400000001-217-0001"` |
| `title` | `"Sample Listing Title"` |
| `descriptionShort` | `"First sentence of the ad as shown in the result list..."` |
| `descriptionFull` | `"Full ad description text appears here when fetchDetails = true."` |
| `price` | `"1.500 € VB"` |
| `priceAmount` | `1500` |
| `priceType` | `"NEGOTIABLE"` |
| `currency` | `"EUR"` |
| `locationText` | `"10115 Berlin - Mitte"` |
| `postalCode` | `"10115"` |
| `regionName` | `"Berlin"` |
| `cityName` | `"Mitte"` |
| `categoryId` | `"217"` |
| `categoryName` | `"Fahrräder & Zubehör"` |
| `categoryPath` | `"Auto, Rad & Boot > Fahrräder & Zubehör"` |
| `postedDate` | `"2026-01-01"` |
| `postedDateText` | `"Heute, 09:00"` |
| `viewCount` | `null` |
| `imageCount` | `5` |
| `thumbnailUrl` | `"https://img.kleinanzeigen.de/api/v1/prod-ads/images/00/00000000-0000-0000-0000-000000000000?rule=$_59.AUTO"` |
| `imageUrls` | `["https://img.kleinanzeigen.de/api/v1/prod-ads/images/00/00000000-0000-0000-0000-000000000000?rule=$_59.AUTO"]` |
| `attributes` | `{ "Art": "Herren", "Typ": "Elektrofahrräder", "Zustand": "Sehr Gut" }` |
| `attributesRaw` | `[{ "name": "Art", "value": "Herren" }]` |
| `isPro` | `false` |
| `isShop` | `false` |
| `isTopAd` | `false` |
| `isSold` | `false` |
| `adType` | `"OFFERED"` |
| `tags` | `["Versand möglich"]` |
| `sellerId` | `"100000000"` |
| `sellerName` | `"Seller Name"` |
| `sellerType` | `"PRIVATE"` |
| `sellerUrl` | `"https://www.kleinanzeigen.de/s-bestandsliste.html?userId=100000000"` |
| `sellerActiveSince` | `"2026-01-01"` |
| `sellerOtherAdsCount` | `4` |
| `imprint` | `{ "raw": "Sample Trader\nEinzelunternehmer\n10115 Berlin, Sample Str. 1\ntrader@example.com", "lines": ["Sample Trader", "Einzelunternehmer", "10115 Berlin, Sample Str. 1", "trader@example.com"], "email": "trader@example.com" }` |
| `searchUrl` | `"https://www.kleinanzeigen.de/s-fahrrad/k0"` |
| `scrapedAt` | `"2026-01-01T00:00:00.000Z"` |

Fields that only come from the detail page (`descriptionFull`, `categoryName`, `categoryPath`, `imageUrls` beyond the first, `attributes`, `seller*`, `imprint`, `postedDate`) are `null` or empty when `fetchDetails` is `false`.

### How to use

Search by keyword:

```json
{
  "mode": "search",
  "keyword": "fahrrad",
  "maxPages": 3,
  "maxListings": 50,
  "fetchDetails": true
}
```

Search a category in a city with a radius and a price range, newest first:

```json
{
  "mode": "search",
  "keyword": "",
  "categoryId": "217",
  "location": "Berlin",
  "radiusKm": 20,
  "minPrice": 100,
  "maxPrice": 800,
  "sortBy": "date",
  "maxPages": 5,
  "maxListings": 100,
  "fetchDetails": true
}
```

Only private sellers, wanted ads:

```json
{
  "mode": "search",
  "keyword": "sofa",
  "offerType": "gesuche",
  "sellerType": "privat",
  "maxPages": 2,
  "fetchDetails": false
}
```

Paste several search URLs (filter fields are ignored in this mode):

```json
{
  "mode": "url",
  "urls": [
    "https://www.kleinanzeigen.de/s-fahrrad/k0",
    "https://www.kleinanzeigen.de/s-berlin/sofa/k0l3331"
  ],
  "maxPages": 3,
  "maxListings": 80,
  "fetchDetails": true
}
```

### Input parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` to build URLs from the filters below, `url` to paste your own search URLs. |
| `keyword` | string | `fahrrad` | Free-text query. Leave empty to browse a whole category. |
| `categoryId` | string | (empty) | Numeric Kleinanzeigen category id, e.g. `217`. Find it in any category URL: `.../c217` -> `217`. |
| `location` | string | (empty) | City name or German postal code. Resolved automatically to the matching location id. |
| `radiusKm` | integer | `0` | Search radius in km around the location. Ignored when no location is set. |
| `minPrice` | integer | (empty) | Lowest price to include. |
| `maxPrice` | integer | (empty) | Highest price to include. |
| `offerType` | string | `any` | `angebote` for offered ads, `gesuche` for wanted ads, `any` for both. |
| `sellerType` | string | `any` | `privat` for private sellers, `gewerblich` for commercial sellers/shops, `any` for both. |
| `sortBy` | string | `relevance` | `relevance`, `date` (newest), `price-asc`, `price-desc`, `distance`. Promoted ads can appear first regardless of sort. |
| `urls` | array of strings | `["https://www.kleinanzeigen.de/s-fahrrad/k0"]` | Search/category URLs to scrape (used only when `mode` = `url`). |
| `maxPages` | integer | `0` | Result pages to walk per search/URL (27 ads per page). `0` = unlimited: the walk keeps going until Kleinanzeigen has no next page for that search (Kleinanzeigen may itself cap a search around 50 pages, which is fine, the walk simply stops there). |
| `maxListings` | integer | `20` | Stop after this many ads across all searches. `0` = unlimited (still bounded by `maxPages`). |
| `fetchDetails` | boolean | `true` | Open each ad's detail page for the full description, all photos, attributes, seller info and imprint. Turn off for a faster, cheaper run. |
| `resumeFromRunId` | string | (empty) | A previous run ID or dataset ID to continue ONE interrupted full-catalogue walk across separate runs. Ads already collected there (matched by `adId`) are skipped, so this run only saves the new ones. |
| `incrementalMode` | boolean | `false` | Track a recurring search over time: remembers the ads from this actor's own previous run of the SAME search and tags every ad with a `changeType` (`NEW`/`UPDATED`/`UNCHANGED`/`REAPPEARED`/`EXPIRED`). Pair with the Apify Scheduler. Distinct from `resumeFromRunId` above. |
| `stateKey` | string | (empty) | Incremental mode only. A name for this recurring search's saved state. Leave empty to auto-derive one from your search/filter settings — two different filter setups never share a baseline. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also push ads with no detected change (`changeType=UNCHANGED`). Bills one extra row per unchanged ad every run. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also push ads tracked previously but no longer found, once, tagged `changeType=EXPIRED`. Only fires on a run that reaches the real end of every tracked search (no `resumeFromRunId`, no `maxListings`/`maxPages` cap hit). Bills one extra row per expired ad. |
| `proxy` | object | Residential, country `DE` | Apify Proxy. Residential is the default for the smoothest runs. On the free plan, switch to Datacenter (expect occasional retries). |

#### Resuming a full-catalogue walk

For an unbounded pull (`maxPages: 0`, `maxListings: 0`), two things protect a long run:

- **`resumeFromRunId`** lets you continue in a brand-new run: paste the ID of a previous run (or its dataset), and this run skips every `adId` already saved there, appending only new ads.
- A **checkpoint** is saved automatically as the run progresses. If the run is interrupted by an Apify platform migration or you click **Resurrect** on a failed run, it picks back up from where it left off in the *same* run, without re-saving (or re-charging for) ads it already collected. No input is needed for this; it's automatic.

#### Tracking a recurring search (incremental mode)

Turn on **`incrementalMode`** to monitor the same search on a schedule instead of re-scraping everything
each time. The actor remembers what it saw last time (keyed by `stateKey`, or an auto-derived hash of your
search/filter settings when left empty) and tags every ad in the dataset with a `changeType`:

- **`NEW`** — first time this ad has been seen for this search.
- **`UPDATED`** — the ad changed since last time; `changedFields` lists exactly which fields.
- **`UNCHANGED`** — nothing changed. Suppressed by default (not pushed, not billed); turn on `emitUnchanged` to include these rows anyway.
- **`REAPPEARED`** — an ad that had gone `EXPIRED` is back.
- **`EXPIRED`** — an ad tracked previously is no longer found. Only emitted when `emitExpired` is on AND the run reached the real end of every tracked search (no `resumeFromRunId`, no `maxListings`/`maxPages` cap cutting it short) — a partial run never marks anything expired, since it can't tell "gone" apart from "not reached yet".

Every row also carries `changedFields`, `firstSeenAt`, and `lastSeenAt`. `incrementalMode` is independent of
`resumeFromRunId`: the former tracks the same search across many separate scheduled runs, the latter
continues one specific interrupted run.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape — the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record — not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify → Settings → Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where items should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Output example

> Sample shape, values are illustrative placeholders.

```json
{
  "adId": "3400000001",
  "url": "https://www.kleinanzeigen.de/s-anzeige/sample-listing-title/3400000001-217-0001",
  "title": "Sample Listing Title",
  "descriptionShort": "First sentence of the ad as shown in the result list...",
  "descriptionFull": "Full ad description text appears here when fetchDetails = true.",
  "price": "1.500 € VB",
  "priceAmount": 1500,
  "priceType": "NEGOTIABLE",
  "currency": "EUR",
  "locationText": "10115 Berlin - Mitte",
  "postalCode": "10115",
  "regionName": "Berlin",
  "cityName": "Mitte",
  "categoryId": "217",
  "categoryName": "Fahrräder & Zubehör",
  "categoryPath": "Auto, Rad & Boot > Fahrräder & Zubehör",
  "postedDate": "2026-01-01",
  "postedDateText": "Heute, 09:00",
  "viewCount": null,
  "imageCount": 1,
  "thumbnailUrl": "https://img.kleinanzeigen.de/api/v1/prod-ads/images/00/00000000-0000-0000-0000-000000000000?rule=$_59.AUTO",
  "imageUrls": ["https://img.kleinanzeigen.de/api/v1/prod-ads/images/00/00000000-0000-0000-0000-000000000000?rule=$_59.AUTO"],
  "attributes": { "Art": "Herren", "Typ": "Elektrofahrräder", "Zustand": "Sehr Gut" },
  "attributesRaw": [{ "name": "Art", "value": "Herren" }, { "name": "Typ", "value": "Elektrofahrräder" }, { "name": "Zustand", "value": "Sehr Gut" }],
  "isPro": false,
  "isShop": false,
  "isTopAd": false,
  "isSold": false,
  "adType": "OFFERED",
  "tags": ["Versand möglich"],
  "sellerId": "100000000",
  "sellerName": "Seller Name",
  "sellerType": "PRIVATE",
  "sellerUrl": "https://www.kleinanzeigen.de/s-bestandsliste.html?userId=100000000",
  "sellerActiveSince": "2026-01-01",
  "sellerOtherAdsCount": 4,
  "imprint": null,
  "searchUrl": "https://www.kleinanzeigen.de/s-fahrrad/k0",
  "scrapedAt": "2026-01-01T00:00:00.000Z"
}
```

### Plan and proxy notes

- Default proxy is Residential with country `DE`. Available on the Starter plan and above. This gives the cleanest runs.
- On the free plan, set `proxy` to `{ "useApifyProxy": true }` (Datacenter). The actor will still work, but expect some retries and occasionally fewer results.
- Detail enrichment (`fetchDetails: true`) makes one extra request per ad. Use `maxListings` and `maxPages` to keep runs bounded, or set `fetchDetails: false` for the fastest, cheapest pass.
- Be considerate with how often and how broadly you run the scraper, and use the data in line with Kleinanzeigen.de's terms and applicable law.

# Actor input Schema

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

Pick how you want to start: 'search' builds search URLs from the filter fields below, 'url' takes ready-made Kleinanzeigen.de search URLs you paste yourself.

## `keyword` (type: `string`):

Free-text query, e.g. 'fahrrad', 'iphone 14', 'sofa'. Leave empty to browse a whole category.

## `categoryId` (type: `string`):

Numeric Kleinanzeigen category id, e.g. '217' (Fahrräder & Zubehör), '161' (Auto, Rad & Boot), '80' (Elektronik). Find it in the URL of any category page: .../c217 → 217. Leave empty to search all categories.

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

City name or German postal code, e.g. 'Berlin', 'München', '10115'. Resolved automatically to the matching location id. Leave empty to search all of Germany.

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

Search radius in kilometres around the chosen location. Ignored when no location is set. 0 = exact location only.

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

Lowest price to include. Leave empty for no lower bound.

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

Highest price to include. Leave empty for no upper bound.

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

Limit to ads offering something ('angebote') or wanted ads ('gesuche'). 'any' includes both.

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

Limit to private sellers ('privat') or commercial sellers/shops ('gewerblich'). 'any' includes both.

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

Result ordering. Note: Kleinanzeigen shows promoted ads first regardless of sort.

## `urls` (type: `array`):

One or more Kleinanzeigen.de search/category URLs to scrape. Filter-mode fields above are ignored. Pagination starts from the page in each URL and walks forward. Example: https://www.kleinanzeigen.de/s-fahrrad/k0

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

How many result pages to walk per search/URL (27 ads per page). 0 = unlimited (walk until the last page Kleinanzeigen serves for that search; Kleinanzeigen may itself cap a search around 50 pages, which is fine, the walk simply stops there).

## `maxListings` (type: `integer`):

Stop after this many ads across all searches. 0 = unlimited (still bounded by maxPages).

## `fetchDetails` (type: `boolean`):

Open every ad's detail page to pull the full description, all photos, attributes, seller info, view count, category path, and commercial-seller imprint. Turn off for a faster, cheaper run with just the search-result fields (~14 fields).

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue ONE interrupted full-catalogue walk across separate runs. Ads already collected there (matched by ad id) are skipped, so this run only saves the new ones. Leave empty for a normal fresh run. Distinct from 'Incremental mode' below, which tracks the SAME recurring search across many scheduled runs by itself.

## `incrementalMode` (type: `boolean`):

When ON, this actor remembers the ads from its own previous run of the SAME search (see 'State key' below) and tags every ad with a changeType: NEW, UPDATED, UNCHANGED, REAPPEARED, or EXPIRED. Use with the Apify Scheduler to monitor a search over time instead of re-scraping everything from scratch. Distinct from 'Resume from a previous run' above, which continues one specific interrupted run rather than tracking a recurring search. Default OFF — existing users/schedules are unaffected.

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

A name for this recurring search's saved state, e.g. 'berlin-fahrrad-watch'. Leave empty to auto-derive one from your search/filter settings (mode, keyword, category, location, radius, price, offer/seller type, sort, URLs, fetch details) — two different filter setups never share a baseline. Set this explicitly if you want a stable name across input tweaks that shouldn't reset tracking, or to run several independent monitors from the same actor.

## `emitUnchanged` (type: `boolean`):

When ON, ads with no detected change are still pushed to the dataset (tagged changeType=UNCHANGED) instead of being skipped. This returns — and BILLS — one extra row per unchanged ad every run. Default OFF: only NEW/UPDATED/REAPPEARED/EXPIRED ads are pushed.

## `emitExpired` (type: `boolean`):

When ON, ads tracked in a previous run but no longer found after a complete, uncapped scan are pushed once more tagged changeType=EXPIRED. This returns — and BILLS — one extra row per expired ad. Only fires on a run that reaches the real end of every tracked search (no resumeFromRunId, no maxListings/maxPages cap hit) — a partial run never marks anything expired. Default OFF.

## `proxy` (type: `object`):

Apify Residential proxy is strongly recommended and is the default. When no country is pinned (the default), each new connection lands on a different EU country (DE, AT, NL, BE, FR, IT, ES, PL, CZ, SE, DK, CH) so a single throttled exit cannot stall the whole run. Pin a country (DE recommended for kleinanzeigen.de) to force all connections through one location.

## `mcpConnectors` (type: `array`):

Optionally send the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize a connector once under Apify → Settings → Integrations, then select it here. The connector receives a condensed, human-readable summary per item (title + key fields), not the full JSON — the complete record stays in the dataset. Leave empty to skip. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com).

## `notionParentPageUrl` (type: `string`):

URL (or id) of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on items written to each connector per run. Does not affect the dataset.

## Actor input object example

```json
{
  "mode": "search",
  "keyword": "fahrrad",
  "radiusKm": 0,
  "offerType": "any",
  "sellerType": "any",
  "sortBy": "relevance",
  "urls": [
    "https://www.kleinanzeigen.de/s-fahrrad/k0"
  ],
  "maxPages": 0,
  "maxListings": 20,
  "fetchDetails": true,
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (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 = {
    "mode": "search",
    "keyword": "fahrrad",
    "urls": [
        "https://www.kleinanzeigen.de/s-fahrrad/k0"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/kleinanzeigen-de-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",
    "keyword": "fahrrad",
    "urls": ["https://www.kleinanzeigen.de/s-fahrrad/k0"],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/kleinanzeigen-de-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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",
  "keyword": "fahrrad",
  "urls": [
    "https://www.kleinanzeigen.de/s-fahrrad/k0"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call abotapi/kleinanzeigen-de-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=abotapi/kleinanzeigen-de-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/zWGpNJn0EofNHM3Jv/builds/3YIGI2BwesjhzvjSh/openapi.json
