Public API für Entwickler

Über diese API kann Ihre CRM-Software oder ein eigenes Skript Angebote in Ihrem Verkäuferkonto anlegen, aktualisieren und mit Fotos versehen – über dieselbe Geschäftslogik, die auch die manuelle Verkäufer-Oberfläche verwendet. Die API ist ausschließlich für Verkäufer gedacht, die bereits ein Konto auf diesem Marketplace haben.

Erste Schritte

  1. Erstellen Sie einen API-Schlüssel unter Einstellungen → API in Ihrem Verkäuferkonto. Der Schlüssel wird nur einmal vollständig angezeigt.
  2. Senden Sie ihn als Authorization: Bearer -Header an /api/public/v1/....
curl -X POST "https://<ihre-domain>/api/public/v1/listings" \
  -H "Authorization: Bearer mp_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "newProduct": { "categoryId": "...", "title": "BMW G30 Türgriff links" },
    "title": "BMW G30 Türgriff links",
    "condition": "used",
    "priceCents": 4500,
    "vatRate": 19
  }'

Basis-URL: /api/public/v1 – relativ zu Ihrer Marketplace-Domain, kein separater Host.

Authentifizierung

API-Schlüssel haben das Format mp_live_... (Produktion) bzw. mp_test_... (Test). Sie werden serverseitig nur als HMAC-Hash gespeichert und einmalig bei Erstellung angezeigt.

Beim Erstellen wählen Sie, welche Berechtigungen (Scopes) der Schlüssel erhält:

ScopeBerechtigt zu
listings:readAngebote lesen, Kategorien/Produkte durchsuchen, Validierung
listings:writeAngebote anlegen/ändern, Fotos-Liste, Kompatibilität, Attribute/Bestand nachtragen
listings:submitAngebot zur Prüfung einreichen
listings:publishAngebot direkt veröffentlichen, an der Moderation vorbei (nur wirksam bei admin-freigegebenen Konten, siehe unten)
images:writeFotos hochladen – per multipart oder per URL
webhooks:manageWebhook-Endpunkte über die API anlegen, ändern, testen und löschen
keys:self-rotateDer Schlüssel kann sich selbst vor Ablauf erneuern (optional, für unbeaufsichtigte Integrationen)

Ein optionales Ablaufdatum können Sie beim Erstellen des Schlüssels selbst festlegen. Die Verwaltung von Schlüsseln (anlegen, rotieren, widerrufen) erfolgt ausschließlich über Ihre eingeloggte Sitzung unter Einstellungen → API, nicht über die Public API selbst – ein kompromittierter Schlüssel kann also nie einen anderen Schlüssel anlegen oder widerrufen.

Angebote (Listings)

Ein „Listing” entspricht genau dem, was die Verkäufer-Oberfläche ein „Angebot” nennt – dieselbe Datenbank, dieselbe Geschäftslogik.

MethodePfadScope
GET/listingslistings:read
GET/listings/:idlistings:read
POST/listingslistings:write
PATCH/listings/:idlistings:write
POST/listings/:id/photosimages:write
POST/listings/:id/images/from-urlimages:write
POST/listings/:id/submitlistings:submit
POST/listings/:id/publishlistings:publish
POST/listings/:id/unpublishlistings:write
POST/listings/:id/archivelistings:write
POST/listings/bulklistings:write
PUT/listings/upsertlistings:write

Standardmäßig durchlaufen neue Angebote denselben zweistufigen Prozess wie in der Oberfläche: nach submit geht ein Angebot in die bestehende Moderationswarteschlange, bevor es sichtbar wird.

Für Verkäuferkonten, die ein Marketplace-Admin zusätzlich ausdrücklich als vertrauenswürdig markiert hat, steht mit dem Scope listings:publish ein direkter Weg offen: POST /listings/:id/publish prüft dieselben Voraussetzungen wie submit (mind. ein Foto, alle Pflicht-Attribute der Kategorie, Lagerbestand größer 0) und setzt das Angebot bei Erfolg direkt auf active – ohne Wartezeit auf eine manuelle Prüfung. Ohne diese Freigabe liefert der Endpunkt 403 DIRECT_PUBLISH_NOT_ALLOWED. Der zweistufige Weg über submit bleibt auch für freigegebene Konten weiterhin nutzbar – publish ist eine zusätzliche Option, kein Ersatz. unpublish und archive führen technisch dieselbe Statusänderung aus – der einzige Unterschied ist, welche Aktion Sie aufgerufen haben.

Änderungen per PATCH erfordern einen If-Match-Header mit der aktuellen Version (aus dem ETag einer vorherigen Antwort) – so verhindern wir, dass zwei gleichzeitige Änderungen sich gegenseitig überschreiben.

