API-Leitfaden (v1)
Versionierte, öffentliche API. Referenz:
ADR-0019.
Interaktiv ausprobieren: /docs/api.
Maschinenlesbar: GET /api/v1/openapi.json.
Basis
https://www.landnutzen.at/api/v1
Pfad-versioniert (/api/v1). Breaking Changes → /api/v2; neue
Felder werden additiv innerhalb v1 ergänzt (Contract bleibt stabil).
Authentifizierung und Report-Quota
Die API funktioniert ohne Key. Der Reportpfad unterscheidet drei strikt getrennte Modi:
| Modus | Wie | Tageslimit |
|---|---|---|
| Anonym | kein Key und keine sicher auflösbare bestehende Kontositzung | 10 pro pseudonymem Request-IP-Bucket |
| Session | bestehende gleich-originäre Browser-Sitzung eines vorhandenen Users | 10 pro pseudonymem Account-Bucket |
| API-Key | Authorization: Bearer ln_… oder X-API-Key: ln_… | eigenes in der Datenbank hinterlegtes Limit |
Der Session-Modus verwendet ausschließlich die vorhandene LandNutzen- Browser-Sitzung. Er ist kein neues Cookie-Auth-Verfahren für headless oder Cross-Origin-Clients; diese verwenden weiterhin einen API-Key. Kann eine Sitzung nicht sicher aufgelöst werden oder existiert der zugehörige User nicht mehr, gilt der anonyme IP-Bucket; eine Sessionstörung überspringt die Bucket-Prüfung nicht.
Ein angebotener API-Key wird immer vor einer Sitzung geprüft. Gespeichert wird
nur ein SHA-256-Hash des Keys, nie der Klartext. Ein ungültiger oder
widerrufener Key antwortet mit 401 und fällt weder auf Cookie-Auth noch auf
anonymen Zugriff zurück. Die Key-Ausstellung erfolgt aktuell durch die
Plattform (Self-Service folgt mit dem Admin-Dashboard).
Endpunkte
GET /api/v1/report
| Parameter | Ort | Pflicht | Beispiel |
|---|---|---|---|
address | Query | ja | Rathausplatz 1, 2230 Gänserndorf |
curl "https://www.landnutzen.at/api/v1/report?address=Rathausplatz%201%2C%202230%20Gänserndorf"
POST /api/v1/report
curl -X POST https://www.landnutzen.at/api/v1/report \
-H "Content-Type: application/json" \
-d '{"address":"Rathausplatz 1, 2230 Gänserndorf"}'
Erfolg (200) = Report-JSON nach
Report-Contract — kein Wrapper. Antwort-
Header: X-RateLimit-Limit, X-RateLimit-Remaining, X-Auth-Mode.
X-Auth-Mode ist anon, session oder key; Limit und Restwert gehören
immer zum tatsächlich verwendeten Bucket. Report-Antworten bleiben
Cache-Control: private, no-store.
Der Account-Bucket ist
account:v1:<HMAC-SHA-256(AUTH_SECRET, report:account:v1:<users.id>)>.
User-ID und E-Mail werden nicht im Rate-Limit gespeichert. Der Prefix trennt
Konto-Buckets von anonymen IP- und API-Key-Buckets. Davon unabhängig bleibt
reports.ip_hash das Pseudonym der tatsächlichen Request-IP. Bei einer
Kontolöschung wird der Account-Bucket explizit entfernt; der allgemeine
wöchentliche Prune begrenzt ältere Rate-Limit-Zeilen nach 90 Tagen. Eine
interne FK-Lifecycle-Zuordnung bindet auch frühere HMAC-Generationen nach einer
AUTH_SECRET-Rotation an das Konto und löscht sie beim User-Delete per
Cascade. Details:
Report-Quota-Vertrag.
Additiver Reportblock: historische Hitzebelastung
risiko.hitzebelastung ist ein optionaler additiver v1-Block. Er
erscheint nur bei einem aktiven, qualifizierten Quellenrelease von
geosphere-hora-hitzelayer und enthält historische mittlere Tage pro
Jahr der Klimaperiode 1991–2020:
hitzetage_30c_jahr,extreme_hitzetage_35c_jahr,tropennaechte_20c_jahr,hitzewellentage_kysely_jahr;- vollständige Release-, Lizenz- und Rasterprovenienz einschließlich
source_release_id,release_key,resolution_m: 1000,native_crs: EPSG:3416und CC BY 4.0.
Kein aktives Release, keine konkrete Rasterabdeckung oder unvollständige Provenienz bedeuten: Der Block fehlt. Bestehende Reports bleiben kompatibel; es gibt weder einen HORA-Live-Provideraufruf noch eine Prognose oder Ampel. Der bestehende SPARTACUS-v3-Klima-Block zeigt unverändert das abgeschlossene Referenzjahr 2025. Vollständiger Quellen- und Reportvertrag.
Der Produktionsreadback
belegt sowohl die aktive Quellenbasis als auch getrennt den öffentlichen,
nicht persistierenden API-Consumer auf Merge 08d55c3. Das Fehlen des
optionalen Blocks bleibt bei fehlender Rasterabdeckung oder einem
fail-closed Release-/Rechte-Gate ein vertragskonformes Ergebnis.
Bedingter DXF-Export eines gespeicherten Reports
GET /api/v1/report/{id}/dxf ist kein allgemeiner Katasterdownload. Der Pfad
wird nur dann im OpenAPI-Schema veröffentlicht, wenn parcel_dxf im Code
release_bound und DXF_EXPORT_ENABLED exakt true ist. Produktion läuft
seit dem nachgewiesenen #412-Cutover mit true. Der tatsächlich ausgeführte
Rollback auf false ließ GET und OPTIONS mit 404 private, no-store
antworten und entfernte den Pfad aus dem Schema. Flagwechsel werden auf Vercel
erst nach einem Redeploy wirksam; Aktivierung, Rollback und finale
Wiederaktivierung liefen deshalb auf demselben geprüften Commit jeweils bis
Ready und wurden danach gegen Route plus OpenAPI rückgelesen. Das
deploymentabhängige OpenAPI-Dokument wird mit Cache-Control: no-store
ausgeliefert. Die Daten-Pointer blieben bei allen drei Zuständen unverändert.
Ein freigeschalteter Aufruf bleibt an den vorhandenen wertschöpfenden Report,
seinen Public-Release, dieselbe Koordinate und den qualifizierten
bev-parzellen/export-Pointer gebunden. Er exportiert vollständige,
unveränderte Pilot-Originalpolygone in voller Stützpunktdichte als ASCII-R12-
DXF in EPSG:31287. Sichtbare BEV-Attribution, Quelle, CC BY 4.0,
Bearbeitungshinweis, Nicht-Unterstützung und Vermessungsgrenze sind Pflicht.
Eigentümerdaten, ein eigenständiges BEV-Produkt, nationale Katasterabdeckung,
Clipping oder Vereinfachung sind ausgeschlossen.
200: qualifizierte DXF-Datei;400: ungültiges Report-ID-Format;403 report_context_mismatch: Der gespeicherte Report ist nicht an den erforderlichen wertschöpfenden Nicht-BEV- und Public-Release-Kontext gebunden;404: Report fehlt, ist abgelaufen oder das Flag ist aus;429: DXF-Tageslimit;503: Export-Release, Geometriezustand oder räumlicher Nutzungszweck passt nicht;500 dxf_failed: Der Renderer konnte die qualifizierte Geometrie nicht als DXF erzeugen; die Antwort bleibtprivate, no-store.
Der Consumer ist produktiv aktiviert. Der Produktionsnachweis belegt Dunkelzustand, positiven und negativen Live-Readback, GDAL-Parser, Pointerkonstanz, Rollback und finale Wiederaktivierung. NÖGIS #413 und ein eigenständiger Parzellenpass bleiben davon getrennt offen.
Fehler-Envelope
Stabil bei jedem Nicht-2xx:
{ "error": { "code": "address_missing", "message": "…" } }
| Status | code | Bedeutung |
|---|---|---|
400 | address_missing / invalid_json | Eingabe fehlt/ungültig |
401 | unauthorized | API-Key ungültig oder widerrufen |
429 | rate_limited | Tageslimit erreicht (Retry-After) |
500 | internal | unerwarteter Fehler |
Admin: Werkzeug-KPIs
GET /api/v1/admin/metrics erfordert einen API-Key mit admin-Scope und
liefert ausschließlich die kennungsfreien Aggregate aus ADR-0042: das
gewählte rollierende Tagesfenster sowie Status und Kennzahlen des festen
14-Tage-Ausgangswerts.
| Parameter | Werte | Standard |
|---|---|---|
days | 1, 7, 14, 30 | 14 |
tool | stabile öffentliche Tool-ID oder all | all |
Die Antwort enthält Funnel-, Qualitäts-, Laufzeit-, Kosten-, Regions- und
Providerzusammenfassungen sowie den Tagesverlauf. baselineSnapshot wird
davon getrennt während D+1 bis einschließlich D+14 nach dem gemeinsamen
Funnel-/Server-Anker fortgeschrieben. Mit Ablauf von D+14 ist er vollständig
und bleibt ab D+15 unverändert. Der Snapshot bewahrt die Funnel-, Fehler-,
Provider-, Runtime-, Laufzeit- und Kostendimensionen einschließlich Zählern
und Summen, enthält aber weder Tag noch Region und bleibt unabhängig von der
30-Kalendertage-Retention der Tageswerte erhalten. Es gibt keinen
Rohereignis-Endpunkt. Antworten tragen
Cache-Control: private, no-store. Ist die Aggregation nicht verfügbar,
antwortet der Endpunkt ehrlich mit 503 metrics_unavailable statt mit einer
leeren Erfolgsantwort.
Admin: Datenquellen und Drift-Policy
GET /api/v1/admin/data-sources erfordert einen API-Key mit admin-Scope.
Der bestehende Quellenkatalog, letzte technische Lauf und aktive Release
bleiben unverändert; DATA-02 ergänzt ausschließlich additive v1-Felder:
{
"generatedAt": "2026-08-25T10:47:00.000Z",
"monitoring": {
"cronName": "monitor-data-source-drift",
"lastCheckedAt": "2026-08-25T10:47:00.000Z",
"lastRunId": 123,
"lastAlertDelivery": { "status": "sent" },
"monitoredSources": 9,
"counts": { "fresh": 8, "due": 1, "stale": 0, "failed": 0, "unknown": 0 }
},
"sources": [
{
"slug": "basemap-orthofoto",
"activePointer": { "releaseId": "…", "pointerVersion": 2 },
"monitoring": {
"releaseId": "…",
"pointerVersion": 2,
"state": "due",
"severity": "warning",
"findings": [{ "code": "freshnessDue" }],
"thresholds": { "dueLeadMinutes": 120 },
"alert": { "status": "sent" }
}
}
]
}
Das Beispiel ist ein Schemaausschnitt, kein Produktionsreadback. Legacy-
Quellen ohne Active Pointer erhalten monitoring: null. Freigegebene
Teilabdeckung und bekannte Warnungen bleiben eine akzeptierte Baseline; erst
relevante Änderungen, veraltete Frische oder fehlende Evidenz erzeugen
due, stale, failed oder unknown.
Alle Antworten tragen Cache-Control: private, no-store. Fehlt der Admin-Key,
antwortet die Route mit 401; ein Key ohne admin-Scope erhält 403. Ist der
Quellen- oder Historienvertrag nicht vollständig lesbar, lautet die Antwort
503 data_sources_unavailable; ein leerer oder erfunden gesunder Status wird
nicht ausgegeben. Der vollständige Contract ist in
GET /api/v1/openapi.json unter /admin/data-sources dokumentiert.
Ehrliche Datenlage
Nicht verfügbare oder unsichere Datenschichten stehen in jedem
Report unter meta.degraded (Layer, Grund, Fallback). Die
Widmung ist eine Indikation (nicht rechtsverbindlich),
Kostenrahmen sind Grob-Schätzungen. Quellen/Lizenzen je Report
unter sources — siehe
Datenquellen & Lizenzen.