---
title: "Kleinanzeigen Agent Dokumentation"
url: https://kleinanzeigen-agent.de/dokumentation
type: documentation
site: "Kleinanzeigen Agent"
language: de-DE
---

# Kleinanzeigen Agent Dokumentation

## Schnellstart

Mit der API rufst du aktuelle Kleinanzeigen-Daten direkt in deiner eigenen Anwendung ab: Suche, Inseratdetails, Verkäuferprofile, Verkäufer-Inserate, Kategorien und Standorte. Du arbeitest mit normalen HTTP-Requests und JSON-Antworten.

### Was du brauchst

- Einen aktiven Kleinanzeigen-Agent Account.
- Einen API-Key aus deinem Dashboard.
- Ausreichend Credits für die Anfragen, die du ausführen willst.

### Basis-URL

```text
https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen
```

`api.kleinanzeigen-agent.de` ist die produktive API-Subdomain. Alle Beispiele in der Dokumentation verwenden diese URL.

### Erste Suche

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/search?q=iphone&location_id=3331&distance=25" \
  -H "klaz_key: klaz_live_..."
```

### Beispiel in JavaScript

```ts
const url = new URL(
  "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/search"
);

url.searchParams.set("q", "iphone");
url.searchParams.set("location_id", "3331");
url.searchParams.set("distance", "25");

const response = await fetch(url, {
  headers: {
    klaz_key: process.env.KLAZ_API_KEY!,
  },
});

const result = await response.json();
```

### Antwort lesen

Jede Antwort hat dieselbe Grundstruktur. Bei einer erfolgreichen Suche findest du die Treffer unter `data.ads` und Informationen zur Seite unter `data.meta`.

```json
{
  "success": true,
  "message": "Kleinanzeigen live search completed",
  "data": {
    "meta": {
      "page": 0,
      "size": 31,
      "total": 126,
      "exact_total": true,
      "has_next_page": true,
      "next_page": 1,
      "source": "live"
    },
    "ads": [
      {
        "ad_id": "1234567890",
        "title": "iPhone 15",
        "price": {
          "amount": 650,
          "currency_code": "EUR"
        },
        "ad_url": "https://www.kleinanzeigen.de/..."
      }
    ]
  },
  "request_id": "req_..."
}
```

`request_id` ist die ID der Anfrage. Gib sie bei Support-Fragen mit an, damit wir den Request schneller finden können.

### Defaults

`page` startet bei `0`. `size` ist standardmäßig `31` und maximal `100`. Alle Parameter und Antwortfelder verwenden `snake_case`.

Die Suche liefert standardmäßig **„Neueste zuerst“**, ohne Sortierparameter.
`sort_type` lässt du weg; eine frei wählbare Sortierung wird aktuell nicht unterstützt.

---

## Authentifizierung

Dein API-Key identifiziert deinen Account und ordnet die verbrauchten Credits deinem Guthaben zu. Sende ihn bei jeder API-Anfrage im Header `klaz_key`.

```http
klaz_key: klaz_live_...
```

Den MCP Server richtest du am besten mit einem Login im Browser ein. Wenn dein MCP-Client das noch nicht unterstützt, kannst du denselben Key als Bearer Token senden:

```http
Authorization: Bearer klaz_live_...
```

### API-Key verwalten

Du findest deinen API-Key im Dashboard. Der vollständige Key wird nur beim Erstellen oder Rotieren angezeigt. Danach siehst du aus Sicherheitsgründen nur noch eine maskierte Version.

Wenn ein Key versehentlich geteilt wurde, rotiere ihn im Dashboard. Der alte Key wird dadurch ungültig und du erhältst einen neuen.

### Sicherer Einsatz

Speichere den Key in deiner Serverumgebung, zum Beispiel als Secret oder Environment Variable. Nutze ihn nicht direkt in öffentlichem Browser-Code und committe ihn nicht in dein Repository.

Sende den Key immer als Header. Verwende ihn nicht als Query-Parameter, weil URLs oft in Logs, Monitoring-Systemen oder Browser-Historien landen.

### Typische Auth-Fehler

`401` bedeutet, dass der Header fehlt oder der Key ungültig ist. Prüfe in diesem Fall den Headernamen `klaz_key`, den vollständigen Key-Wert und ob der Key im Dashboard rotiert wurde.

`429` bedeutet, dass dein aktuelles Rate Limit erreicht wurde. Reduziere die Anfragefrequenz oder wähle einen Plan mit höherem Limit.

---

## API Referenz

Diese Endpunkte sind für deine Anwendung gedacht. Alle Requests sind `GET`-Requests, benötigen den Header `klaz_key` und liefern JSON zurück.

### Inserate suchen

```http
GET /api/v2/kleinanzeigen/search
```

Nutze diesen Endpoint für Suchseiten, Preisvergleiche, Monitoring oder Matching in deiner Anwendung.

Parameter:

- `q` string: Suchbegriff.
- `page` integer, Default `0`: Ergebnis-Seite, startet bei `0`.
- `size` integer, Default `31`: Ergebnisse pro Seite, maximal `100`.
- `category_id` string: Kleinanzeigen-Kategorie.
- `location_id` string: Kleinanzeigen-Standort. Nicht zusammen mit `latitude` und `longitude` verwenden.
- `distance` string: Umkreis in Kilometern.
- `min_price` integer: Mindestpreis.
- `max_price` integer: Höchstpreis.
- `picture_required` boolean: Nur Inserate mit Bildern.
- `buy_now_only` boolean: Nur Direktkauf-Inserate.
- `shippable` boolean: Nur Inserate mit Versand.
- `ad_type` enum: `OFFER` oder `WANTED`. `OFFER` ist die normale
  Angebotssuche und wird nicht extra an Kleinanzeigen gesendet. Nur `WANTED`
  wird als Gesuch-Filter weitergereicht.
- `poster_type` enum: `PRIVATE` oder `COMMERCIAL`.
- `latitude` number: Breitengrad. Nur zusammen mit `longitude`.
- `longitude` number: Längengrad. Nur zusammen mit `latitude`.
- `attr[...]` string: Kategorieattribute, zum Beispiel `attr[condition]=new`.

Die Suche liefert standardmäßig **„Neueste zuerst“**. Dafür ist kein
Sortierparameter nötig. Eine andere Sortierung per `sort_type` ist aktuell
nicht verfügbar; die API antwortet mit `400`, wenn der Parameter gesendet wird.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/search?q=sofa&min_price=50&max_price=300&picture_required=true" \
  -H "klaz_key: klaz_live_..."
```

Die Antwort enthält `data.meta` und `data.ads`. `data.meta` enthält `page`, `size`, `total`, `exact_total`, `has_next_page`, `next_page` und `source`; Paging bleibt damit auf unserer API-Oberfläche und enthält keine Upstream-URLs. Jedes Inserat kommt in einem konsistenten Format zurück. `shipping_available` ist `true`, `false` oder `null`, wenn Kleinanzeigen in der Antwort kein zuverlässiges Versandsignal liefert; `shippable=true` filtert trotzdem serverseitig bei Kleinanzeigen.

