Konventionen
Diese gelten auf jedem Endpunkt und stehen deshalb einmal hier statt auf jedem einzelnen.
Seitenweises Lesen
Sammlungen werden über Cursor geblättert, nicht über Offsets. Ein Offset ist instabil auf einem Bestand, der sich beim Durchlaufen ändert: Wird während eines Scans ein Datensatz eingefügt, wiederholt Offset-Paging stillschweigend einen; wird einer gelöscht, überspringt es stillschweigend einen. Ein Cursor zeigt auf eine Position in einer stabilen Sortierung, also geschieht weder das eine noch das andere.
# Erste Seite
GET /v1/api/properties?city=Berlin&limit=100
# Jede weitere Seite
GET /v1/api/properties?city=Berlin&limit=100&cursor=eyJpZCI6…Halten Sie an, wenn meta.has_more false ist. Halten Sie nicht bei einer kurzen
Seite an, und berechnen Sie keine Seitenzahl: Es gibt bewusst keine Gesamtsumme,
denn eine große gefilterte Menge zu zählen ist teuer, und die Antwort ist im
Moment ihrer Rückgabe veraltet.
Jeder Client hat einen Iterator, der das für Sie erledigt. Siehe Paging.
Behandeln Sie einen Cursor als undurchsichtig. Er kodiert Sortierposition und Filterzustand, ist also nur gegen dieselbe Abfrage gültig, und einen zu dekodieren, um selbst welche zu bauen, wird nicht unterstützt.
Eine Kopie aktuell halten
Lesen Sie einen Markt nicht per Zeitgeber erneut vollständig. Blättern Sie ihn
einmal durch, merken Sie sich das neueste gesehene updated_at und fragen Sie
danach nur nach dem, was sich seitdem geändert hat:
GET /v1/api/properties?city=Berlin&updated_since=2026-08-11T04:00:00ZÜberlappen Sie das Fenster um ein paar Minuten, statt den genauen Zeitstempel Ihres letzten Laufs zu nehmen. Datensätze werden nebenläufig geschrieben, eine strikte Grenze kann also einen verpassen, der einen Augenblick nach Ihrem Schnitt festgeschrieben wurde. Denselben Datensatz zweimal zu verarbeiten ist harmlos, wenn Ihr Schreibvorgang ein Upsert ist; einen zu verpassen nicht.
Webhooks machen diese Schleife ganz überflüssig.
Filtern und Sortieren
Filter werden mit UND verknüpft. Ein wiederholter Parameter bildet ein ODER
innerhalb dieses Parameters, ?property_type=apartment&property_type=studio
meint also die eine oder die andere Art. Bereichsfilter tragen die Präfixe min_
und max_ und sind einschließend.
Sortiert wird über einen Feldnamen, mit vorangestelltem Minus für absteigend:
?sort=-price. Bei Gleichstand wird immer über die id entschieden, das Blättern
ist also auch dann eindeutig, wenn viele Datensätze denselben Preis haben.
Suchergebnisse sind nach Relevanz sortiert und ignorieren die Sortierung.
Sparsame Felder und Erweiterung
Zwei Parameter formen die Nutzlast in entgegengesetzte Richtungen. fields
verengt einen Datensatz auf das Angefragte, und expand bettet eine verwandte
Ressource ein, die sonst ein eigener Aufruf wäre. Zusammen genutzt fassen sie
meist ein N+1 zu einer Anfrage zusammen:
GET /v1/api/properties?fields=id,price,living_area&expand=imagesIdempotenz
Senden Sie bei jedem POST, der etwas anlegt, einen Idempotency-Key. Ein
Netzwerk-Timeout sagt Ihnen nichts darüber, ob der Server gehandelt hat, und ein
Wiederholungsversuch ohne Schlüssel ist die Art, wie doppelte Angebote entstehen.
curl -sS -X POST "https://api.skautik.com/v1/api/properties" \
-H "Authorization: Bearer $SKAUTIK_API_KEY" \
-H "Idempotency-Key: 6f1c2a7e-4d90-4a1b-9f33-0c2f5b8e77aa" \
-H "Content-Type: application/json" \
-d @property.jsonEinen Schlüssel erneut zu senden liefert die ursprüngliche Antwort, statt einen
zweiten Datensatz anzulegen. Einen mit anderem Rumpf wiederzuverwenden ist ein
Fehler und liefert 409. Schlüssel werden 24 Stunden lang erinnert. Erzeugen Sie
eine UUID je logischer Anlage, nicht je Versuch.
Bedingte Anfragen
Lesevorgänge liefern ein ETag. Senden Sie es als If-None-Match zurück, und
eine unveränderte Ressource antwortet mit 304 ohne Rumpf, was kein Kontingent
kostet.
Bei einem PATCH senden Sie das ETag stattdessen als If-Match. Hat sich der
Datensatz seit Ihrem Lesen geändert, wird der Schreibvorgang mit 412 abgelehnt,
statt die andere Änderung stillschweigend zu verwerfen. Erneut lesen, Änderung
erneut anwenden, erneut versuchen.
Datenformate
| Art | Format |
|---|---|
| Zeitstempel | RFC 3339, immer UTC, immer mit dem Suffix Z. Nie Ortszeit und nie ein Epoch-Wert. |
| Geld | Ganzzahl in kleinster Einheit mit separater ISO-4217-Währung. 42900000 mit EUR sind 429.000,00. Für Geld werden nie Gleitkommazahlen verwendet. |
| Flächen | Quadratmeter als Zahl. Es wird nicht umgerechnet; der Marktdatensatz nennt seine eigene Flächenkonvention. |
| Kennungen | Undurchsichtige Zeichenketten mit Präfix wie prop_ und whk_. Zerlegen Sie sie nicht; das Präfix dient dem Wiedererkennen in Protokollen, nicht dem Routing. |
| Fehlende Werte | Ausdrückliches null, kein weggelassener Schlüssel und kein leerer String. Null heißt, die Quelle hat nie etwas geliefert, und das ist etwas anderes als null als Zahl. |
Anfragekennungen
Jede Antwort trägt einen Header X-Request-Id, der im Rumpf jedes Fehlers
wiederholt wird. Protokollieren Sie ihn. Wer eine nennt, ermöglicht dem Support,
genau diese Anfrage zu finden, statt um eine Reproduktion zu bitten.