{
  "openapi": "3.1.0",
  "info": {
    "title": "ImmoAnalyse.Pro Bewertungs-API",
    "version": "1.3",
    "description": "Automatisierte Wertindikation für Wohnimmobilien in Deutschland nach ImmoWertV (Vergleichs-, Ertrags- und Bodenwert). Server-zu-Server-API: Den Schlüssel niemals im Browser oder in Apps einbetten. Engine-Version 2026.10.19. Ergebnisse sind Wertindikationen und ersetzen kein Verkehrswertgutachten nach § 194 BauGB.",
    "contact": {
      "name": "ImmoAnalyse.Pro Support",
      "email": "support@immoanalyse.pro",
      "url": "https://immoanalyse.pro/api-docs"
    },
    "termsOfService": "https://immoanalyse.pro/rechtliches/api-nutzungsbedingungen"
  },
  "servers": [
    {
      "url": "https://immoanalyse.pro/api/v1",
      "description": "Produktion"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Bewertung",
      "description": "Marktwert, Spanne, Konfidenz und ImmoWertV-Herleitung"
    },
    {
      "name": "Adresse",
      "description": "Normalisierung und Geocoding"
    },
    {
      "name": "Metadaten",
      "description": "Objekttypen und Feld-Schema für Formulare"
    }
  ],
  "paths": {
    "/valuation": {
      "post": {
        "tags": [
          "Bewertung"
        ],
        "operationId": "createValuation",
        "summary": "Immobilie bewerten",
        "description": "Zählt auf das Monatskontingent. Identische Anfragen desselben Schlüssels innerhalb von 10 Minuten werden aus dem Zwischenspeicher beantwortet (`cached: true`) und zählen nicht erneut.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValuationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bewertung",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Limit": {
                "description": "Monatskontingent (nur bei begrenzten Tarifen)",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Remaining": {
                "description": "Verbleibendes Monatskontingent nach dieser Anfrage",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Reset": {
                "description": "Beginn des nächsten Abrechnungsmonats (ISO 8601, UTC)",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValuationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Ungültige Eingaben (`validation_error`, `invalid_json`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Schlüssel fehlt, ist ungültig, deaktiviert oder abgelaufen (`unauthorized`, `key_expired`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Monatskontingent ausgeschöpft (`quota_exceeded`)",
            "headers": {
              "X-Quota-Limit": {
                "description": "Monatskontingent (nur bei begrenzten Tarifen)",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Remaining": {
                "description": "Verbleibendes Monatskontingent nach dieser Anfrage",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Reset": {
                "description": "Beginn des nächsten Abrechnungsmonats (ISO 8601, UTC)",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Nicht bewertbar: `property_type_not_supported`, `input_implausible`, `region_data_insufficient`, `property_not_automatically_valuable`",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Minuten-Limit erreicht (`rate_limited`)",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Sekunden bis zum nächsten freien Platz",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Marktdaten vorübergehend nicht verfügbar (`temporary_market_data_unavailable`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/address-lookup": {
      "post": {
        "tags": [
          "Adresse"
        ],
        "operationId": "lookupAddress",
        "summary": "Adresse normalisieren und geocodieren",
        "description": "Eigenes Monatskontingent je Tarif. Wiederholte Adressen werden 30 Tage zwischengespeichert und zählen nicht. `koordinaten` direkt als `lat`/`lon` an /valuation übergeben.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddressLookupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Normalisierte Adresse",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Limit": {
                "description": "Monatskontingent (nur bei begrenzten Tarifen)",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Remaining": {
                "description": "Verbleibendes Monatskontingent nach dieser Anfrage",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Reset": {
                "description": "Beginn des nächsten Abrechnungsmonats (ISO 8601, UTC)",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressLookupResponse"
                }
              }
            }
          },
          "400": {
            "description": "Ungültige Eingaben (`validation_error`, `invalid_json`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Schlüssel fehlt, ist ungültig, deaktiviert oder abgelaufen (`unauthorized`, `key_expired`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Kontingent für Adressprüfungen ausgeschöpft (`quota_exceeded`)",
            "headers": {
              "X-Quota-Limit": {
                "description": "Monatskontingent (nur bei begrenzten Tarifen)",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Remaining": {
                "description": "Verbleibendes Monatskontingent nach dieser Anfrage",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Quota-Reset": {
                "description": "Beginn des nächsten Abrechnungsmonats (ISO 8601, UTC)",
                "schema": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Adresse nicht auflösbar (`address_not_resolved`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Minuten-Limit erreicht (`rate_limited`)",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Sekunden bis zum nächsten freien Platz",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Marktdaten vorübergehend nicht verfügbar (`temporary_market_data_unavailable`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/schema": {
      "get": {
        "tags": [
          "Metadaten"
        ],
        "operationId": "getSchema",
        "summary": "Feld-Schema je Objekttyp",
        "description": "Felder in Formular-Reihenfolge inkl. Pflichtgrad, Wirkung, Einheit und Optionen. Zählt nicht auf das Kontingent.",
        "parameters": [
          {
            "name": "haustyp",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "mfh",
                "wgh",
                "etw",
                "efh",
                "dhh",
                "rh",
                "reh",
                "villa",
                "bungalow",
                "bauernhaus",
                "burg",
                "besondere",
                "sonstiges",
                "grundstueck",
                "gewerbe"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Schema",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Ungültige Eingaben (`validation_error`, `invalid_json`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Schlüssel fehlt, ist ungültig, deaktiviert oder abgelaufen (`unauthorized`, `key_expired`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Minuten-Limit erreicht (`rate_limited`)",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Sekunden bis zum nächsten freien Platz",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Marktdaten vorübergehend nicht verfügbar (`temporary_market_data_unavailable`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/types": {
      "get": {
        "tags": [
          "Metadaten"
        ],
        "operationId": "listTypes",
        "summary": "Unterstützte Objekttypen",
        "responses": {
          "200": {
            "description": "Objekttypen",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Ungültige Eingaben (`validation_error`, `invalid_json`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Schlüssel fehlt, ist ungültig, deaktiviert oder abgelaufen (`unauthorized`, `key_expired`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Minuten-Limit erreicht (`rate_limited`)",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Sekunden bis zum nächsten freien Platz",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Marktdaten vorübergehend nicht verfügbar (`temporary_market_data_unavailable`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/result/{request_id}": {
      "get": {
        "tags": [
          "Bewertung"
        ],
        "operationId": "getResult",
        "summary": "Gespeicherte Bewertung erneut abrufen",
        "description": "Liefert Anfrage und Ergebnis unverändert (reproduzierbar). Zählt nicht auf das Kontingent.",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Gespeichertes Ergebnis",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Ungültige Eingaben (`validation_error`, `invalid_json`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Schlüssel fehlt, ist ungültig, deaktiviert oder abgelaufen (`unauthorized`, `key_expired`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unbekannte request_id (`result_not_found`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Minuten-Limit erreicht (`rate_limited`)",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "Anfragen pro Minute laut Tarif",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Verbleibende Anfragen im laufenden 60-Sekunden-Fenster",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix-Zeitstempel (Sekunden), zu dem wieder ein Platz frei wird",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Sekunden bis zum nächsten freien Platz",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Marktdaten vorübergehend nicht verfügbar (`temporary_market_data_unavailable`)",
            "headers": {},
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Schlüssel im Format `iap_live_…`"
      }
    },
    "schemas": {
      "ValuationRequest": {
        "type": "object",
        "required": [
          "plz",
          "haustyp",
          "wohnflaeche"
        ],
        "properties": {
          "haustyp": {
            "type": "string",
            "enum": [
              "mfh",
              "wgh",
              "etw",
              "efh",
              "dhh",
              "rh",
              "reh",
              "villa",
              "bungalow",
              "bauernhaus",
              "burg",
              "besondere",
              "sonstiges"
            ],
            "description": "Objekttyp. Felder und Pflichtangaben je Typ liefert GET /schema?haustyp=…"
          },
          "plz": {
            "description": "Postleitzahl — Fünfstellige deutsche PLZ. Bestimmt die regionale Datenbasis der Bewertung.",
            "type": "string",
            "pattern": "^\\d{5}$"
          },
          "ort": {
            "description": "Ort — Verbessert die Auswahl der Vergleichsobjekte und den regionalen Liegenschaftszins.",
            "type": "string",
            "maxLength": 120
          },
          "adresse": {
            "description": "Straße und Hausnummer — Basis für die adressgenaue Abfrage des amtlichen Bodenrichtwerts (BORIS).",
            "type": "string",
            "maxLength": 200
          },
          "lat": {
            "description": "Breitengrad — Aus dem Address-Lookup übernehmen. Umgeht das unpräzise Geocoding bei der Bodenrichtwert-Abfrage.",
            "type": "number",
            "minimum": -90,
            "maximum": 90
          },
          "lon": {
            "description": "Längengrad — Aus dem Address-Lookup übernehmen.",
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          "wohnflaeche": {
            "description": "Wohnfläche gesamt — Wohnfläche in Quadratmetern. Zentrale Bezugsgröße des Vergleichswertverfahrens.",
            "type": "number",
            "minimum": 1,
            "maximum": 100000,
            "x-unit": "m²"
          },
          "grundstuecksflaeche": {
            "description": "Grundstücksfläche — Ohne Grundstücksfläche kann kein amtlicher Bodenwert angesetzt werden — der Ertragswert wird dann nicht gerechnet.",
            "type": "number",
            "minimum": 0,
            "maximum": 1000000,
            "x-unit": "m²"
          },
          "baujahr": {
            "description": "Baujahr — Grundlage der Restnutzungsdauer nach ImmoWertV. Ohne Baujahr wird pauschal die halbe Gesamtnutzungsdauer angesetzt.",
            "type": "integer",
            "minimum": 1500,
            "maximum": 2031
          },
          "zustandSterne": {
            "description": "Zustand — Wirkt als Zu-/Abschlag auf den Vergleichswert (−15 % bis +12 %) und auf die Restnutzungsdauer.",
            "enum": [
              1,
              2,
              3,
              4,
              5
            ],
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "default": 3
          },
          "modernisierung_punkte": {
            "description": "Modernisierungsgrad (ImmoWertV Anlage 2) — Optionaler Feinwert 0–20 Punkte. Überschreibt die aus dem Zustand abgeleiteten Punkte bei der Restnutzungsdauer.",
            "type": "integer",
            "minimum": 0,
            "maximum": 20
          },
          "bauschadenGrad": {
            "description": "Bekannte Bauschäden — Führt zu einem Instandsetzungsabschlag und verkürzt die Restnutzungsdauer.",
            "enum": [
              "keine",
              "leicht",
              "mittel",
              "schwer"
            ],
            "type": "string",
            "default": "keine"
          },
          "denkmalschutz": {
            "description": "Denkmalgeschützt — Erhöht Liegenschaftszins und Bewirtschaftungskosten (höheres Bewirtschaftungsrisiko).",
            "type": "boolean"
          },
          "kaltmiete": {
            "description": "Ist-Kaltmiete gesamt (monatlich) — Ohne Miete ist kein Ertragswert möglich — das Ergebnis beruht dann allein auf dem Vergleichswert. Fehlt die Angabe, wird ersatzweise die regionale Marktmiete angesetzt.",
            "type": "number",
            "minimum": 0,
            "maximum": 10000000,
            "x-unit": "€/Monat"
          },
          "stellplatzMieteMonat": {
            "description": "Stellplatzmieten (monatlich) — Fließt als zusätzlicher Reinertrag (90 %) in den Ertragswert ein.",
            "type": "number",
            "minimum": 0,
            "x-unit": "€/Monat"
          },
          "ist_erbbaurecht": {
            "description": "Erbbaurecht — Bei Erbbaurecht wird ohne Bodenwert gerechnet und ein Wertabschlag berücksichtigt.",
            "type": "boolean"
          },
          "erbbauzins_jahr": {
            "description": "Erbbauzins pro Jahr — Nur bei Erbbaurecht. Mindert den Jahresrohertrag.",
            "type": "number",
            "minimum": 0,
            "x-unit": "€/Jahr"
          },
          "erbbaurecht_restlaufzeit": {
            "description": "Restlaufzeit Erbbaurecht — Nur bei Erbbaurecht. Begrenzt die Kapitalisierungsdauer.",
            "type": "integer",
            "minimum": 0,
            "maximum": 200,
            "x-unit": "Jahre"
          },
          "kaufpreis": {
            "description": "Kaufpreis / Preisvorstellung — Beeinflusst die Bewertung nicht, liefert aber Kaufpreisfaktor, Rendite und eine Markteinordnung.",
            "type": "number",
            "minimum": 0,
            "x-unit": "€"
          },
          "bodenrichtwert_pro_m2": {
            "description": "Bodenrichtwert — Überschreibt die BORIS-Abfrage. Nur setzen, wenn ein amtlicher Wert vorliegt.",
            "type": "number",
            "minimum": 0,
            "x-unit": "€/m²"
          },
          "gewerbeflaeche": {
            "description": "Gewerbefläche — Nutzungsartenreine Trennung: steuert Liegenschaftszins und Bewirtschaftungskosten anteilig nach Gewerbeanteil.",
            "type": "number",
            "minimum": 0,
            "maximum": 100000,
            "x-unit": "m²"
          },
          "meta": {
            "type": "object",
            "description": "Reine Attribution (Logging/Auswertung). Beeinflusst die Bewertung nicht und zählt nicht zum Idempotenz-Fingerabdruck.",
            "properties": {
              "client": {
                "type": "string",
                "maxLength": 120
              },
              "source": {
                "type": "string",
                "maxLength": 120
              },
              "landingpage": {
                "type": "string",
                "maxLength": 300
              },
              "campaign": {
                "type": "string",
                "maxLength": 120
              },
              "session_reference": {
                "type": "string",
                "maxLength": 120
              }
            }
          }
        },
        "example": {
          "adresse": "Suderwichstraße 143",
          "plz": "45665",
          "ort": "Recklinghausen",
          "haustyp": "mfh",
          "baujahr": 1965,
          "wohnflaeche": 320,
          "grundstuecksflaeche": 1668,
          "kaltmiete": 1800,
          "zustandSterne": 3
        }
      },
      "ValuationResponse": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "cached": {
            "type": "boolean"
          },
          "schema_version": {
            "type": "string"
          },
          "engine_version": {
            "type": "string"
          },
          "data_as_of": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "bewertbarkeit": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "valuable",
                  "valuable_with_limitations",
                  "not_reliably_valuable"
                ]
              },
              "begruendung": {
                "type": "string"
              },
              "limitations": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "vergleichsbasis": {
                "type": "object",
                "description": "Grundlage der Einstufung: valuable ab 8 sehr ähnlichen Vergleichsobjekten und Preisstreuung unter 35 %; valuable_with_limitations ab 3 und unter 50 %; sonst not_reliably_valuable.",
                "properties": {
                  "sehr_aehnliche_objekte": {
                    "type": "integer",
                    "description": "Sehr ähnliche Vergleichsobjekte (Lage, Baujahr, Fläche) unter den 25 ähnlichsten."
                  },
                  "streuung_prozent": {
                    "type": "number",
                    "nullable": true,
                    "description": "Preisstreuung der ähnlichsten Vergleichsobjekte (Interquartilsabstand / Median) in Prozent."
                  },
                  "vergleichsobjekte_umgebung": {
                    "type": "integer",
                    "description": "Vergleichsobjekte im gleichen 3-stelligen PLZ-Gebiet."
                  }
                }
              }
            }
          },
          "marktwert": {
            "type": "object",
            "properties": {
              "median": {
                "type": "number"
              },
              "min": {
                "type": "number"
              },
              "max": {
                "type": "number"
              },
              "waehrung": {
                "type": "string",
                "const": "EUR"
              },
              "preis_pro_m2": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "konfidenz": {
            "type": "object",
            "properties": {
              "wert": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100
              },
              "level": {
                "type": "string",
                "enum": [
                  "hoch",
                  "mittel",
                  "niedrig"
                ]
              },
              "treiber": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          },
          "verbesserungs_hinweise": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "methodik": {
            "type": "object"
          },
          "vergleichswert": {
            "type": [
              "number",
              "null"
            ]
          },
          "ertragswert": {
            "type": [
              "number",
              "null"
            ]
          },
          "bodenwert": {
            "type": "object"
          },
          "liegenschaftszins": {
            "type": "object"
          },
          "miete": {
            "type": "object"
          },
          "kennzahlen": {
            "type": "object"
          },
          "gebaeude": {
            "type": "object"
          },
          "vergleichsobjekte": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "erklaerungen": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "warnungen": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "kontingent": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "limit": {
                "type": "integer"
              },
              "verbraucht": {
                "type": "integer"
              },
              "verbleibend": {
                "type": "integer"
              },
              "reset_at": {
                "type": "string",
                "format": "date-time"
              },
              "tarif": {
                "type": "string"
              }
            }
          }
        }
      },
      "AddressLookupRequest": {
        "type": "object",
        "properties": {
          "adresse": {
            "type": "string",
            "maxLength": 300,
            "description": "Freitext, z. B. „Suderwichstraße 143, 45665 Recklinghausen“"
          },
          "strasse": {
            "type": "string",
            "maxLength": 200
          },
          "hausnummer": {
            "type": "string",
            "maxLength": 20
          },
          "plz": {
            "type": "string",
            "maxLength": 10
          },
          "ort": {
            "type": "string",
            "maxLength": 120
          }
        }
      },
      "AddressLookupResponse": {
        "type": "object",
        "additionalProperties": true,
        "properties": {
          "cached": {
            "type": "boolean"
          },
          "quality": {
            "type": "string",
            "enum": [
              "exact",
              "street",
              "city",
              "postcode",
              "failed"
            ]
          },
          "valuation_ready": {
            "type": "boolean"
          },
          "normalized": {
            "type": "object"
          },
          "koordinaten": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              }
            }
          },
          "alternativen": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "ambiguous": {
            "type": "boolean"
          },
          "hinweise": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "quelle": {
            "type": "string",
            "enum": [
              "geoapify",
              "nominatim",
              "plz_centroids",
              "keine"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "status",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stabiler Legacy-Code"
              },
              "status": {
                "type": "string",
                "description": "Granularer Vertragsstatus"
              },
              "message": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      }
    }
  }
}