```json
{
  "ad_id": "1234567890",
  "title": "Sofa",
  "description": "Guter Zustand",
  "price": {
    "amount": 120,
    "currency_code": "EUR",
    "currency_label": "Euro",
    "price_type": "FIXED",
    "negotiable": false
  },
  "shipping_available": true,
  "buy_now_available": false,
  "images": [],
  "ad_url": "https://www.kleinanzeigen.de/...",
  "views": null,
  "created_at": "2026-05-04T10:00:00.000Z",
  "status": "ACTIVE",
  "deleted": false,
  "seller": {
    "seller_id": "abc123",
    "name": "Max",
    "type": "PRIVATE"
  },
  "location": {
    "id": "3331",
    "name": "Berlin",
    "city": "Berlin"
  },
  "category": {
    "id": "88",
    "name": "Sofas",
    "parent_id": "80"
  },
  "attributes": [],
  "details": {}
}
```

Kleinanzeigen liefert in Suchantworten oft keine `user-id`. `seller.seller_id` kann deshalb bei `/search` `null` sein; nutze `GET /api/v2/kleinanzeigen/ads/:ad_id`, wenn du die Verkäufer-ID zuverlässig brauchst. Weitere Inserate desselben Verkäufers bekommst du auch direkt über `GET /api/v2/kleinanzeigen/ads/:ad_id/seller-ads`.

Genau diese Filter kannst du auch für Webhooks verwenden. Wir fragen die
Live-Suche im gebuchten Intervall automatisch für dich ab und schicken dir
jedes passende Inserat genau einmal.

### Treffer pro Kategorie

```http
GET /api/v2/kleinanzeigen/search/category-counts
```

Zeigt, wie viele Inserate eine Suche in jeder Kategorie findet, ohne dafür Inserate zu laden. Nutze den Endpoint für Marktanalysen, um das Angebot zu einem Produkt einzuschätzen, oder um eine Suche gezielt in die passende Kategorie zu lenken.

Parameter: dieselben Filter wie bei der Suche, also `q`, `category_id`, `location_id`, `distance`, `min_price`, `max_price`, `picture_required`, `buy_now_only`, `shippable`, `ad_type`, `poster_type`, `latitude`, `longitude` und `attr[...]`. `page` und `size` entfallen.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/search/category-counts?q=iphone%2015" \
  -H "klaz_key: klaz_live_..."
```

```json
{
  "success": true,
  "message": "Kleinanzeigen search category counts fetched",
  "data": {
    "meta": {
      "total": 21258,
      "exact_total": true,
      "source": "live"
    },
    "categories": [
      { "category_id": "161", "name": "Elektronik", "parent_id": "0", "count": 20541 },
      { "category_id": "153", "name": "Mode & Beauty", "parent_id": "0", "count": 525 },
      { "category_id": "297", "name": "Dienstleistungen", "parent_id": "0", "count": 274 }
    ]
  },
  "request_id": "req_..."
}
```

Die Kategorien sind nach Trefferzahl absteigend sortiert. Ohne `category_id` bekommst du die Hauptkategorien. Setzt du `category_id`, enthält die Liste diese Kategorie selbst und ihre direkten Unterkategorien, zum Beispiel mit `category_id=161` die Kategorie Elektronik mit ihrer Gesamtzahl und darunter Smartphones, Wearables und weitere Unterkategorien. So kannst du Schritt für Schritt tiefer gehen.

### Inseratdetails abrufen

```http
GET /api/v2/kleinanzeigen/ads/:ad_id
```

Parameter:

- `ad_id` numeric string, erforderlich: Kleinanzeigen-Inserat-ID im Pfad.
- `include_views` boolean, Default `false`: Lädt zusätzlich die Aufrufzahl und berechnet einen Extra-Credit.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/ads/1234567890?include_views=true" \
  -H "klaz_key: klaz_live_..."
```

Antwort:

```json
{
  "success": true,
  "message": "Kleinanzeigen ad fetched",
  "data": {
    "source": "live",
    "ad": {
      "ad_id": "1234567890",
      "title": "Sofa",
      "views": 42
    }
  },
  "request_id": "req_..."
}
```

### Inseratstatus prüfen

```http
GET /api/v2/kleinanzeigen/ads/:ad_id/status
```

Nutze diesen Endpoint, wenn du nur wissen willst, ob ein Inserat noch aktiv ist. `include_views=true` ist auch hier möglich.

```json
{
  "success": true,
  "data": {
    "source": "live",
    "status": {
      "ad_id": "1234567890",
      "status": "ACTIVE",
      "deleted": false,
      "views": null
    }
  },
  "request_id": "req_..."
}
```

### Ähnliche Inserate

```http
GET /api/v2/kleinanzeigen/ads/:ad_id/similar
```

Liefert bis zu 10 Inserate, die Kleinanzeigen als ähnlich zu einem Inserat einstuft. Das passt für Preisvergleiche, Alternativen oder Empfehlungen in deiner Anwendung.

Parameter:

- `ad_id` numeric string, erforderlich: Kleinanzeigen-Inserat-ID im Pfad.
- `size` integer, Default `10`: Anzahl ähnlicher Inserate, `1` bis `10`.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/ads/1234567890/similar?size=5" \
  -H "klaz_key: klaz_live_..."
```

Die Antwort enthält `data.meta` mit `ad_id`, `total` und `source` sowie `data.ads` im selben Format wie bei der Suche. Mehr als 10 ähnliche Inserate liefert Kleinanzeigen nicht, deshalb gibt es kein Paging.

### Weitere Inserate zum Verkäufer eines Inserats

```http
GET /api/v2/kleinanzeigen/ads/:ad_id/seller-ads
```

Liefert bis zu 30 weitere Inserate vom Verkäufer eines Inserats. Dafür reicht die Inserat-ID. Das ist praktisch bei Suchergebnissen, in denen Kleinanzeigen keine `seller_id` mitliefert.

Parameter:

- `ad_id` numeric string, erforderlich: Kleinanzeigen-Inserat-ID im Pfad.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/ads/1234567890/seller-ads" \
  -H "klaz_key: klaz_live_..."
```

```json
{
  "success": true,
  "message": "Kleinanzeigen seller ads for ad fetched",
  "data": {
    "meta": {
      "ad_id": "1234567890",
      "total": 70,
      "has_more": true,
      "source": "live"
    },
    "ads": []
  },
  "request_id": "req_..."
}
```

Das Ausgangsinserat selbst ist nicht in der Liste. `meta.total` ist die Gesamtzahl der weiteren Inserate des Verkäufers, `meta.has_more` ist `true`, wenn es mehr als die gelieferten 30 gibt. Für alle Inserate mit Paging nimmst du `ads[0].seller.seller_id` und rufst damit `GET /api/v2/kleinanzeigen/sellers/:seller_id/ads` auf.

### Verkäuferprofil abrufen

