Überblick
Die API liefert amtliche Bodenrichtwerte (BRW) zu einer deutschen Adresse. Die Adresse wird
geokodiert, das Bundesland erkannt und der jeweils zuständige amtliche Geodienst abgefragt —
standardmäßig zum aktuellsten Stichtag, auf Wunsch zu einem historischen (Parameter
stichtag). Basis-URL ist die Domain dieser Seite, alle Antworten sind JSON
(UTF-8). Das Tool ist kostenlos und ohne Key nutzbar; ein API-Key erhöht lediglich die Limits.
Optional je Adresse: das amtliche ALKIS-Flurstück (mit_flurstueck) und ein
rechnerischer Bodenwert (mit_bodenwert) — beides derzeit für fünfzehn Länder (BB, BE, BW, HB, HE, HH, MV, NI, NW, RP, SH, SL, SN, ST und TH).
Beide Optionen gibt es auch im CSV-Batch.
Hinweis: Bayern (BY) ist derzeit nicht enthalten — Lizenzlage ungeklärt.
Endpunkte
GET /api/v1/abfrage — Einzelabfrage einer Adresse (synchron)
| Parameter | Typ | Beschreibung |
|---|---|---|
| adresse | Query, Weg 1 | 5–300 Zeichen, z. B. Domplatz 1, 39104 Magdeburg |
| lat, lon | Query, Weg 2 | Breiten- und Längengrad in WGS84, Dezimalpunkt statt Komma. Beide zusammen, sonst HTTP 400. Dieser Weg fragt keinen Geocoder. |
| gemarkung, flur, flurstueck, nenner | Query, Weg 3 | Amtliche Flurstücks-Kennung. 15 Länder (BB, BE, BW, HB, HE, HH, MV, NI, NW, RP, SH, SL, SN, ST und TH). In den INSPIRE-Ländern (BB, BW, HE, HH, MV, NI, SH, SL) ist statt des Gemarkungsnamens der sechsstellige Gemarkungsschlüssel anzugeben, und die Flur ist dort Pflicht — der amtliche Dienst führt dort keine Gemarkungsnamen. gemarkung und flurstueck (Zähler) sind Pflicht, flur und nenner optional — aber ohne sie ist die Kennung oft mehrdeutig, s. unten. |
| land | Query | Bundesland-Kürzel, z. B. NW. Pflicht bei Weg 2 und 3, unzulässig bei Weg 1. |
| kompakt | Query, optional | true = schlanke Antwort ohne roh_payload und Geometrie (für Integrationen) |
| mit_flurstueck | Query, optional | true = amtliches ALKIS-Flurstück an der Adresse ergänzen. 15 Länder (BB, BE, BW, HB, HE, HH, MV, NI, NW, RP, SH, SL, SN, ST und TH) — die übrigen liefern den Bodenrichtwert unverändert, aber kein Flurstück. Enthält seit 08/2026 auch tatsaechliche_nutzung (s. unten). |
| mit_bodenwert | Query, optional | true = rechnerischer Bodenwert (amtliche Fläche × amtlicher BRW, je Zone anteilig). Impliziert mit_flurstueck. |
| stichtag | Query, optional | JJJJ-MM-TT — historischer Stichtag. Freigegeben für BE, SN, ST, TH. Ohne den Parameter gilt immer der aktuellste Wert. |
Drei Zugangswege, ein Endpunkt.
Adresse, Koordinate oder Flurstücks-Kennung — genau einer je Anfrage. Zwei gleichzeitig
ergeben HTTP 400 statt einer stillen Bevorzugung: welcher gewonnen hätte, wäre von außen nicht
erkennbar. Alle übrigen Parameter (kompakt, mit_flurstueck,
mit_bodenwert, stichtag) gelten für jeden Weg gleich, und die
Antwortform ist identisch.
Warum land bei der
Koordinate Pflicht ist. Aus einer Koordinate allein lässt sich das Bundesland hier nicht
bestimmen: Wir führen keine Landesgrenzen-Geometrie, und der Geocoder kennt keine
Rückwärtssuche. Bliebe raten — an jeder Landesgrenze falsch — oder der Reihe nach zwölf
Landesdienste zu befragen. Also wird gefragt statt geraten:
GET /api/v1/abfrage?lat=52.1249036&lon=11.6356315&land=ST
{
"eingabe_adresse": "52.1249036, 11.6356315",
"geocode_quelle": "eingabe", "geocode_treffertyp": "koordinate",
"latitude": 52.1249036, "longitude": 11.6356315,
"treffer": [
{ "brw_nummer": "03204303", "bodenrichtwert_eur_qm": 900.0,
"bodenrichtwertzone_name": "Breiter Weg/Hasselbachplatz", "…": "…" },
{ "brw_nummer": "03207541", "bodenrichtwert_eur_qm": 4.45,
"bodenrichtwertzone_name": "Magdeburg_A", "…": "…" }
]
}
Ohne land:
fehler_typ: "land_erforderlich". Außerhalb Deutschlands oder mit vertauschten
Achsen: koordinate_ausserhalb; kein Zahlenformat (etwa Dezimalkomma):
koordinate_ungueltig.
Eine Flurstücksnummer ist nicht eindeutig. Gemessen am 2026-09-03 an den amtlichen Diensten: In der Gemarkung Köln tragen 19 Flurstücke die Nummer 344 — in 19 verschiedenen Fluren. Auch Gemarkung + Flur + Zähler kann mehrdeutig sein, wo ein Nenner existiert (Magdeburg, Flur 145, Nummer 5008 → 4 Flurstücke). Bleibt die Kennung mehrdeutig, wird keines ausgewählt:
GET /api/v1/abfrage?land=NW&gemarkung=Köln&flurstueck=344
{ "fehler_typ": "flurstueck_mehrdeutig",
"mehrdeutige_flurstuecke": ["05495803500344______", "05495800100344______", "…"] }
Weitere Fehlertypen dieses Weges:
flurstueckssuche_fuer_land_nicht_verfuegbar (Land ohne ALKIS),
flurstueckskennung_unvollstaendig, flurstueck_nicht_gefunden,
flurstueck_ohne_geometrie. gemarkung nimmt den Klartext
(Köln) oder den Gemarkungsschlüssel (054958); führende Nullen
der Flur werden ergänzt.
Historischer Stichtag. Die Abfrage ist geografisch adressiert: „welcher amtliche Wert galt an diesem Punkt zu diesem Stichtag?“ — nicht „was machte Zone X über die Jahre?“. Zonennummern und -zuschnitte sind über Jahrgänge nicht stabil, ein Join über die Nummer würde verschiedene Flächen vermischen.
Ein nicht vorhandener Stichtag wird nie stillschweigend durch den nächstgelegenen oder aktuellen Wert ersetzt. Stattdessen:
{
"fehler_typ": "stichtag_nicht_verfuegbar",
"stichtag_angefragt": "2016-01-01",
"verfuegbare_stichtage": ["2016-12-31", "2018-12-31", "2020-12-31", "2022-01-01"],
"treffer": []
}
Für ein Land ohne Historie-Freigabe:
fehler_typ: "historie_fuer_land_nicht_verfuegbar". Achtung: die Länder mischen die
Stichtagskonvention — Thüringen führt 2008–2020 als JJJJ-12-31 und erst ab 2022 als
JJJJ-01-01. 2016-01-01 ist dort leer, 2016-12-31 liefert
einen Wert. JJJJ-12-31 und 01.01.(JJJJ+1) sind
verschiedene Ermittlungszeitpunkte (§ 196 Abs. 1 Satz 5 BauGB, geändert zum 23.06.2021)
und werden nicht zusammengelegt.
Tatsächliche Nutzung.
Das Flurstück führt tatsaechliche_nutzung — eine Liste aus
nutzungsart (Klartext des Katasters), funktion (sofern
geführt) und flaeche_qm (Anteil). Quelle ist das amtliche
ALKIS-Feld tntxt; es liegt unverändert auch im
roh_payload.
"tatsaechliche_nutzung": [
{"nutzungsart": "Fläche besonderer funktionaler Prägung",
"funktion": "Öffentliche Zwecke", "flaeche_qm": 310.0}
]
Wichtige Abgrenzung: Die tatsächliche Nutzung ist nicht der Entwicklungszustand nach § 3 ImmoWertV, und sie wird von uns auch nicht in einen übersetzt. Sie wird ausschließlich ausgegeben — es werden daraus keine Bodenrichtwertzonen gefiltert, ausgewählt oder gewichtet. Ein baureifes, unbebautes Grundstück kann im Kataster als Grünland geführt sein; der Umkehrschluss trägt also nicht. Mehrere Nutzungen je Flurstück sind der Normalfall und werden vollständig ausgegeben.
Bodenwert.
ist_amtlich ist immer false: amtlich sind die beiden Eingangswerte
(Fläche, Bodenrichtwert), die Multiplikation und Flächenaufteilung sind unsere Berechnung.
bodenwert_gesamt_eur ist null, wenn Zonen dieselbe Fläche überdecken —
dann sind die Teilwerte alternative Wertansätze, keine Summanden, und Addieren wäre
fachlich falsch. Der Grund steht maschinenlesbar in abdeckung und im Klartext in
hinweise.
Beispielantwort (gekürzt):
{
"eingabe_adresse": "Domplatz 1, 39104 Magdeburg",
"geocode_status": "ok",
"longitude": 11.6339,
"latitude": 52.1250,
"treffer": [
{
"bodenrichtwert_eur_qm": 250.0,
"stichtag": "2024-12-31",
"bodenrichtwertzone_name": "...",
"quelle": { "land": "ST", "dienst_bezeichnung": "..." },
"attribution": {
"bereitsteller": "(c) GeoBasis-DE / LVermGeo ST (...)",
"lizenz": "dl-de/by-2-0",
"lizenz_uri": "https://www.govdata.de/dl-de/by-2-0"
},
"modified": "Schema normalisiert, Werte unveraendert"
}
],
"fehler": null
}
Jeder Treffer trägt die lizenzpflichtige attribution
(Bereitsteller · Lizenz · URI) und den Änderungshinweis modified.
Diese sind bei jeder Weiterverwendung mitzuführen.
POST /api/v1/batch — Datei-Abruf starten (asynchron)
| Parameter | Typ | Beschreibung |
|---|---|---|
| datei | multipart/form-data, Pflicht | CSV oder XLSX, max. 1 MB. Excel wird am Inhalt erkannt (ZIP-Signatur), nicht am Dateinamen; maßgeblich ist das erste Tabellenblatt, höchstens 5000 Zeilen. Die 1 MB gelten für die hochgeladene Datei. Bei einer XLSX ist das das komprimierte ZIP — deshalb wird die Zeilenzahl zusätzlich beim Lesen begrenzt. |
| mit_flurstueck | Form-Feld, optional | true = ALKIS-Flurstück je Adresse (15 Länder, alle außer Bayern) |
| mit_bodenwert | Form-Feld, optional | true = rechnerischer Bodenwert je Adresse; impliziert mit_flurstueck |
| stichtag | Form-Feld, optional | JJJJ-MM-TT — gilt für den gesamten Lauf, gleiche Regeln wie bei /api/v1/abfrage |
Antwort: { "job_id": "abc123...", "adressen": 12, "status": "wartet", "optionen": {...} }
Die Zusatzoptionen verlängern den Lauf spürbar (je Adresse weitere
amtliche Abrufe). Die XLSX enthält dann die Flurstücks- und Bodenwertspalten sowie das
Detailblatt Bodenwert mit einer Zeile je Teilfläche.
GET /api/v1/vorlagen — welche Spalten eine Datei tragen darf
Je Zugangsweg (adresse, koordinate,
flurstueck): die kanonischen Spaltennamen, welche davon Pflicht sind, eine
Beispielzeile und erkannte_namen — alle Schreibweisen, die der Upload
akzeptiert (etwa breitengrad für lat).
Der Zugangsweg muss nicht angegeben werden; er wird an den Spaltenüberschriften
erkannt. Ohne Rate-Limit: Der Endpunkt fragt keinen Landesdienst, er beschreibt das
eigene Format.
GET /api/v1/vorlage?modus=…&format=csv|xlsx — Mustervorlage als Datei
Kopfzeile plus eine echte, prüfbare Beispielzeile — die Datei lässt sich unverändert wieder hochladen. Sie wird aus derselben Tabelle erzeugt, die der Upload auswertet; eine gepflegte Beispieldatei würde beim nächsten Spaltennamen unbemerkt veralten. Die CSV ist semikolon-getrennt und trägt ein BOM, damit Excel sie in deutscher Systemumgebung mit einem Doppelklick richtig öffnet.
GET /api/v1/batch/{job_id} — Job-Status abfragen
Antwort enthält status (wartet/laeuft/fertig/fehler), verarbeitet, treffer, fehler.
GET /api/v1/batch/{job_id}/download?format=xlsx|json — Ergebnis herunterladen
Liefert die XLSX- bzw. JSON-Datei. Verfügbar, sobald status = fertig.
GET /api/v1/batch/{job_id}/vorschau?limit=20 — Ergebnis-Vorschau (JSON)
GET /api/v1/flurstueck — Flurstück per amtlichem Kennzeichen
| Parameter | Typ | Beschreibung |
|---|---|---|
| kennzeichen | Query, Pflicht | ALKIS-flstkennz, z. B. 150938145050080001__ |
| mit_bodenwert | Query, optional | true = rechnerischer Bodenwert je Flurstück |
Das Land wird aus den ersten beiden Stellen abgeleitet
(05 = NW, 15 = ST); andere Präfixe liefern HTTP 400 mit klarer Meldung.
Das Kennzeichen ist fix 20-stellig und rechts mit Unterstrichen aufgefüllt — kürzere
Eingaben werden serverseitig aufgefüllt.
GET /api/v1/flurstueck — auch per Koordinate (seit 2026-09-05)
| Parameter | Typ | Beschreibung |
|---|---|---|
| kennzeichen | Query | ALKIS-Kennzeichen (20-stellig). Entweder dies oder lat+lon — beides zusammen ergibt HTTP 400. |
| lat | Query | Breitengrad (WGS84). Das Bundesland wird aus der Ausdehnung der beiden ALKIS-Dienste bestimmt; ein Punkt außerhalb ergibt HTTP 400 mit land_ohne_alkis. |
| lon | Query | Längengrad (WGS84). |
enthaelt_punkt beachten. Trifft die Koordinate kein Flurstück — Straße, Gewässer, außerhalb des Landes —, liefert der amtliche Dienst nicht nichts, sondern das nächstgelegene Flurstück mit enthaelt_punkt: false. Wer das ungeprüft übernimmt, gibt für einen Klick auf den Asphalt wortlos das Nachbargrundstück aus. Der Wert wird deshalb unverändert durchgereicht statt hier gefiltert — der Aufrufer soll die Lage sehen.
GET /api/v1/flurstueck?lat=51.4818&lon=7.2162
{ "kennzeichen": null, "land": "NW", "anzahl": 1,
"flurstuecke": [ { "flurstueckskennzeichen": "05110700600396______",
"gemarkung_name": "Bochum", "flaeche_amtlich_qm": 1202.0,
"enthaelt_punkt": true, "geometrie_wgs84": { "…": "…" } } ] }
GET /api/v1/flurstueck/bereich — alle Flurstücke eines Kartenausschnitts
| Parameter | Typ | Beschreibung |
|---|---|---|
| bbox | Query, Pflicht | west,sued,ost,nord in WGS84. Höchstens 1000 Flurstücke je Anfrage — unsere Grenze gegen die Nutzlast, nicht die des Dienstes: Der liefert bei COUNT=5000 alle 2274 eines Quadratkilometers (gemessen 2026-09-06). |
Eine Zeichenvorlage, keine Auskunft. Dieser Endpunkt liefert Umrisse, damit eine Karte ein Flurstück beim Zeigen hervorheben und per Klick auswählen kann — eine Kachelebene ist ein Bild und gibt das nicht her. Bewusst ohne roh_payload: Das archivierte Original ist hier nutzlos und machte den größten Teil der Antwort aus (gemessen 1,7 kB je Flurstück). Wer einen Wert braucht, holt das Flurstück über /api/v1/flurstueck oder den Lagedaten-Auftrag erneut — dort kommt es mit vollständiger Belegkette.
gekappt: true heißt, die Obergrenze wurde erreicht
und im Ausschnitt können weitere Flurstücke liegen, die nicht dabei sind. Der Dienst
selbst sagt das nicht — er meldet numberMatched="unknown".
Liegt der Ausschnitt außerhalb der angebundenen Länder, kommt land: null
und eine leere Liste, ohne dass ein Landesdienst befragt wird.
GET /api/v1/flurstueck/bereich?bbox=7.2133,51.4809,7.2191,51.4827
{ "land": "NW", "anzahl": 216, "gekappt": false,
"flurstuecke": [ { "flurstueckskennzeichen": "05110701500301______",
"flaeche_amtlich_qm": 106.0, "lage_bez": "…",
"gemarkung_name": "Bochum",
"geometrie_wgs84": { "…": "…" } } ] }
GET /api/v1/flurstueck/nachbarn — angrenzende Flurstücke
| Parameter | Typ | Beschreibung |
|---|---|---|
| kennzeichen | Query, Pflicht | ALKIS-flstkennz des Ausgangsflurstücks, z. B. 054958030003440017__ |
15 Länder; das Präfix im Kennzeichen entscheidet (01 SH, 02 HH, 03 NI, 04 HB, 05 NW, 06 HE, 07 RP, 08 BW, 10 SL, 11 BE, 12 BB, 13 MV, 14 SN, 15 ST, 16 TH). Berlin führt das Kennzeichen 18-stellig, die übrigen 20-stellig. In Hamburg und Hessen ist die Suche über das Kennzeichen nicht möglich — deren amtlicher Dienst kann nicht nach Attributen filtern; dort führt nur der Weg über lat/lon, und die Antwort sagt das mit fehler_typ: kennzeichensuche_nicht_moeglich;
andere Präfixe liefern HTTP 400. Ermittelt über fes:Touches am amtlichen
ALKIS-Dienst — kein Pufferabstand, kein Toleranzwert. Das Ausgangsflurstück selbst ist
nicht in der Liste. Jeder Nachbar trägt geometrie_wgs84 für die Karte.
Grenze: Bei mehrteiligen Flurstücken wird der größte Teil
abgefragt; Nachbarn, die nur an einen Nebenteil grenzen, können fehlen. Das steht auch als
hinweis in der Antwort.
GET /api/v1/flurstueck/nachbarn?kennzeichen=054958030003440017__
{ "kennzeichen": "054958030003440017__", "land": "NW", "anzahl": 10,
"nachbarn": [ { "flurstueckskennzeichen": "05495803000304______",
"flaeche_amtlich_qm": 92.0, "lage_bez": "Domkloster 4",
"geometrie_wgs84": { "…": "…" } } ] }
GET /api/v1/bodenwert/auswahl — Bodenwert mehrerer Flurstücke, getrennt je Zone
| Parameter | Typ | Beschreibung |
|---|---|---|
| kennzeichen | Query, Pflicht | Ein oder mehrere ALKIS-Kennzeichen, durch Komma getrennt. Höchstens 25 je Anfrage, und alle im selben Bundesland — sonst HTTP 400. Rechnen Sie mit rund 7 Sekunden je Flurstück: Die Fläche wird beim Landesdienst mit zwölf Punkten abgetastet, mit Pacing. 25 Flurstücke sind also über zwei Minuten in einer Anfrage — für größere Auswahlen ist /api/v1/lagedaten gedacht, das als Auftrag läuft. |
15 Länder. Keine Gesamtsumme über Zonen hinweg — und das ist keine Lücke, sondern die Entscheidung: Eine Summe über mehrere Bodenrichtwertzonen stünde in keinem amtlichen Dokument, und wo die Zonen verschiedene Entwicklungszustände tragen, vermischte sie Bauland mit Land- und Forstwirtschaft. Innerhalb einer Zone wird summiert — dort wird derselbe amtliche Bodenrichtwert auf mehrere Flächen angewendet, das ist keine neue Ableitung.
zonen_ueberlappen: true heißt: dasselbe Flurstück
liegt in mehreren Zonen, die Flächen der Zonengruppen sind also nicht disjunkt. Wer die
Zonensummen dann addierte, zählte dieselbe Fläche doppelt. In Sachsen-Anhalt ist das der
Regelfall — großflächige Zonen der Land- und Forstwirtschaft liegen dort über den
kleinräumigen Bauland-Zonen.
GET /api/v1/bodenwert/auswahl?kennzeichen=150938145050080001__,15093814505011______
{ "land": "ST", "anzahl_flurstuecke": 3, "anzahl_zonen": 2,
"zonen": [
{ "brw_nummer": "03204303", "bodenrichtwertzone_name": "Breiter Weg/Hasselbachplatz",
"bodenrichtwert_eur_qm": 900.0, "flaeche_qm": 897.0, "bodenwert_eur": 807300.0,
"flurstuecke": ["150938145050080001__", "…"] },
{ "brw_nummer": "03207541", "bodenrichtwertzone_name": "Magdeburg_A",
"bodenrichtwert_eur_qm": 4.45, "flaeche_qm": 897.0, "bodenwert_eur": 3991.65,
"flurstuecke": ["150938145050080001__", "…"] }
],
"zonen_ueberlappen": true,
"flurstuecke_in_mehreren_zonen": ["150938145050080001__", "…"],
"hinweis_keine_gesamtsumme": "Je Zone wird getrennt ausgewiesen. …" }
Der Bodenwert ist rechnerisch, nicht amtlich: amtliche Fläche × amtlicher Bodenrichtwert. Amtlich sind nur die Eingangswerte.
GET /api/v1/grundsteuer — Grundsteuer B für ein unbebautes Grundstück
| Parameter | Typ | Beschreibung |
|---|---|---|
land | Text | Länderkürzel. Sechzehn Rechtslagen: fünf Länder haben ein eigenes Modell, vier eigene Steuermesszahlen. |
hebesatz_prozent | Zahl | Der Hebesatz der Gemeinde. Ohne ihn wird bis zum Messbetrag gerechnet — der ist gemeindeunabhängig. |
flaeche_qm, bodenrichtwert_eur_qm | Zahl | Der einfache Fall: ein Grundstück, eine Zone. Maßgeblich ist der Wert zum 1. Januar 2022 (§ 266 BewG), nicht der heutige. |
teil | Text, mehrfach | Je qm:brw:bezeichnung. Liegt ein Flurstück in mehreren Zonen, setzt das Finanzamt jeden Teil mit dem Wert seiner Zone an. Leerer Bodenrichtwert = für diese Zone fehlt der Stichtag; der Teil wird als Lücke ausgewiesen, nicht weggelassen. |
Das Ergebnis ist eine Überschlagsrechnung nach der gesetzlichen Formel, kein Bescheid. Den Grundsteuerwert stellt das Finanzamt fest. Nur unbebaute Grundstücke — bebaute brauchen Wohnfläche, Baujahr und Gebäudeart, die kein offener Dienst führt.
GET /api/v1/kaufnebenkosten — Grunderwerbsteuer, Notar, Grundbuch, Maklerrichtwert
| Parameter | Typ | Beschreibung |
|---|---|---|
kaufpreis | Zahl | Die Gegenleistung (§ 8 GrEStG) — nicht Bodenrichtwert × Fläche |
land | Text | Länderkürzel, z. B. NW. Ohne Land keine Steuer — es wird nicht geraten. |
objektart | Text | grundstueck (Vorgabe), wohnung_efh, sonstiges |
mit_makler, makler_prozent | bool, Zahl | Provision abschalten oder eigenen Käuferanteil setzen |
{ "summe": 58539.96, "summe_ohne_annahmen": 29979.96, "anteil_prozent": 14.63,
"bloecke": [ { "bezeichnung": "Grunderwerbsteuer", "betrag": 26000.0,
"ist_annahme": false, "grundlage": "6,5 % — Gesetz über die Festsetzung …" },
{ "bezeichnung": "Maklerprovision (Käuferanteil)", "betrag": 28560.0,
"ist_annahme": true, "grundlage": "7,14 % — Richtwert, kein festgesetzter Satz" } ] }
Drei Arten von Wahrheit in einer Antwort, und sie sind unterscheidbar.
ist_annahme trennt den Richtwert (Maklerprovision, frei verhandelbar) von den
gerechneten Beträgen: Die Grunderwerbsteuer ist ein amtlich festgesetzter Satz, die Notar- und
Grundbuchgebühren folgen dem GNotKG (Tabelle B nach § 34). summe_ohne_annahmen
nennt zusätzlich, was übrig bleibt, wenn man nur dem Gerechneten traut.
Keine Steuer- oder Rechtsberatung. Nicht enthalten sind Auslagen und die Finanzierungsgrundschuld — sie hängt am Darlehensbetrag, nicht am Kaufpreis.
GET /api/v1/laender — aktive Länder, Limits & Quellenvermerke
Das Feld aufrufer zeigt, welche Limits für Sie gelten (erkannt/anonym) — ideal zum Prüfen Ihres Keys.
aufrufer.verbleibend nennt je Topf
(teuer, guenstig), wie viel Ihres Stundenkontingents
noch frei ist und in wie vielen Sekunden der nächste Platz frei wird.
Diese Auskunft kostet nichts — dieser Endpunkt hat kein Limit.
Das ist der Unterschied zu den X-RateLimit-Kopfzeilen: Die stehen
an der Antwort des teuren Endpunkts, und der zählt jede Anfrage mit. Wer seinen
Rest dort erfragt, gibt dafür einen Abruf aus.
datenlage weist je Land aus, was der amtliche Dienst
wirklich hergibt: geometrie (Zonenfläche für die Karte), klartext
(Klartext oder nur amtliche Codes), historie (ist stichtag freigegeben)
und zeitreihe (zusätzlich mit Zonenfläche je Stichtag). Die Listen
historie_laender und zeitreihe_laender geben dasselbe kompakt.
wertentwicklung_laender ist eine
andere Angabe und deckt sich absichtlich nicht mit
zeitreihe_laender: Sie nennt die Länder, für die wir einen fertigen
Verlauf mitliefern — er hängt an den Zonen der Antwort
(zeitreihe je Zone) und kostet keine zusätzliche Abfrage.
Nordrhein-Westfalen steht nur hier, weil sein Abfragedienst keine Historie führt,
das Land die Jahrgänge aber zum Download stellt. Sachsen-Anhalt steht nur in
zeitreihe_laender, weil sein Dienst zwar stichtagsgenau antwortet,
aber keinen Massenabruf erlaubt.
GET /api/v1/marktdaten/liegenschaftszinssatz — amtlicher Liegenschaftszinssatz
| Parameter | Typ | Beschreibung |
|---|---|---|
| ags | Query, alternativ Pflicht | Amtlicher Gemeindeschlüssel, 8-stellig, z. B. 05315000 (Köln) |
| gaa | Query, alternativ Pflicht | Kennung des Gutachterausschussgebiets, z. B. 11400 |
| teilmarkt | Query, optional | Ein Teilmarkt, z. B. mfh. Ohne Angabe kommen alle elf |
Liegenschaftszinssätze nach § 21 Abs. 2 ImmoWertV, je
Gutachterausschussgebiet und Teilmarkt. Genau einer der Parameter ags
oder gaa ist anzugeben — beide oder keiner liefert HTTP 400. Der Wert ist aus
einem Berichtszeitraum abgeleitet, nicht von einem Stichtag; deshalb heißt das Feld
berichtszeitraum und nicht stichtag.
Ein Wert liegt genau dann vor, wenn wert
gefüllt ist. Ist es leer, nennt fehlanzeige den amtlichen Grund — „kein Markt“
und „zu wenige Fälle“ sind verschiedene Auskünfte, keine davon ist eine Null. Ist
unsicher gesetzt, hat der Herausgeber den Zinssatz selbst so gekennzeichnet.
fundstelle nennt Datei, Zeile und Feld, beleg_sha256 die
Prüfsumme der Quelldatei — damit ist jeder Wert im Original wiederfindbar.
Derzeit ausschließlich Nordrhein-Westfalen, siehe
abgedeckte_laender. Ein Gemeindeschlüssel außerhalb liefert HTTP 404 mit
fehler_typ = gemeinde_nicht_abgedeckt — nie eine Näherung aus einem
Nachbargebiet. Sind auf der Instanz noch keine Marktdaten erfasst, antwortet der Endpunkt
mit HTTP 503 und fehler_typ = bestand_leer, nicht mit einem leeren Ergebnis.
curl "https://www.lagedaten.de/api/v1/marktdaten/liegenschaftszinssatz?ags=05315000&teilmarkt=mfh"
{
"gebiet": {
"kennung": "11400", "name": "Köln", "gemeindeschluessel": "05315000",
"gutachterausschuss": "Der Gutachterausschuss für Grundstückswerte in der Stadt Köln"
},
"berichtszeitraum": { "von": "2024-01-01", "bis": "2024-12-31" },
"anzahl_teilmaerkte": 1,
"davon_mit_wert": 1,
"liegenschaftszinssaetze": [
{
"teilmarkt": "mfh",
"teilmarkt_klartext": "Mehrfamilienhäuser (inkl. gewerblicher Anteil bis 20% des Rohertrages)",
"wert": 2.9, "einheit": "prozent",
"spanne_von": null, "spanne_bis": null,
"fehlanzeige": null, "unsicher": false,
"standardabweichung": 1.0, "fallzahl": 92.0,
"fundstelle": "lzs.csv, Zeile 24, Feld mfh_lzs",
"beleg_sha256": "0080c6ca2976ac660655e30d7b6fe4658c4780abc55d01e88db4640e728fc7d6",
"abrufzeitpunkt": "2026-08-27T13:56:16+00:00"
}
],
"abgedeckte_laender": ["NW"],
"quelle": "https://www.opengeodata.nrw.de/produkte/infrastruktur_bauen_wohnen/boris/GMD/GMDNRW_CSV.zip",
"attribution": "(c) GeoBasis NRW",
"lizenz": "dl-de/zero-2.0",
"lizenz_uri": "https://www.govdata.de/dl-de/zero-2-0"
}
GET /api/v1/marktdaten/teilmaerkte — Teilmarkt-Kürzel im Klartext
Ohne Parameter. Liefert die Zuordnung der elf Kürzel für
teilmarkt: efh, zfh, rhdhh,
dreifh, mfh, ggg, handel,
buero, gegi, we_s, we_v. Kürzel und
Bezeichnung stammen aus der amtlichen Erläuterung des Herausgebers
(allgemein_erlaeuterung.xlsx, Blatt lzs) — übernommen ohne
den dort angehängten Zusatz „, Liegenschaftszinssatz“, der die Spalte benennt und
nicht den Teilmarkt. Die Bezeichnungen sind teils lang und fachlich abgegrenzt
(ggg etwa nennt den gewerblichen Anteil, der ihn gegen mfh
abgrenzt); sie werden hier nicht gekürzt wiedergegeben, sondern sind beim
Endpunkt abzurufen.
GET /health — Health-Check (status, version, zeit)
Suchen (verorten, nicht abrufen)
POST /api/v1/suche — mehrere Eingaben auf einmal verorten
Zwei Schritte statt einem. Dieser Endpunkt sagt, wo etwas liegt und — in den fünfzehn ALKIS-Ländern — welches Flurstück dort ist. Er liefert keinen Bodenrichtwert und keinen Bodenwert: Die kosten zusammen rund 7 Sekunden je Flurstück, und beim Suchen will niemand darauf warten. Sie kommen danach, gesammelt, über /api/v1/lagedaten.
Fehler stehen je Zeile, nicht für die ganze Anfrage — eine unauffindbare Adresse reißt die anderen nicht mit. Höchstens 10 Zeilen; für längere Listen gibt es den Stapelabruf.
Ohne Flurstück (nur Bayern hat kein angebundenes ALKIS) kommt nur die Position zurück. Das ist kein Fehler: Der Punkt allein genügt, um beim Abrufen den Bodenrichtwert an dieser Stelle zu holen.
POST /api/v1/suche
{ "auftraege": [ { "modus": "adresse", "adresse": "Domkloster 4, 50667 Köln" },
{ "modus": "adresse", "adresse": "Kleine Johannisstraße 4, 20457 Hamburg" },
{ "modus": "koordinate", "lat": 52.1247, "lon": 11.6350, "land": "ST" } ] }
{ "anzahl": 3, "ergebnisse": [
{ "eingabe": "Domkloster 4, 50667 Köln", "land": "NW", "lat": 50.94…, "lon": 6.95…,
"flurstueck": { "flurstueckskennzeichen": "054958030003440017__", "…": "…" },
"fehler_typ": null },
{ "eingabe": "Kleine Johannisstraße 4, 20457 Hamburg", "land": "HH",
"flurstueck": null, "fehler_typ": null },
{ "eingabe": "52.1247, 11.635", "land": "ST",
"flurstueck": { "flurstueckskennzeichen": "15093814510224______", "…": "…" } } ] }
Lagedaten-Auftrag (Fortschritt statt Wartezeit)
POST /api/v1/lagedaten — Eingabe und ausgewählte Flurstücke in einem Auftrag
Der Weg, den die Oberfläche seit dem 2026-09-05 benutzt. Er nimmt einen Zugangsweg (Adresse, Koordinate oder Flurstückskennung) und eine Liste bereits bekannter Kennzeichen, legt einen Auftrag an und antwortet sofort. Der Fortschritt wird abgeholt.
Warum ein Auftrag und keine Sofortantwort: Ein Flurstück kostet rund 7 Sekunden, weil seine Fläche beim amtlichen Dienst abgetastet wird. 25 Flurstücke wären über zwei Minuten — das überlebt keine HTTP-Anfrage. Der Auftrag meldet stattdessen nach jedem Schritt, wie weit er ist: verarbeitet von adressen ist ein gemessener Anteil, keine Zeitschätzung.
POST /api/v1/lagedaten
{ "auftraege": [ { "modus": "adresse", "adresse": "Domkloster 4, 50667 Köln" } ],
"kennzeichen": ["05110700600396______"],
"stichtag": null }
{ "job_id": "a1b2c3d4e5f6", "positionen": 14, "status": "wartet" }
GET /api/v1/lagedaten/{job_id} — Fortschritt und Ergebnis
Ohne Rate-Limit, damit ein Fortschrittsbalken sich nicht selbst ins Limit fragt. status ist wartet (es läuft bereits ein anderer Abruf — wir fragen die amtlichen Dienste bewusst nur nacheinander), laeuft, fertig oder fehler.
Einheiten statt einer Flurstücksliste: Ausgewählte Flurstücke, die sich berühren, erscheinen als ein verbund. Das ist eine selbst gewählte Zusammenstellung und kein amtliches Grundstück — das Liegenschaftskataster führt keinen Grundstücksbegriff dieser Art. Die Antwort trägt deshalb immer die Zusammensetzung mit; die Fläche ist die Summe der amtlichen Einzelflächen. Innerhalb eines Verbunds gilt die Zonenregel unverändert: getrennt je Zone, keine Gesamtsumme darüber hinweg.
GET /api/v1/lagedaten/a1b2c3d4e5f6
{ "status": "fertig", "adressen": 27, "verarbeitet": 27,
"ergebnis": {
"land": "NW",
"eingabe": { "…": "das gewohnte Abfrage-Ergebnis, oder null" },
"einheiten": [
{ "verbund": { "anzahl_flurstuecke": 2, "flaeche_amtlich_qm": 2307.0,
"kennzeichen": ["05110700600396______", "05110700800196______"],
"hinweis": "Zusammengefasst wurden Flurstücke, die sich berühren. …" },
"zonen": [ { "brw_nummer": "801", "bodenrichtwert_eur_qm": 2000.0, "…": "…" } ] } ],
"nicht_gefunden": [] } }
Rate-Limit & API-Keys
| Zugang | Adressen/Batch | Anfragen/Stunde | Zählung |
|---|---|---|---|
| anonym | 100 | 30 | pro IP-Adresse |
| mit gültigem API-Key | 500 | 100 | pro Schlüssel |
Der Key wird per Header X-API-Key: <key> übergeben; alternativ
Authorization: Bearer <key>. Jede Antwort enthält die Header
X-RateLimit-Limit und X-RateLimit-Remaining. Firmenkunden hinter einem
gemeinsamen Proxy werden pro Schlüssel gezählt, nicht pro IP.
Einen Test-Key können Sie formlos per E-Mail anfragen: kontakt@lagedaten.de.
CSV-Format für Batch-Uploads
CSV (UTF-8 oder Latin-1), eine Adresse je Zeile. Erste Zeile darf die Kopfzeile
adresse sein (optional). Duplikate werden entfernt. Maximal 1 MB.
adresse
Domplatz 1, 39104 Magdeburg
Aegidientorplatz 1, 30159 Hannover
Rathausstraße 15, 10178 Berlin
Trennzeichen: Komma und Semikolon werden erkannt — das Semikolon ist die Voreinstellung von Excel in deutscher Systemumgebung. Erkannt wird es über die Häufigkeit in der gesamten Datei; ist das Ergebnis nicht eindeutig, wird das Komma angenommen und in der Antwort als unsicher gekennzeichnet.
Mehrere Spalten: Trägt die Kopfzeile
adresse (an beliebiger Stelle, Groß-/Kleinschreibung egal), wird
nur diese Spalte gelesen — eine Objekt-ID oder Fläche daneben wandert nicht in
die Adresse. Ist adresse die einzige Spalte, dürfen die Adressen
selbst Kommas enthalten; die Felder einer Zeile werden dann zusammengefügt. Ohne
erkennbare Kopfzeile werden die Felder einer Zeile ebenfalls zusammengefügt.
Steht die Adresse über mehrere Spalten verteilt (etwa
adresse;plz;ort), wird nur adresse gelesen — dann gehört
die vollständige Adresse in eine einzige Spalte.
Die Antwort sagt, was verstanden wurde.
POST /api/v1/batch liefert ein Feld gelesen mit
erkanntes_trennzeichen, adressspalte,
gelesene_zeilen, duplikate_entfernt,
kopfzeile_verworfen und beispiel_erste_adresse. An der
Beispieladresse ist auf einen Blick zu sehen, ob die Datei richtig gelesen wurde —
bevor der Lauf durch ist.
"gelesen": {
"gelesene_zeilen": 42, "duplikate_entfernt": 2,
"erkanntes_trennzeichen": ";", "trennzeichen_sicher": true,
"adressspalte": "adresse", "kopfzeile_verworfen": false,
"beispiel_erste_adresse": "Domplatz 1, 39104 Magdeburg",
"modus": "adresse", "ignorierte_spalten": []
}
Drei Suchmodi auch im Batch. Welchen eine Datei benutzt, ergibt sich aus ihren Spaltenüberschriften — erkannt werden (Groß-/Kleinschreibung egal):
| Modus | Erkannte Spaltennamen | Gilt für |
|---|---|---|
| adresse | adresse, address, adressen | alle Länder |
| koordinate | lat, latitude, breitengrad, breite · lon, lng, long, longitude, laengengrad, laenge · dazu land bzw. bundesland | alle Länder, land erforderlich |
| flurstueck | gemarkung · flur · flurstueck, flurstuecksnummer, zaehler · nenner · land | 15 Länder |
Eine Datei benutzt genau einen Modus. Stehen Spalten
mehrerer Modi nebeneinander, gewinnt der spezifischere (Koordinate vor Flurstück vor Adresse)
und die übergangenen werden in ignorierte_spalten genannt — eine Datei mit
adresse und lat/lon wäre sonst eine Wundertüte: Welcher Weg
gelaufen ist, sähe man erst am Ergebnis. modus nennt den erkannten Weg,
beispiel_erste_adresse zeigt die erste gelesene Eingabe im Klartext.
lat,lon,land
52.1249036,11.6356315,ST
50.941357,6.958307,NW
gemarkung,flur,flurstueck,nenner,land
Köln,030,344,17,NW
Copy-&-Paste-Beispiele
curl
# Einzelabfrage, ohne Key
curl "https://IHRE-DOMAIN/api/v1/abfrage?adresse=Domplatz%201%2C%2039104%20Magdeburg"
# Einzelabfrage, mit API-Key
curl -H "X-API-Key: IHR_KEY" \
"https://IHRE-DOMAIN/api/v1/abfrage?adresse=Domplatz%201%2C%2039104%20Magdeburg"
# Batch-Upload (mit Key via Bearer)
curl -H "Authorization: Bearer IHR_KEY" \
-F "datei=@adressen.csv" \
"https://IHRE-DOMAIN/api/v1/batch"
Python (requests)
import requests
BASIS = "https://IHRE-DOMAIN"
# ohne Key
r = requests.get(f"{BASIS}/api/v1/abfrage",
params={"adresse": "Domplatz 1, 39104 Magdeburg"})
print(r.json())
# mit Key
kopf = {"X-API-Key": "IHR_KEY"}
r = requests.get(f"{BASIS}/api/v1/abfrage",
params={"adresse": "Domplatz 1, 39104 Magdeburg"},
headers=kopf)
print(r.headers.get("X-RateLimit-Remaining"), r.json())
# Batch mit Key
with open("adressen.csv", "rb") as f:
r = requests.post(f"{BASIS}/api/v1/batch",
files={"datei": f}, headers=kopf)
job = r.json()["job_id"]
JavaScript (fetch)
const BASIS = "https://IHRE-DOMAIN";
// ohne Key
const r1 = await fetch(
`${BASIS}/api/v1/abfrage?adresse=` +
encodeURIComponent("Domplatz 1, 39104 Magdeburg"));
console.log(await r1.json());
// mit Key
const r2 = await fetch(
`${BASIS}/api/v1/abfrage?adresse=` +
encodeURIComponent("Domplatz 1, 39104 Magdeburg"),
{ headers: { "X-API-Key": "IHR_KEY" } });
console.log(r2.headers.get("X-RateLimit-Remaining"), await r2.json());
// Batch mit Key
const fd = new FormData();
fd.append("datei", dateiObjekt); // File aus
const r3 = await fetch(`${BASIS}/api/v1/batch`,
{ method: "POST", body: fd, headers: { "X-API-Key": "IHR_KEY" } });
Fehlercodes
| Code | Bedeutung |
|---|---|
| 400 | Ungültige Eingabe (keine Adressen erkannt oder zu viele Adressen im Batch) |
| 401 | Ungültiger API-Key |
| 404 | Job oder Ergebnis nicht gefunden |
| 413 | Upload zu groß (max. 1 MB) |
| 429 | Rate-Limit überschritten (siehe X-RateLimit-*-Header) |
Fehler werden als JSON { "detail": "..." } mit deutscher Meldung zurückgegeben.
Ein Abruffehler erscheint nie als „0 Treffer“, sondern als Fehler-/Hinweistext im jeweiligen Ergebnis.
Quellenvermerkspflicht
Die gelieferten Werte stammen unverändert aus amtlichen Diensten der Bundesländer und stehen
unter Lizenzen wie dl-de/by-2-0 bzw. dl-de/zero-2.0. Bei jeder Weiterverwendung
der Daten sind die zugehörigen Quellen-/Namensnennungen mitzuführen. Die vollständige Liste liefert
GET /api/v1/laender (Feld quellenvermerke) und steht in jedem Export.
Maschinenlesbar liegt sie je Treffer im Feld attribution vor.
Geocoding: Koordinaten stammen von © OpenStreetMap-Mitwirkenden (ODbL), ermittelt über Nominatim / Photon — bei Weitergabe von Koordinaten mitzuführen.