GET /v1/buildings — Gebäude per Bounding-Box
Föderierte GeoJSON-FeatureCollection aller LoD2-Surfaces innerhalb einer WGS84-Bounding-Box. „Föderiert” bedeutet: die API durchsucht alle Bundesländer mit import_finished_at IS NOT NULL parallel und mischt die Ergebnisse. Aus Konsumentensicht: eine API für 16 Bundesländer.
Wichtig — Surface-Level: Jedes Feature ist eine einzelne Surface (Wall/Ground/Roof), nicht ein ganzes Gebäude. Mehrere Surfaces gehören über building_id zum selben Gebäude — zum Rekonstruieren eines Gebäudes nach building_id gruppieren.
Wann verwenden
- Karten-Frontends, die Gebäude-Footprints + 3D-Geometrie auf einer Region brauchen.
- Analyse-Scripts (Building-Count pro Stadtteil, Attribut-Filter).
- 3D-Renderer mit eigener Triangulation (statt fertiges 3D-Tiles aus
/v1/tilesets).
Wenn du eine bbox > ~1 km² brauchst → eher /v1/tilesets verwenden (3D-Tiles streamen). /v1/buildings ist für detail-orientierte, kleinflächige Queries.
Examples
curl — kleine bbox Frankfurt-Mitte
curl -s "https://api.lodapi.de/v1/buildings?bbox=8.66,50.108,8.665,50.111&limit=50" | jq '.count, .features[0].properties'
Python — bbox + Pagination
import httpx
def fetch_all_buildings(bbox: str, limit: int = 1000):
cursor = None
while True:
params = {"bbox": bbox, "limit": limit}
if cursor:
params["cursor"] = cursor
r = httpx.get("https://api.lodapi.de/v1/buildings", params=params)
r.raise_for_status()
data = r.json()
yield from data["features"]
cursor = data["lodapi"]["next"]
if not cursor:
break
for feature in fetch_all_buildings("8.66,50.108,8.69,50.13"):
print(feature["id"], feature["properties"]["surface_class"])
TypeScript — Cesium-Anbindung
import * as Cesium from "cesium";
const r = await fetch(
"https://api.lodapi.de/v1/buildings?bbox=8.66,50.108,8.665,50.111&limit=200"
);
const fc = await r.json();
const ds = await Cesium.GeoJsonDataSource.load(fc, { clampToGround: false });
viewer.dataSources.add(ds);
Parameters
| Parameter | In | Type | Required | Default | Beschreibung |
|---|---|---|---|---|---|
bbox | query | string | yes | — | WGS84 minLon,minLat,maxLon,maxLat (Komma-separiert) |
limit | query | int | no | 100 | 1 ≤ limit ≤ 1000 |
cursor | query | string | no | — | Opaque cursor aus lodapi.next des vorherigen Calls |
Response
200 OK · application/json — GeoJSON-FeatureCollection. Schema-Quelle openapi.json.
{
"type": "FeatureCollection",
"bbox": [8.66, 50.108, 8.665, 50.111],
"count": 47,
"limit": 100,
"features": [
{
"type": "Feature",
"id": "DEHE_LOD2_45292_GMLID_1",
"geometry": {
"type": "MultiPolygon",
"coordinates": [[[ [8.661, 50.108, 105.2], ... ]]]
},
"properties": {
"surface_id": 12345678,
"building_id": 987654,
"gmlid": "DEHE_LOD2_45292_GMLID_1",
"surface_class": 712,
"bundesland": "he",
"lod": "LoD2"
}
}
],
"lodapi": {
"attribution": [
{
"source": "HLBG",
"license": "DL-DE/Zero 2.0",
"url": "https://www.govdata.de/dl-de/zero-2-0",
"tiles_count": 1
}
],
"next": "aGU6MTIzNDU2Nzg="
}
}
Attribution
Jede Antwort enthält lodapi.attribution[] mit einem Eintrag pro Bundesland, das in den features[] vorkommt. Die Felder:
| Feld | Bedeutung |
|---|---|
source | Behörden-/Geobasis-Stelle (z.B. „HLBG”, „BezReg Köln”) |
license | DL-DE/Zero 2.0, CC BY 4.0 oder DL-DE BY 2.0 |
url | Lizenz-URL für Verweis im UI |
tiles_count | nicht verwendet bei /v1/buildings (immer 1) |
Frontend-Pflicht: Bei license != DL-DE/Zero 2.0 muss der source-String sichtbar in der Map-Footer-Zeile stehen (siehe Attribution-Guide).
Cursor-Pagination
/v1/buildings verwendet opaque cursor pagination statt OFFSET, weil OFFSET auf großen PostGIS-bbox-Resultaten pathologisch langsam ist. Der Cursor ist ein base64-codiertes <bundesland>:<surface_id>-Tupel — clients sollten ihn als opak behandeln.
Achtung: Der Cursor ist federation-aware (breaking change ggü. Phase-1-Clients, siehe README). Cursor aus altem Format führen zu 400.
Stolperdrähte
limit > 1000→ 422. Validation Error, RFC-7807-Format.bboxohne korrekte 4 Komma-Werte → 400 mit Hinweis aufminLon,minLat,maxLon,maxLat.- bbox-Area-Limit gibt es nicht auf
/v1/buildings— aber: bei sehr großen bboxen werden viele Pages gezogen. Für Großflächen-Use-Cases ist/v1/tilesetsder richtige Pfad (3D-Tiles streamen statt FeatureCollection). features[0].properties.bundeslandist der unprefixte 2-Buchstaben-Code ("he","nrw", …), keinerlei BL-Prefix in den IDs in dieser Property.- Surface-Level, nicht Building-Level: ein Feature = eine Surface.
properties.surface_classist die CityGML-ObjectClass-ID:709= WallSurface,710= GroundSurface,712= RoofSurface. Zum Rekonstruieren eines kompletten Gebäudes die Features nachbuilding_idgruppieren (oder direkt/v1/buildings/{gmlid}für die aggregierte Geometrie nutzen). - Nur Geometrie + Klassifikation: Die Properties sind
{surface_id, building_id, gmlid, surface_class, bundesland, lod}. Per-Building-Sachattribute sind im Slim-Datenbestand nicht enthalten; ältere Feldnamen wieroof_type/tile_x/tile_y/feature_idexistieren nicht.
Verwandte Endpoints
GET /v1/buildings/{gmlid}— Single-Building-Detail.GET /v1/buildings/3d.glb— bbox → einzelnes GLB-Mesh.GET /v1/tilesets— 3D-Tiles-Streaming für große Flächen.