```http
GET /api/v2/kleinanzeigen/sellers/:seller_id/profile
```

Parameter:

- `seller_id` string, erforderlich: Verkäufer-ID aus `ad.seller.seller_id`.

Die Antwort nutzt das echte öffentliche Kleinanzeigen-Profil. Sie ist nicht aus den Inseraten zusammengesetzt. Je nach Verkäufer kann Kleinanzeigen einzelne Felder weglassen; deshalb sind viele Felder nullable.

```json
{
  "success": true,
  "data": {
    "source": "live",
    "profile": {
      "seller_id": "12345678",
      "name": "Max",
      "initials": "M",
      "type": "PRIVATE",
      "account_type": "PRIVATE",
      "since": "2021-03-12T10:15:30.000Z",
      "counters": {
        "historical_ads": 120,
        "online_ads": 8,
        "followers": 4,
        "following": 0
      },
      "ratings": {
        "average_rating": 4.8
      },
      "badges": [
        {
          "name": "friendly",
          "level": 1,
          "value": null
        }
      ],
      "biz_branding": null
    }
  },
  "request_id": "req_..."
}
```

### Weitere Inserate eines Verkäufers

```http
GET /api/v2/kleinanzeigen/sellers/:seller_id/ads
```

Parameter:

- `seller_id` string, erforderlich: Verkäufer-ID aus `ad.seller.seller_id`.
- `page` integer, Default `0`: Ergebnis-Seite.
- `size` integer, Default `31`: Ergebnisse pro Seite, maximal `100`.

Die Antwort entspricht der Suchantwort mit `data.meta` und `data.ads`.

### Kategorien

```http
GET /api/v2/kleinanzeigen/categories
GET /api/v2/kleinanzeigen/categories/:category_id/metadata
GET /api/v2/kleinanzeigen/categories/:category_id/search-metadata
```

Nutze Kategorien, um Suchanfragen genauer einzugrenzen und passende Filter für deine Oberfläche zu bauen.

`/categories` liefert den Kategoriebaum. `metadata` liefert verfügbare Felder für eine Kategorie. `search-metadata` liefert Suchfilter und Attribute, die du für `attr[...]` verwenden kannst.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/categories/161/search-metadata" \
  -H "klaz_key: klaz_live_..."
```

### Standorte

```http
GET /api/v2/kleinanzeigen/locations
GET /api/v2/kleinanzeigen/locations/:location_id
```

Parameter:

- `q` string: Standortsuche nach Name oder PLZ.
- `latitude` number: Breitengrad. Nur zusammen mit `longitude`.
- `longitude` number: Längengrad. Nur zusammen mit `latitude`.
- `limit` integer, Default `100`: Maximale Anzahl normalisierter Standorte, maximal `500`.

```bash
curl "https://api.kleinanzeigen-agent.de/api/v2/kleinanzeigen/locations?q=Berlin&limit=10" \
  -H "klaz_key: klaz_live_..."
```

Nutze Standorte für Autocomplete, regionale Suchen und Umkreissuchen. Die Standortliste wird flach zurückgegeben, damit du sie direkt in Suchfeldern oder Matchern verwenden kannst.

---

## MCP Server

Mit dem MCP Server kann ein KI-Client Kleinanzeigen Agent selbst nutzen. Er kann live suchen, Inseratdetails laden, Verkäufer prüfen und die Ergebnisse für dich weiterverarbeiten.

Kurz gesagt: MCP passt, wenn du einer KI eine Rechercheaufgabe gibst. Für feste Abläufe in deiner eigenen Software ist die API weiterhin der richtige Weg.

### Wann du MCP verwenden solltest

Nutze MCP, wenn ein KI-Client interaktiv mit Live-Daten arbeiten soll:

- Recherche-Assistenten, die erst suchen und dann einzelne Treffer prüfen.
- Agenten, die Verkäufer, Kategorien oder Standorte abhängig vom Zwischenergebnis nachladen.
- Interne Tools, in denen ein Mensch Fragen stellt und der Client passende Kleinanzeigen-Tools aufruft.
- Marktanalysen, bei denen ein Modell mehrere Suchen ausführt und daraus eine Tabelle oder Shortlist erstellt.

Nutze stattdessen die API, wenn deine eigene Software feste Anfragen ausführt. Nutze Webhooks, wenn du dauerhaft neue Inserate zu gespeicherten Filtern überwachen willst.

### Server-URL

```text
https://api.kleinanzeigen-agent.de/mcp
```

Diese URL trägst du in deinem MCP-Client ein. Die technischen Nachrichten erzeugt dein Client automatisch; du musst sie normalerweise nicht selbst schreiben.

### Automatische Discovery

Kompatible Clients und Agenten können die Verbindung auch über die öffentlichen Discovery-Dokumente erkennen:

- MCP Server Card: `https://kleinanzeigen-agent.de/.well-known/mcp/server-card.json`
- A2A Agent Card: `https://kleinanzeigen-agent.de/.well-known/agent-card.json`
- Authentifizierung: `https://kleinanzeigen-agent.de/auth.md`
- Agent Skill: `https://kleinanzeigen-agent.de/.well-known/agent-skills/kleinanzeigen-research/SKILL.md`
- API-Katalog: `https://kleinanzeigen-agent.de/.well-known/api-catalog`

Die Server Card beschreibt denselben MCP-Endpoint und dieselben Tools wie diese Dokumentation. Es ist kein zweiter Server und kein separater Tarif.