Ein optionaler Idempotency-Key-Header auf POST-Anfragen sorgt dafür, dass ein wiederholter Request (z. B. nach einem Netzwerkfehler) niemals doppelt anlegt. Für die Synchronisation mit Ihrem CRM können Sie externalId/externalSource mitgeben, oder direkt PUT /listings/upsert verwenden (legt an oder aktualisiert, je nachdem ob der Eintrag schon existiert).

Listen werden per Cursor paginiert, nicht per Seitenzahl:

GET /listings?status=active&limit=50&cursor=eyJjcmVhdGVkQXQiOi...

Kategorien & Attribute

Bevor Sie ein Angebot anlegen, können Sie abfragen, welche Felder eine Kategorie erwartet – nützlich, um in Ihrem eigenen System automatisch das passende Formular zu erzeugen.

GET /categories
GET /categories/search?q=bremsbelag&limit=10
GET /categories/{id}
GET /categories/{id}/attributes

Über search lösen Sie einen frei eingegebenen Kategorienamen (auch mit Tippfehlern, dank Trigram-Suche) direkt zu einer categoryId auf, ohne den gesamten Baum selbst durchsuchen zu müssen. Mit leafOnly=true erhalten Sie nur Kategorien, gegen die Sie tatsächlich ein Angebot anlegen können.

Die meisten Kategorien haben kein zusätzliches Attribut-Schema – dort genügen Titel und (vor dem Einreichen) mindestens ein Foto, wie gewohnt. Nur einzelne Kategorien (z. B. Bremsbeläge) verlangen zusätzliche, strukturierte Angaben; diese werden beim Anlegen unter newProduct.attributes mitgeschickt und können später über PATCH /listings/:id/attributes nachgetragen werden. Ein Angebot kann bis zu 100 Attribut-Felder tragen; jeder Wert ist ein einzelner Text, eine Zahl oder ein Wahrheitswert – Listen/Arrays werden aktuell nicht unterstützt.

Produkte (Katalog-Abgleich)

Bevor Sie ein neues Angebot für ein Ersatzteil anlegen, können Sie prüfen, ob dieses Teil – z. B. über die OE-/OEM-Nummer – bereits als Katalogprodukt existiert. So entstehen keine doppelten Katalogeinträge für dasselbe physische Teil, wenn mehrere Verkäufer es anbieten.

GET /products/search?oeNumber=A2129003514
GET /products/search?q=Bremsbelagsatz&categoryId=...

oeNumber wird exakt abgeglichen – Schreibweise, Leer- und Trennzeichen spielen dank Normalisierung keine Rolle ("A 212 900 35 14" und "A2129003514" treffen denselben Eintrag). q durchsucht den Titel als Teilzeichenkette. Mindestens eines der beiden Felder ist erforderlich; categoryId/brandId grenzen zusätzlich ein.

Die Treffer sind reine Vorschläge – ob Sie beim Anlegen eines Angebots productId (bestehendes Produkt) oder newProduct (neu anlegen) verwenden, entscheiden ausschließlich Sie – die API verknüpft nichts automatisch.

Bilder

POST /listings/:id/photos
Content-Type: multipart/form-data

Erlaubte Formate: JPEG, PNG, WebP. Maximal 8 MB pro Datei, 24 Fotos pro Angebot.

Der tatsächliche Dateiinhalt wird geprüft, nicht nur die angegebene Content-Type – ein falsch deklariertes Format wird abgelehnt.

Liegt das Bild bereits online (eigenes Storage, bestehender CRM-Datensatz), müssen Sie es nicht erst lokal herunterladen, um es hier wieder hochzuladen:

POST /listings/:id/images/from-url
{ "url": "https://ihr-storage.example/teile/12345.jpg" }

Nur https://. Die URL wird serverseitig aufgelöst und abgelehnt, wenn sie auf localhost, eine private/interne Adresse oder die Cloud-Metadata-Adresse zeigt (400 UNSAFE_IMAGE_URL); Weiterleitungen (Redirects) werden nicht verfolgt, und die Antwort wird beim Herunterladen laufend gezählt, nicht anhand der vom Server behaupteten Größe (413 IMAGE_DOWNLOAD_TOO_LARGE ab 8 MB). Danach gelten dieselben Prüfungen wie beim direkten Upload (Format, 24-Foto-Limit).

Webhooks

Über Webhooks erfahren Sie in Echtzeit von Änderungen an Ihren Angeboten sowie von neuen Bestellungen und Verkäufen – ohne dass Sie selbst regelmäßig abfragen müssen.