A2A ist ein separater Protokoll-Endpoint unter `https://api.kleinanzeigen-agent.de/a2a`. Er verwendet dieselben geprüften Tool-Funktionen, ist aber kein MCP-Transport. Details stehen in der [A2A-Dokumentation](#a2a).

### Authentifizierung

Empfohlen ist der Login im Browser: Dein MCP-Client öffnet einmal Kleinanzeigen Agent, du meldest dich an und erlaubst den Zugriff. Danach merkt sich der Client die Verbindung; dein API-Key muss nicht mehr in die lokale MCP-Konfiguration.

Wenn dein Client diesen Login unterstützt, füge nur die Server-URL hinzu und starte danach den Login im Client:

```text
https://api.kleinanzeigen-agent.de/mcp
```

Für ältere Clients bleibt die Einrichtung mit API-Key möglich. Verwende dafür:

```http
Authorization: Bearer klaz_live_...
```

Aus Kompatibilitätsgründen akzeptiert der MCP Server auch den bestehenden API-Header:

```http
klaz_key: klaz_live_...
```

Speichere den Key nur als Secret in deinem MCP-Client. Gib ihn nicht in einen Prompt ein, hänge ihn nicht an die URL und committe ihn nicht in eine Config-Datei. Wenn ein Key öffentlich geworden ist, rotiere ihn im Dashboard.

### Tools

Alle Werkzeuge liefern Ergebnisse im gleichen Format wie die API zurück. Dein Client kann sie anzeigen, zusammenfassen oder für den nächsten Schritt weiterverwenden.

Wenn dein System sehr viele Treffer sammeln, stark filtern oder eigene Zwischenergebnisse bauen soll, kann es statt MCP auch direkt die API nutzen. Die komplette API-Doku gibt es als Markdown unter `https://kleinanzeigen-agent.de/api/docs-raw`, damit KI-Clients sie direkt lesen können. Für die API brauchst du weiterhin deinen `klaz_live_...` API-Key als `Authorization: Bearer ...` Header oder als `klaz_key` Header. Der Key gehört trotzdem in Secrets oder Umgebungsvariablen, nicht in Prompts, Logs oder Code.

| Tool | Typische Eingaben | Zweck | Credits |
|---|---|---|---:|
| `search_kleinanzeigen` | `q`, `category_id`, `location_id`, `distance`, Preisfilter, Versandfilter, Attribute | Live-Inserate suchen | 1 |
| `get_kleinanzeigen_search_category_counts` | dieselben Filter wie `search_kleinanzeigen`, ohne `page` und `size` | Trefferzahl je Kategorie zählen, ohne Inserate zu laden | 1 |
| `get_kleinanzeigen_ad` | `ad_id`, optional `include_views` | Inseratdetails laden | 1 |
| `get_kleinanzeigen_ad_status` | `ad_id`, optional `include_views` | Prüfen, ob ein Inserat aktiv oder gelöscht ist | 1 |
| `get_kleinanzeigen_similar_ads` | `ad_id`, optional `size` (1 bis 10) | Bis zu 10 ähnliche Inserate laden | 1 |
| `get_kleinanzeigen_ad_seller_ads` | `ad_id` | Bis zu 30 weitere Inserate vom Verkäufer eines Inserats laden | 1 |
| `get_kleinanzeigen_seller_profile` | `seller_id` | Öffentliches Verkäuferprofil mit Name, Badges und Zählern laden | 1 |
| `get_kleinanzeigen_seller_ads` | `seller_id`, `page`, `size` | Weitere Inserate eines Verkäufers laden | 2 |
| `list_kleinanzeigen_categories` | keine | Kategoriebaum laden | 1 |
| `get_kleinanzeigen_category_metadata` | `category_id` | Felder einer Kategorie ansehen | 1 |
| `get_kleinanzeigen_category_search_metadata` | `category_id` | Suchfilter und Attribute einer Kategorie laden | 1 |
| `search_kleinanzeigen_locations` | `q` oder Koordinaten, optional `limit` | Standorte suchen | 1 |
| `get_kleinanzeigen_location` | `location_id` | Standortdetails laden | 1 |

Bei `get_kleinanzeigen_ad` und `get_kleinanzeigen_ad_status` kostet `include_views=true` einen zusätzlichen Credit.

`page` ist 0-basiert: Die erste Ergebnisseite ist `page: 0`. Wenn ein MCP Client versehentlich eine Seite außerhalb des vorhandenen Ergebnisbereichs anfragt, lädt das Tool automatisch die letzte verfügbare Seite nach.

Die Live-Suche liefert standardmäßig **„Neueste zuerst“**, ohne Sortierparameter.
`sort_type` wird aktuell nicht unterstützt. Für eine andere Reihenfolge kannst
du deinen MCP Client die bereits abgerufenen Inserate beispielsweise nach Preis
sortieren lassen; das sortiert nur diese Treffer, nicht den gesamten Suchbestand.

Ungültige Angaben kosten keine Credits. Wenn eine Live-Abfrage bei Kleinanzeigen fehlschlägt, wird der Credit wie bei der API automatisch zurückerstattet. Die Limits hängen weiter an deinem Plan.

### Beispiel für ältere Clients mit API-Key

Wenn dein MCP-Client den Login im Browser noch nicht unterstützt, kannst du den Server mit deinem API-Key verbinden:

```json
{
  "mcpServers": {
    "kleinanzeigen-agent": {
      "url": "https://api.kleinanzeigen-agent.de/mcp",
      "headers": {
        "Authorization": "Bearer klaz_live_..."
      }
    }
  }
}
```

Danach sollte dein Client die Werkzeuge selbst finden. Je nach Client heißt die Funktion zum Hinzufügen eines Servers anders, aber die wichtigen Teile bleiben gleich: Server-URL und, bei älteren Clients, der API-Key.

### Codex CLI

Wenn du Codex lokal nutzt, kannst du den MCP Server direkt über die CLI hinzufügen und danach den Login starten:

```bash
codex mcp add kleinanzeigen-agent \
  --url https://api.kleinanzeigen-agent.de/mcp

codex mcp login kleinanzeigen-agent
```

Prüfen kannst du die Verbindung mit:

```bash
codex mcp list
```

In der Codex-Oberfläche zeigt `/mcp` die verbundenen MCP Server.

Wenn deine Codex-Version diesen Login noch nicht unterstützt, nutze die API-Key-Variante mit `--bearer-token-env-var KLAZ_API_KEY`.

### Claude Code CLI

Wenn du Claude Code nutzt, kannst du den Server ebenfalls per CLI eintragen und danach den Login starten:

```bash
claude mcp add --transport http kleinanzeigen-agent \
  https://api.kleinanzeigen-agent.de/mcp

claude mcp login kleinanzeigen-agent
```

Prüfen kannst du die Verbindung mit:

```bash
claude mcp list
```

In Claude Code zeigt `/mcp` den Serverstatus und die verfügbaren Tools.

Der Befehl schreibt die Verbindung in deine lokale Claude-Code-Konfiguration. Wenn du die API-Key-Variante nutzt, speichere den Key nur als lokales Secret und nicht in einem geteilten Projekt.

### Beispiel-Fragen an den Client

Nachdem der Server verbunden ist, kannst du dem Client normale Aufgaben geben. Gute Prompts nennen Ziel, Filter und gewünschtes Ausgabeformat:

```text
Suche private iPhone 15 Angebote im Umkreis von 25 km um Berlin.
Lade Details für die 5 günstigsten Treffer nach und gib mir eine Tabelle mit Preis, Standort, Versand, Verkäufer und URL.
```

```text
Finde Sofas bis 300 EUR in München, nur mit Bildern.
Markiere auffällige Angebote und erkläre kurz, warum sie interessant sein könnten.
```

Der Client entscheidet dann selbst, ob er nur `search_kleinanzeigen` braucht oder zusätzlich Details, Verkäuferprofile, Verkäufer-Inserate, Kategorien oder Standorte nachladen sollte.

### Sicherheit und Grenzen

MCP gibt deinem KI-Client Zugriff auf die Kleinanzeigen-Werkzeuge, nicht auf dein ganzes Konto. Beim Login im Browser muss dein API-Key nicht kopiert werden. Wenn du die API-Key-Variante nutzt, bleibt der Key in deinem MCP-Client.

Der MCP Server speichert keine Inserate und ersetzt keinen Webhook. Er ist für interaktive Recherche gedacht. Für produktive Entscheidungen solltest du wichtige Ergebnisse mit `ad_id`, Preis, URL und Zeitpunkt ausgeben lassen und bei Bedarf selbst prüfen.

### Was dein MCP Client können muss

Dein MCP-Client muss Online-MCP-Server verbinden können. Für die einfachste Einrichtung sollte er den Login im Browser unterstützen. Wenn nicht, muss er einen API-Key mitsenden können:

```http
Authorization: Bearer klaz_live_...
```

Wenn dein Client weder den Login noch API-Keys für MCP-Server erlaubt, kannst du diesen MCP Server dort aktuell nicht direkt verbinden. Nutze in dem Fall die API oder einen MCP-Client, der eine der beiden Anmeldearten unterstützt.

### Häufige Fehler

| Problem | Ursache | Lösung |
|---|---|---|
| `401 MCP_AUTH_REQUIRED` | Kein Login und kein API-Key gesetzt | Login im MCP-Client starten oder `Authorization: Bearer klaz_live_...` setzen |
| `401 API_KEY_INVALID` | Key falsch, rotiert oder deaktiviert | Key im Dashboard prüfen oder rotieren |
| `401 MCP_OAUTH_INVALID_TOKEN` | Login ist abgelaufen oder wurde widerrufen | Login im Client erneut starten |
| `402 INSUFFICIENT_CREDITS` | Zu wenig Guthaben | Credits kaufen oder Plan upgraden |
| `403 MCP_ORIGIN_NOT_ALLOWED` | Verbindung kommt aus einer nicht erlaubten Umgebung | Client-Einstellung prüfen oder Support kontaktieren |
| Client findet keine Tools | MCP Server nicht korrekt verbunden | URL und Login-Status im Client prüfen |

---

## A2A 1.0

Mit dem A2A-Endpoint kann ein anderes Agentensystem Kleinanzeigen Agent beauftragen. Es sendet einen strukturierten Auftrag über das Agent2Agent-Protokoll und erhält einen Task mit Status und Ergebnis-Artefakt zurück.

Kurz gesagt: A2A passt, wenn eine Agentenplattform bereits Tasks, Statusabfragen und Artefakte spricht. Für interaktive Recherche mit einem KI-Client ist der [MCP Server](#mcp) der richtige Weg, für feste Abläufe in deiner eigenen Software die API.

### Wann du A2A verwenden solltest

- Deine Plattform orchestriert mehrere Agenten und erwartet überall dasselbe Task-Modell.
- Ein aufrufender Agent plant die Schritte selbst und braucht nur einen geprüften Datenschritt.
- Du willst Aufträge asynchron starten und den Status später abfragen.
- Du willst Ergebnisse als maschinenlesbares Artefakt weiterreichen, ohne HTML zu scrapen.

### Verbindung

```text
Agent Card: https://kleinanzeigen-agent.de/.well-known/agent-card.json
Kanonische Agent Card: https://api.kleinanzeigen-agent.de/.well-known/agent-card.json
A2A Endpoint: https://api.kleinanzeigen-agent.de/a2a
Protokoll: A2A 1.0, Binding JSON-RPC
```

Sende bei A2A-Anfragen diese Header:

```http
A2A-Version: 1.0
Content-Type: application/json
```

Die Agent Cards werden als `application/a2a+json` ausgeliefert. Das JSON-RPC-Binding erwartet für Requests dagegen `application/json`.

Die Agent Card beschreibt Endpoint, Version, Skills, Authentifizierung und Grenzen. Kompatible Clients können die Verbindung darüber automatisch einrichten.

### Authentifizierung

A2A verwendet denselben API-Key wie die REST API:

```http
Authorization: Bearer klaz_live_...
```

Aus Kompatibilitätsgründen funktioniert auch der bestehende Header:

```http
klaz_key: klaz_live_...
```

Speichere den Key nur in der Secret-Verwaltung deines Clients. Er gehört nicht in den Request-Body, nicht in einen Prompt, nicht in eine URL und nicht in ein Log. Ist ein Key öffentlich geworden, rotiere ihn im Dashboard.

Die OAuth-Tokens des MCP Servers gelten hier nicht. Sie sind auf die Ressource `/mcp` beschränkt. A2A-OAuth kommt erst, wenn es dafür eine eigene geschützte Ressource und einen passenden Scope gibt.

### Aufbau eines Auftrags

Jede `SendMessage`-Anfrage enthält genau einen strukturierten Data-Part mit einem vorhandenen Tool und dessen Argumenten:

```json
{
  "jsonrpc": "2.0",
  "id": "request-1",
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "client-message-1",
      "role": "ROLE_USER",
      "parts": [
        {
          "data": {
            "tool": "search_kleinanzeigen",
            "arguments": {
              "q": "Lastenrad",
              "page": 0,
              "size": 20
            }
          },
          "mediaType": "application/json"
        }
      ]
    }
  }
}
```

Der Endpoint interpretiert keine frei formulierten Prompts. Tool und Argumente wählt der aufrufende Agent ausdrücklich aus.

### Verfügbare Tools

Es sind dieselben 13 Lese-Tools wie beim [MCP Server](#mcp) verfügbar, mit denselben Argumenten und denselben Credit-Kosten:

| Tool | Zweck | Credits |
|---|---|---:|
| `search_kleinanzeigen` | Live-Inserate suchen | 1 |
| `get_kleinanzeigen_search_category_counts` | Trefferzahl je Kategorie für eine Suche zählen | 1 |
| `get_kleinanzeigen_ad` | Inseratdetails laden | 1 |
| `get_kleinanzeigen_ad_status` | Prüfen, ob ein Inserat aktiv oder gelöscht ist | 1 |
| `get_kleinanzeigen_similar_ads` | Bis zu 10 ähnliche Inserate laden | 1 |
| `get_kleinanzeigen_ad_seller_ads` | Bis zu 30 weitere Inserate vom Verkäufer eines Inserats laden | 1 |
| `get_kleinanzeigen_seller_profile` | Öffentliches Verkäuferprofil laden | 1 |
| `get_kleinanzeigen_seller_ads` | Weitere Inserate eines Verkäufers laden | 2 |
| `list_kleinanzeigen_categories` | Kategoriebaum laden | 1 |
| `get_kleinanzeigen_category_metadata` | Felder einer Kategorie ansehen | 1 |
| `get_kleinanzeigen_category_search_metadata` | Suchfilter einer Kategorie laden | 1 |
| `search_kleinanzeigen_locations` | Standorte suchen | 1 |
| `get_kleinanzeigen_location` | Standortdetails laden | 1 |

Bei `get_kleinanzeigen_ad` und `get_kleinanzeigen_ad_status` kostet `include_views=true` einen zusätzlichen Credit.

`search_kleinanzeigen` liefert standardmäßig **„Neueste zuerst“**, genau wie API
und MCP. Dafür ist kein Sortierparameter nötig; `sort_type` wird nicht unterstützt.

Unbekannte Tools und ein falsches Nachrichtenformat werden vor dem Tool-Aufruf abgelehnt und kosten keine Credits. Die Argumente prüft danach dasselbe Schema wie bei MCP; Validierungsfehler kommen mit ihren Details als Fehler-Artefakt zurück.

Requests dürfen höchstens 64 KiB groß sein. Das Feld `tenant` lässt du leer oder weg, denn frei wählbare Tenant-Namespaces werden nicht unterstützt.

### Tasks und Ergebnisse

Eine gültige Anfrage erzeugt einen Task, der die Zustände `SUBMITTED` und `WORKING` durchläuft und mit `COMPLETED` oder `FAILED` endet. Das strukturierte Tool-Ergebnis hängt als `application/json`-Artefakt am Task.

Unterstützt sind `SendMessage`, `GetTask` und `ListTasks`. Task-Snapshots liegen pro Benutzer getrennt für eine Stunde in Redis; pro Benutzer werden höchstens 500 Tasks indexiert. Sie sind als kurzfristiger Abholpunkt gedacht und können bei Betriebsarbeiten früher verschwinden.

Weitere Aufträge dürfen dieselbe Context-ID verwenden, erzeugen aber jeweils einen neuen Task. Sende dafür eine neue `messageId` ohne `taskId`. Follow-up-Nachrichten mit vorhandener `taskId` werden abgelehnt, und abgeschlossene Tasks bleiben unverändert. Jeder Auftrag enthält genau einen Tool-Aufruf.

### Wiederholte Anfragen

`messageId` ist zugleich der Idempotenz-Schlüssel für kostenrelevante Wiederholungen. Sendet derselbe angemeldete Benutzer dieselben vollständigen `SendMessage`-Parameter mit derselben `messageId` erneut, liefert der Server den bereits angelegten Task zurück und führt das Tool kein zweites Mal aus. Das gilt auch für zeitgleiche Requests.

Dieselbe `messageId` mit verändertem Inhalt wird als ungültiger Request abgelehnt. Die JSON-RPC-Request-ID gehört nicht zum Fingerprint und darf sich beim Retry ändern.

Der Schutz gilt, solange der zugehörige Task gespeichert ist: regulär bis zu einer Stunde, beim Überschreiten des Limits oder bei Betriebsarbeiten gegebenenfalls kürzer. Danach muss dein Client davon ausgehen, dass der Server die frühere `messageId` nicht mehr kennt.

### Credits und Limits

A2A ruft intern dieselben geprüften Funktionen auf wie MCP und die REST API. Deshalb gelten dieselben Regeln:

- Credit-Kosten pro Tool-Aufruf
- Erstattungen bei fehlgeschlagenen Live-Abfragen
- Schema- und Eingabeprüfungen
- Rate-Limits deines gebuchten Plans

Ein A2A-Request umgeht weder dein Guthaben noch die Tarifgrenzen.

Für einen technischen Retry verwendest du dieselbe `messageId` und denselben Request-Inhalt. So löst ein Netzwerk-Retry keinen zweiten Tool-Aufruf und keine zweite Credit-Belastung aus.

### Häufige Fehler

| Problem | Ursache | Lösung |
|---|---|---|
| `401` bei jedem Request | API-Key fehlt oder ist ungültig | `Authorization: Bearer klaz_live_...` setzen oder Key im Dashboard rotieren |
| `REQUEST_MALFORMED` | Nachrichtenformat falsch, Request über 64 KiB oder `messageId` mit verändertem Inhalt wiederverwendet | Aufbau des Data-Parts prüfen, Request verkleinern oder neue `messageId` verwenden |
| Task endet als `REJECTED` | Unbekannter Tool-Name oder ungültige Hülle | Tool-Namen aus der Tabelle oben verwenden |
| Task endet als `FAILED` | Live-Abfrage oder Argumentprüfung fehlgeschlagen | Fehler-Artefakt am Task auswerten |
| `402 INSUFFICIENT_CREDITS` | Zu wenig Guthaben | Credits kaufen oder Plan upgraden unter [/app/abrechnung](/app/abrechnung) |
| `TASK_NOT_CANCELABLE` | Live-Abfrage läuft bereits | Ergebnis abwarten und per `GetTask` abholen |
| `TASK_NOT_FOUND` | Task abgelaufen oder gehört einem anderen Konto | Auftrag mit neuer `messageId` erneut senden |

### Grenzen

- A2A interpretiert keine frei formulierten Prompts.
- Es wird kein zusätzlicher LLM-Anbieter aufgerufen und kein Prompt an ein Modell übertragen.
- Streaming, Push Notifications und Extended Agent Cards sind nicht aktiv.
- Bereits gestartete Live-Abfragen lassen sich nicht zuverlässig abbrechen; `CancelTask` liefert deshalb `TASK_NOT_CANCELABLE`.
- Der Endpoint implementiert A2A 1.0. Einen Kompatibilitätsmodus für A2A 0.3 gibt es nicht.

Für interaktive Recherche, bei der dein KI-Client Tools selbst entdeckt und während einer Unterhaltung flexibel aufruft, bleibt MCP die passende Wahl. A2A ist für Agentensysteme gedacht, die standardisierte Tasks, Status und Ergebnis-Artefakte erwarten.

---

## Webhooks

Lass dir neue Inserate automatisch an deinen Server schicken, sobald sie auf
Kleinanzeigen erscheinen — passend zu deinen Filtern, im gewählten Intervall.

Jedes Inserat erreicht dich genau einmal pro Webhook. Auch wenn ein Inserat
noch tagelang in der Suche steht, schicken wir es nicht erneut.

### Tarife

Webhooks haben ihr eigenes Abo. Dein API-Guthaben und dein Rate-Limit bleiben
davon unberührt.

| Tarif | Intervall | Preis netto |
|---|---:|---:|
| Webhook Pulse | 60 Sekunden | 39 EUR / Monat |
| Webhook Flow | 30 Sekunden | 79 EUR / Monat |
| Webhook Stream | 10 Sekunden | 229 EUR / Monat |
| Webhook Realtime | 3 Sekunden | 749 EUR / Monat |

Ein Abo entspricht einem aktiven Webhook mit einem Suchfilter und einer
Ziel-URL.

### Filter

Webhooks nutzen genau die Filter, die du auch von der Live-Suche kennst:

```http
GET /api/v2/kleinanzeigen/search
```

Verfügbar sind unter anderem `q`, `category_id`, `location_id`, `distance`,
`min_price`, `max_price`, `picture_required`, `buy_now_only`, `shippable`,
`ad_type`, `poster_type`, `latitude`, `longitude` sowie kategoriespezifische
`attr[...]`-Werte. `ad_type=OFFER` ist die normale Angebotssuche und wird nicht
extra an Kleinanzeigen gesendet. Nur `ad_type=WANTED` wird als Gesuch-Filter
weitergereicht.

### Sortierung und Suchumfang

Die Suche liefert standardmäßig **„Neueste zuerst“**, ohne Sortierparameter.
Der Webhook prüft pro Lauf nur die erste Ergebnisseite mit standardmäßig
100 Treffern (maximal 100); weitere Seiten ruft er nicht automatisch ab.

Für die Erfassung neuer Anzeigen ist daher entscheidend, ob sie zwischen zwei
Prüfungen aus diesem Ergebnisfenster rutschen, nicht allein die Zahl bereits
bestehender Treffer. Kürzere Intervalle verringern dieses Risiko, garantieren
aber keine lückenlose Erfassung.

### Lieferung

Sobald wir ein neues Inserat finden, schicken wir dir einen `POST` an deine
Ziel-URL.

Header:

```http
X-Klaz-Webhook-Id: <webhook_id>
X-Klaz-Delivery-Id: <delivery_id>
X-Klaz-Timestamp: 2026-05-09T12:00:00.000Z
X-Klaz-Signature: v1=<hex_hmac>
```

Payload:

```json
{
  "event": "kleinanzeigen.search.ads_found",
  "webhook_id": "uuid",
  "delivery_id": "uuid",
  "created_at": "2026-05-09T12:00:00.000Z",
  "query": {
    "q": "sofa",
    "location_id": "3331",
    "distance": "25"
  },
  "data": {
    "meta": {
      "source": "live",
      "interval_seconds": 30,
      "dedupe": "new_ad_ids_only"
    },
    "ads": []
  }
}
```

`data.ads` hat dasselbe Format wie die Antwort der Live-Such-API.

### Signatur prüfen

So weißt du, dass eine Lieferung wirklich von uns stammt: Wir hängen jeder
Anfrage einen HMAC-SHA256 als Header an, gebildet aus

```text
timestamp + "." + raw_body
```

mit deinem Webhook-Signing-Secret als Schlüssel. Das Secret findest du
jederzeit im Dashboard und kannst es bei Bedarf rotieren.

Beispiel in Node.js:

```ts
import crypto from "node:crypto";

function verifyKlazWebhook({
  rawBody,
  timestamp,
  signature,
  secret,
}: {
  rawBody: string;
  timestamp: string;
  signature: string;
  secret: string;
}) {
  const expected =
    "v1=" +
    crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}.${rawBody}`)
      .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
```

### Test schicken

Im Dashboard kannst du jederzeit eine Testlieferung auslösen. Sie ist signiert
wie eine echte Lieferung und enthält keine Inserate, damit du deinen Endpunkt
und die Signaturprüfung in Ruhe einspielen kannst.

---

## Suchagenten

Ein Suchagent speichert eine Kleinanzeigen-Suche und meldet neue Treffer
automatisch per E-Mail, Telegram oder Discord. Er ist für alle gedacht, die keine
eigene Software anbinden wollen. Eingerichtet wird alles im Dashboard unter
[/app/suchagenten](/app/suchagenten), ganz ohne API-Key.

### Einrichtung

1. **Suche zusammenstellen.** Suchbegriff, Kategorie, Ort mit Umkreis,
   Preisspanne, Inserats- und Anbietertyp. Kategorien und Orte wählst du per
   Auswahlliste; die Live-Vorschau zeigt dir vor dem Abo, wie viele Inserate
   aktuell passen und wie die neuesten Treffer aussehen.
2. **Kanal wählen.** E-Mail geht an die verifizierte Adresse deines Kontos.
   Für Telegram öffnen wir unseren Bot: dort einmal auf „Start“ drücken,
   und dein privater Chat ist verbunden. Für Discord fügst du die Webhook-URL
   eines Textkanals auf deinem Server ein und sendest eine Testnachricht.
3. **Abo abschließen.** Ein Suchagent kostet 5,90 € netto pro Monat, zzgl.
   gesetzlicher MwSt., monatlich kündbar. Die Zahlung läuft über Stripe;
   Rabattcodes kannst du im letzten Schritt einlösen.

Direkt nach der Einrichtung läuft die erste Prüfung. Du bekommst eine erste
Übersicht mit bis zu 20 aktuellen Treffern, damit du siehst, dass alles
funktioniert.

Die zugrunde liegende Live-Suche liefert standardmäßig **„Neueste zuerst“**.
Die Vorschau und die erste Übersicht zeigen dir daher die neuesten Treffer.

### Lieferung

- Dein Suchagent prüft alle **10 Minuten**.
- Nach der ersten Übersicht melden wir nur noch **neue** Inserate. Jedes
  Inserat erreicht dich genau einmal, auch wenn es tagelang in der Suche
  sichtbar bleibt.
- Eine Nachricht bündelt bis zu 20 Inserate mit Titel, Preis, Ort und Link
  zum Original-Inserat.
- Läufe ohne neue Treffer verschicken nichts, dein Postfach bleibt ruhig.

### Kanäle verwalten

Deine Benachrichtigungskanäle findest du in den
[Einstellungen](/app/einstellungen#notification-channels). Dort verbindest
oder trennst du Telegram und Discord. Wichtig: Wenn du eine Verbindung trennst, werden
Suchagenten deaktiviert, die dorthin liefern. Nach dem erneuten Verbinden
kannst du sie mit einem Klick fortsetzen.

Der Kanal eines Suchagenten lässt sich jederzeit ändern, ohne den Agenten neu
anzulegen.

### Discord einrichten

Öffne in Discord die Einstellungen deines Textkanals und gehe zu
**Integrationen → Webhooks**. Erstelle einen Webhook und kopiere dessen URL.
Dafür brauchst du die Berechtigung „Webhooks verwalten“. Füge die URL im
Suchagenten oder in den Benachrichtigungseinstellungen ein und wähle
**Verbinden & testen**. Die Verbindung wird nach erfolgreicher Testnachricht
gespeichert. Discord ist ohne Aufpreis im Suchagent-Abo enthalten.

Alle Mitglieder mit Zugriff auf den Kanal können die Suchtreffer und den
Suchbegriff sehen. Pro Konto gibt es eine Discord-Verbindung; ein Wechsel
betrifft alle deine Discord-Suchagenten. Persönliche Direktnachrichten,
Forenkanäle und Threads werden nicht unterstützt. Die geheime Webhook-URL wird
nach dem Speichern nicht angezeigt und beim Trennen entfernt.

### Pausieren, testen, kündigen

- **Pausieren** stoppt die Zustellung, das Abo läuft weiter, praktisch für
  Urlaub oder ruhige Phasen. Beim Fortsetzen prüft der Agent sofort wieder.
- **Test senden** schickt eine Testnachricht an deinen Kanal, damit du die
  Zustellung prüfen kannst.
- **Kündigen** beendet das Abo zum Ende des bezahlten Zeitraums. Bis dahin
  läuft dein Suchagent normal weiter, und du kannst die Kündigung jederzeit
  zurücknehmen.

Schlägt die Zustellung wiederholt fehl (zum Beispiel nach einem getrennten
Telegram-Chat oder einem gelöschten Discord-Webhook), deaktivieren wir den Agenten automatisch und zeigen dir das
im Dashboard an. Nach dem Beheben reicht ein Klick auf „Reaktivieren“.

### Abgrenzung zu Webhooks und API

Suchagenten sind ein eigenes Produkt mit eigenem Abo. Sie verbrauchen keine
API-Credits und belasten kein Rate-Limit.

| Produkt | Am besten für |
|---|---|
| Suchagent | Benachrichtigungen per E-Mail, Telegram oder Discord, ohne eigenen Code |
| [Webhooks](#search-webhooks) | Neue Inserate per HTTPS-POST an deine eigene Software, Intervalle ab 3 Sekunden |
| [API](#live-api-reference) | Eigene Abfragen, Analysen und Integrationen |

Wenn du mehrere Suchen parallel beobachten willst, legst du einfach mehrere
Suchagenten an, jeder mit eigenem Filter, Kanal und Abo.

---

## Fehler, Credits und Limits

Die API soll für deine Anwendung vorhersehbar sein: ungültige Requests verbrauchen keine Credits, erfolgreiche Requests werden nach Aktion berechnet, und fehlgeschlagene Live-Anfragen werden automatisch erstattet.

### Fehlerantwort

Jede Fehlerantwort enthält ein maschinenlesbares `error_code` und eine `message` für Menschen. Bei Validierungsfehlern listet `errors` die betroffenen Felder. Bei manchen Fehlern liefert `data` zusätzliche Hinweise (z. B. fehlende Credits oder weiterführende Links).

```json
{
  "success": false,
  "error_code": "INVALID_PARAMETERS",
  "message": "Invalid request parameters",
  "errors": {
    "min_price": ["min_price must be less than or equal to max_price"]
  },
  "request_id": "req_..."
}
```

### Statuscodes

- `400`: Ungültige Parameter, ungültige Pfad-ID oder ein vom Upstream zurückgewiesener Request.
- `401`: `klaz_key` fehlt oder ist ungültig.
- `402`: Nicht genug Credits.
- `404`: Ressource wurde upstream nicht gefunden.
- `429`: Planlimit erreicht oder Kleinanzeigen selbst hat das Limit ausgelöst.
- `500`: Unerwarteter Serverfehler.
- `502`: Kleinanzeigen hat abgelehnt oder ist temporär nicht verfügbar.
- `504`: Timeout bei der Live-Anfrage.

### Error Codes

Diskriminiere im Client auf `error_code` — die Codes sind stabil, während sich die `message`-Texte ändern können.

| `error_code` | Status | Bedeutung |
| --- | --- | --- |
| `INVALID_PARAMETERS` | 400 | Validierung fehlgeschlagen. `errors` enthält die Feldfehler. |
| `INVALID_AUTH_CONTEXT` | 400 | Auth-Header technisch fehlerhaft. Prüfe `klaz_key`. |
| `API_KEY_MISSING` | 401 | Kein `klaz_key` Header gesetzt. `data.signup_url` führt zur Anmeldung. |
| `API_KEY_INVALID` | 401 | Schlüssel unbekannt oder Account inaktiv. Neuen Schlüssel im Dashboard erzeugen. |
| `INSUFFICIENT_CREDITS` | 402 | Aktion zu teuer. `data.required_credits` und `data.available_credits` sind gesetzt; `data.buy_credits_url` und `data.upgrade_url` führen zur Abrechnung. |
| `UPSTREAM_BAD_REQUEST` | 400 | Kleinanzeigen hat den Request zurückgewiesen. |
| `UPSTREAM_NOT_FOUND` | 404 | Inserat, Verkäufer, Kategorie oder Standort existiert dort nicht (mehr). |
| `UPSTREAM_RATE_LIMITED` | 429 | Kleinanzeigen drosselt. Kurz warten und erneut versuchen — kein Plan-Upgrade nötig. |
| `PLAN_RATE_LIMIT_EXCEEDED` | 429 | Dein per-Minute-Limit ist erreicht. `data.upgrade_url` zeigt auf die Abrechnung. |
| `INGRESS_RATE_LIMIT_EXCEEDED` | 429 | Globales IP-Limit erreicht (sehr hoch angesetzt, normalerweise nur bei sehr aggressivem Traffic). |
| `UPSTREAM_UNAVAILABLE` | 502 | Kleinanzeigen ist gerade nicht erreichbar. Wird automatisch retryed. |
| `UPSTREAM_TIMEOUT` | 504 | Antwort kam nicht rechtzeitig. Erneut versuchen. |
| `INTERNAL_ERROR` | 500 | Unerwarteter Fehler bei uns. `request_id` mitsenden, wenn du Support kontaktierst. |

Bei `INSUFFICIENT_CREDITS` enthält `data` die Credit-Zähler (`required_credits`, `available_credits`) plus `buy_credits_url`, `upgrade_url`, `plans_url`. Bei `API_KEY_MISSING`, `API_KEY_INVALID` und `PLAN_RATE_LIMIT_EXCEEDED` enthält `data` weiterführende URLs (`signup_url`, `api_keys_url`, `docs_url`, `upgrade_url`, `plans_url`), damit du Endnutzer:innen direkt zum richtigen Schritt schicken kannst.

### Credit-Kosten

Kosten:

- Live-Suche: `1` Credit.
- Treffer pro Kategorie: `1` Credit.
- Verkäuferprofil: `1` Credit.
- Verkäufer-Inserate: `2` Credits.
- Inseratdetails: `1` Credit.
- Inseratstatus: `1` Credit.
- Ähnliche Inserate: `1` Credit.
- Weitere Inserate des Verkäufers ab Inserat-ID: `1` Credit.
- Views zusätzlich laden: `1` Credit.
- Kategorien: `1` Credit.
- Kategorie-Metadaten: `1` Credit.
- Standorte: `1` Credit.

`include_views=true` addiert den Views-Credit zur jeweiligen Anfrage. Ein Detailabruf mit Views kostet also `2` Credits, ein Statusabruf mit Views kostet `2` Credits.

### Live-Daten

Die API liefert aktuelle Daten aus der Live-Abfrage. Sie ist nicht als historisches Archiv gedacht und liefert keine gespeicherten Suchergebnisse aus früheren Läufen.

### Verbindung und Stabilität

Du musst keine Proxy-Daten, Cookies oder Kleinanzeigen-spezifische Verbindungseinstellungen konfigurieren. Wir kümmern uns um Routing, Timeouts, Normalisierung und retryfähige Verbindungsfehler.

### Aktuelle Einschränkungen

Die erste v2-Version konzentriert sich auf Live-Abfragen. Gespeicherte Suchhistorie, Harvesting und Raw-Antworten sind nicht Teil dieser API.

Die Standardsortierung der Suche ist **„Neueste zuerst“** und benötigt keinen
Sortierparameter. Nur die freie Wahl der Sortierung ist aktuell nicht verfügbar:
Die REST API lehnt `sort_type` mit `INVALID_PARAMETERS` ab, das MCP-Tool bewirbt
den Parameter nicht und entfernt ihn defensiv, falls ein Client ihn trotzdem mitsendet.

---
Kleinanzeigen Agent · https://kleinanzeigen-agent.de · office@kleinanzeigen-agent.de