EreignisAusgelöst durch
listing.created / listing.updatedAngebot angelegt bzw. geändert
listing.submittedAngebot zur Prüfung eingereicht
listing.approvedAdmin hat das Angebot in der Moderation freigegeben
listing.publishedAngebot ist live (nach Freigabe oder direkter Veröffentlichung)
listing.rejectedAdmin hat das Angebot abgelehnt
listing.archived / listing.unpublishedAngebot deaktiviert – identischer Zustandswechsel, nur der Auslöser unterscheidet sich
order.createdKäufer hat eine Bestellung mit Ihrem Artikel aufgegeben (vor Zahlung)
offer.soldZahlung erfolgreich, Lagerbestand des Angebots auf 0

Endpunkte richten Sie entweder über Ihre eingeloggte Sitzung ein (Einstellungen → Webhooks), oder direkt über die API, mit einem Schlüssel, der den Scope webhooks:manage besitzt – nützlich, um die Einrichtung aus Ihrem CRM heraus zu automatisieren. Beide Wege rufen dieselbe Logik auf, es gibt keine zwei getrennten Implementierungen:

MethodePfad
GET/webhooks
POST/webhooks
PATCH/webhooks/:id
POST/webhooks/:id/rotate-secret
POST/webhooks/:id/disable
POST/webhooks/:id/test
DELETE/webhooks/:id

test schickt ein einzelnes Test-Ereignis nur an den betroffenen Endpunkt, nie an alle Ihre Endpunkte – nützlich, um URL und Secret vor dem Live-Einsatz zu prüfen. Dafür gilt ein deutlich strengeres Rate-Limit als für die übrige Webhook-Verwaltung (siehe unten), da Test-Aufrufe dieselbe Zustellungs-Warteschlange nutzen wie echte Ereignisse.

Jede Zustellung ist per HMAC-SHA256 signiert (X-Marketplace-Signature). Prüfen Sie die Signatur über die exakten, unveränderten Rohdaten des Requests:

const signedPayload = `${timestamp}.${rawBody}`;
const expected = crypto.createHmac('sha256', secret).update(signedPayload).digest('hex');
// mit dem hex-Teil von X-Marketplace-Signature (Format "v1=<hex>") vergleichen

Fehlerbehandlung

Jede Fehlerantwort hat dieselbe Struktur:

{
  "error": {
    "code": "SKU_ALREADY_EXISTS",
    "message": "...",
    "details": [ { "field": "inventory.sku", "message": "..." } ],
    "requestId": "a73c6fbb-ed58-45d7-896d-1a9f3df27e56"
  }
}

requestId finden Sie auch im X-Request-Id-Response-Header jeder Antwort – geben Sie ihn bei Rückfragen an unseren Support an.

StatusBedeutung
400Fehlerhafte Anfrage, fehlender If-Match-Header, zu viele Bulk-Items, unsichere Bild-/Webhook-URL
401API-Schlüssel fehlt, ungültig, widerrufen oder abgelaufen
403Fehlende Berechtigung (Scope), oder Direktveröffentlichung ohne Admin-Freigabe
404Ressource existiert nicht oder gehört nicht Ihnen
409SKU-/Externe-ID-Konflikt, Idempotency-Key bereits verwendet, ungültiger Angebots-Status für diese Aktion, Lagerbestand leer
412If-Match stimmt nicht mit aktueller Version überein
413Per URL heruntergeladenes Bild überschreitet 8 MB
415Datei-Inhalt entspricht nicht dem angegebenen Format
422Fachliche Validierung fehlgeschlagen (z. B. Bild zu groß, Angebot noch nicht bereit zur Veröffentlichung)
429Rate-Limit erreicht

Rate Limits

Begrenzt pro API-Schlüssel und Anfrageart, konfigurierbar serverseitig – Richtwerte:

ArtStandard-LimitFür freigegebene Schlüssel
Lesen (GET)300 Anfragen / 60 s3000 Anfragen / 60 s
Schreiben (POST/PATCH)60 Anfragen / 60 s600 Anfragen / 60 s
Bild-Upload30 Anfragen / 60 s300 Anfragen / 60 s
Bulk-Import10 Anfragen / 60 s100 Anfragen / 60 s
Webhook-Verwaltung30 Anfragen / 60 s—
Webhook-Test5 Anfragen / 60 s—

Bei Überschreitung antwortet die API mit 429 und dem Header Retry-After-publicApiKey.

Für etablierte Integrationen kann ein Marketplace-Admin Ihren Schlüssel auf die höheren Limits in der rechten Spalte hochstufen (Lesen/Schreiben/Bild-Upload/Bulk-Import) – das ist keine Selbstbedienungsfunktion, wenden Sie sich dafür an unseren Support. Die Webhook-Verwaltung/-Test-Limits sind davon unabhängig, da sie eine administrative Aktion und keinen Katalog-/Bestell-Durchsatz begrenzen.

Vollständige Referenz

Alle Endpunkte mit vollständigem Schema finden Sie in der interaktiven API-Dokumentation.

API-Dokumentation öffnen