Interne REST-API
Datenversorgung für interne Tools — nur mit gültigem API-Key zugänglich.
Öffentliche, rate-limitierte API ohne Key: Öffentliche API-Dokumentation →
https://api.gemeindeverzeichnis.de/api/internalAuthentication
https://api.gemeindeverzeichnis.de/api/internal/ — kein Redirect, kein Header-Verlust. Bitte immer diese Domain verwenden.Alle Anfragen benötigen einen Bearer-Token im Authorization-Header.
Authorization: Bearer <INTERNAL_API_KEY>Bei fehlendem oder falschem Key: HTTP 404 (nicht 401) — um die Existenz des Endpunkts nicht zu verraten.
Rate Limits
Die interne API ist vom öffentlichen Rate-Limiter ausgenommen. Es gibt kein erzwungenes Limit pro Minute. Die List- und Einzeleintrags-Endpunkte werden 5 Minuten serverseitig gecacht. Der /changes-Endpunkt ist immer unkached (no-store).
GET/api/internal/gemeinden
Gemeindeliste mit Filtern
Gibt eine paginierte Liste aller Gemeinden zurück. Unterstützt Filterung nach Bundesland, Gemeindetyp und Freitextsuche. Nutzt Cursor-basierte Paginierung.
Query-Parameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| bundesland | string | Nein | AGS des Bundeslandes (2-stellig, z.B. 08 für BW) |
| satzart | string | Nein | 60 = Gemeinde, 50 = Gemeindefreies Gebiet |
| q | string | Nein | Freitextsuche in Name, normalisiertem Namen, AGS |
| cursor | string | Nein | AGS des letzten Elements der Vorseite |
| limit | integer | Nein | Einträge pro Seite. Standard: 100, max: 500 |
| fields | string | Nein | Sparse Fieldset: kommagetrennte Feldliste (z.B. fields=ags,name,email_pattern). Nur Array-Syntax wird nicht unterstützt. |
Beispiel
curl -H "Authorization: Bearer <KEY>" \
"https://api.gemeindeverzeichnis.de/api/internal/gemeinden?bundesland=08&limit=50"Response
{
"data": [
{
"ags": "08111000",
"name": "Stuttgart",
"bundesland_text": "Baden-Württemberg",
"population_total": 626275,
"chief_admin_name_full": "Dr. Frank Nopper",
"chief_admin_job_title": "Oberbürgermeister",
"chief_admin_gender": "male",
"chief_admin_salutation_german": "Sehr geehrter Herr Dr. Nopper",
"chief_admin_email": "oberbuergemeister@stuttgart.de",
"contact_email": "poststelle@stuttgart.de",
"website": "https://www.stuttgart.de"
}
],
"meta": {
"total": 1101,
"count": 50,
"cursor": "08115081",
"has_more": true,
"generated_at": "2026-04-22T01:48:00.000Z",
"data_freshness": "2026-04-22T02:00:00Z"
}
}Solange meta.has_more === true, den Wert aus meta.cursor als ?cursor= im nächsten Request übergeben.
GET/api/internal/gemeinden/{ags}
Einzeleintrag per AGS
Gibt einen einzelnen Eintrag zurück. Nutzt einen In-Memory-Hash-Map für O(1)-Lookup — schneller als der Listendpunkt für einzelne Einträge.
Pfad-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
| ags | string | 8-stellig (Gemeinde) oder 12-stellig (Verbandsgemeinde/VVG), z.B. 08111000 |
Beispiel
curl -H "Authorization: Bearer <KEY>" \
"https://api.gemeindeverzeichnis.de/api/internal/gemeinden/08111000"Response
{
"data": {
"ags": "08111000",
"name": "Stuttgart",
...
}
}Response-Header
| Header | Beschreibung |
|---|---|
| X-Wikidata-Timestamp | ISO-Zeitstempel des letzten Wikidata-Updates für diesen Eintrag |
| X-Data-Generated-At | Zeitstempel der Response-Generierung |
Gibt 400 zurück wenn AGS nicht 8-stellig numerisch ist. Gibt 404 zurück wenn die Gemeinde nicht gefunden wurde.
GET/api/internal/gemeinden/changes
Delta-Sync: geänderte Einträge
Gibt nur Gemeinden zurück, deren wikidata_person_timestamp nach dem angegebenen since-Zeitpunkt liegt. Ideal für tägliche Sync-Jobs — nur abholen was sich geändert hat. Response ist nie gecacht.
Query-Parameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| since | string | Ja | ISO 8601 Datetime, z.B. 2026-04-20T00:00:00Z |
| cursor | string | Nein | AGS des letzten Elements der Vorseite |
| limit | integer | Nein | Standard: 100, max: 500 |
Beispiel
curl -H "Authorization: Bearer <KEY>" \
"https://api.gemeindeverzeichnis.de/api/internal/gemeinden/changes?since=2026-04-20T00:00:00Z"Response
{
"data": [ { "ags": "...", ... } ],
"meta": {
"since": "2026-04-20T00:00:00.000Z",
"total_changed": 47,
"count": 47,
"cursor": null,
"has_more": false,
"generated_at": "2026-04-22T01:48:00.000Z"
}
}Nur Einträge mit bekanntem wikidata_person_timestamp erscheinen im Changes-Feed. Einträge ohne Wikidata-Treffer werden ausgelassen.
GET/api/internal/pendler/{ags}
Pendler-Zeitreihe 2013–2023 (API-Key erforderlich)
Gibt historische Ein- und Auspendlerzahlen einer Gemeinde zurück. Quelle: Pendleratlas / Bundesagentur für Arbeit, Daten für 2013–2023. Deckt ca. 10.750 Gemeinden ab (AGS 8-stellig).
private, max-age=300.Pfad-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
| ags | string | 8-stellig numerisch, z.B. 09162000 für München |
Query-Parameter
| Parameter | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| year | integer | Nein | Einzeljahr filtern (2013–2023). Ohne year: alle Jahre. |
Alle Jahre (ohne year-Parameter)
curl -H "Authorization: Bearer <KEY>" \
"https://api.gemeindeverzeichnis.de/api/internal/pendler/09162000"
# Response
{
"ags": "09162000",
"data": [
{ "year": 2013, "commute_in": 358212, "commute_out": 136819, "commute_within": 421338, "commute_saldo": 221393 },
{ "year": 2014, "commute_in": 368431, ... },
...
{ "year": 2023, "commute_in": 457594, "commute_out": 203136, "commute_within": 502434, "commute_saldo": 254458 }
]
}Einzeljahr (?year=2020)
curl -H "Authorization: Bearer <KEY>" \
"https://api.gemeindeverzeichnis.de/api/internal/pendler/09162000?year=2020"
# Response
{
"ags": "09162000",
"year": 2020,
"commute_in": 413547,
"commute_out": 183633,
"commute_within": 483743,
"commute_saldo": 229914
}Fehler bei unbekanntem Jahr
# year=1990 existiert nicht
{
"error": "Kein Datensatz für Jahr 1990",
"available_years": [2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023]
}Felder
| Feld | Typ | Beschreibung |
|---|---|---|
| commute_in | integer | Einpendler (sozialversicherungspflichtig Beschäftigte, die von außerhalb einpendeln) |
| commute_out | integer | Auspendler (Beschäftigte, die in eine andere Gemeinde auspendeln) |
| commute_within | integer | Binnenpendler (wohnen und arbeiten in der gleichen Gemeinde) |
| commute_saldo | integer | Pendlersaldo: commute_in − commute_out (positiv = Einpendlerüberschuss) |
| year | integer | Berichtsjahr (2013–2023) |
Die commute_*-Felder stehen auch im Gemeinde-Einzelabruf (/api/internal/gemeinden/{ags}) zur Verfügung und enthalten jeweils den Wert des neuesten verfügbaren Jahres. Für die vollständige Zeitreihe diesen Endpunkt verwenden.
Felder
Alle Endpunkte geben dieselbe Feldmenge zurück (Allowlist in frontend/lib/data.ts).
Identifikatoren
| Feld | Beschreibung |
|---|---|
| ags | 8-stellig für Gemeinden, 12-stellig für Verbandsgemeinden/VVG — VARCHAR(12) verwenden |
| ars | 12-stelliger Amtlicher Regionalschlüssel |
| uuid | Interne Datensatz-ID |
| wikidata_id | Wikidata-Kennung (z.B. Q1022) |
| wikipedia_url | Wikipedia-URL der Gemeinde |
Typ & Klassifikation
| Feld | Beschreibung |
|---|---|
| satzart | Hierarchie-Ebene: 10=Land, 40=Kreis, 50=Gemeindeverband, 60=Gemeinde |
| satzart_text | Bezeichnung der Hierarchie-Ebene (z.B. "Gemeinde") |
| textkennzeichen | Numerischer Typ-Code |
| textkennzeichen_text | ✅ Empfohlener Klartext-Gattungsbegriff (z.B. "Kreisfreie Stadt", "Gemeinde", "Markt") — Nachfolger von name_raw |
| is_city | true wenn Gemeinde eine Stadt ist (aus textkennzeichen_text abgeleitet) |
| is_display_relevant | true wenn Eintrag für Verzeichnisanzeige vorgesehen |
Name
| Feld | Beschreibung |
|---|---|
| name | Anzeigename (z.B. Stuttgart) |
| name_raw | ⚠ Deprecated — inkonsistentes Format (6+ Varianten), textkennzeichen_text verwenden |
| name_base | Basisname ohne Zusätze |
| normalized_display_name | Normalisiert für Suche |
| city_type | Stadttyp-Zusatz (z.B. Landeshauptstadt) |
Geografie
| Feld | Beschreibung |
|---|---|
| bundesland | Bundesland-Kürzel (z.B. BW) |
| bundesland_text | Bundesland-Name |
| bundesland_ags | Bundesland-AGS (2-stellig) |
| kreis_ags | Kreis-AGS (5-stellig) |
| landkreis_text | Landkreis-Name |
| gemeindeverband_text | Gemeindeverbands-Name |
| plz_array | Array aller PLZ der Gemeinde |
| area_km2 | Fläche in km² |
Bevölkerung
| Feld | Beschreibung |
|---|---|
| population_total | Einwohnerzahl gesamt |
| population_male | Einwohner männlich |
| population_female | Einwohner weiblich |
| population_density | Einwohner pro km² |
| population_reference_date | Stichtag der Bevölkerungsdaten |
| population_source | Datenquelle |
Pendler-Daten
| Feld | Beschreibung |
|---|---|
| commute_in | Einpendler (neuestes verfügbares Jahr, siehe commute_year) |
| commute_out | Auspendler |
| commute_within | Binnenpendler |
| commute_saldo | Pendlersaldo (commute_in − commute_out) |
| commute_year | Berichtsjahr des aktuellen Werts (i.d.R. 2023). Zeitreihe: GET /api/pendler/{ags} |
Adresse Rathaus
| Feld | Beschreibung |
|---|---|
| address_verwaltungssitz | Verwaltungssitz |
| address_strasse | Straße und Hausnummer |
| address_plz | PLZ des Rathauses |
| address_ort | Ort des Rathauses |
Kontakt
| Feld | Beschreibung |
|---|---|
| contact_phone | Telefon |
| contact_fax | Fax |
| contact_email | Allgemeine E-Mail-Adresse |
| website | Website der Gemeinde |
| email_domain | E-Mail-Domain (z.B. stuttgart.de) |
| email_pattern | Erkanntes E-Mail-Muster |
Bürgermeister / Verwaltungsleitung
| Feld | Beschreibung |
|---|---|
| chief_admin_name_full | Vollständiger Name (z.B. Dr. Frank Nopper) |
| chief_admin_name_prefix | Titel (z.B. Dr.) |
| chief_admin_name_given | Vorname |
| chief_admin_name_middle | Zweiter Vorname |
| chief_admin_name_family | Nachname |
| chief_admin_name_suffix | Namenssuffix |
| chief_admin_job_title | Amtsbezeichnung (z.B. Oberbürgermeister) |
| chief_admin_party | Partei |
| chief_admin_gender | Geschlecht: male / female / null (wenn confidence < 70) |
| chief_admin_gender_confidence | Konfidenz-Score 0–100 |
| chief_admin_gender_source | Herkunft des Genders: wikidata | firstname_classifier | null |
| chief_admin_salutation_german | Deutsche Anrede (null wenn confidence < 70) |
| chief_admin_salutation_neutral | ✅ Immer befüllt: gendered Anrede wenn confidence ≥ 70, sonst "Sehr geehrte Damen und Herren" |
| chief_admin_salutation_en | Englische Anrede (z.B. "Dear Mr Nopper" / "Dear Sir or Madam") |
| chief_admin_email | Direkte E-Mail-Adresse |
| chief_admin_birth_year | Geburtsjahr |
| chief_admin_amtsantritt | Jahr des Amtsantritts |
| chief_admin_wahlvorschlag | Wahlvorschlag / Partei |
| chief_admin_professional_status | Beruflicher Status |
| data_quality_flags | Array mit Flags: gender_unresolved | gender_low_confidence | no_wikidata | stale_wikidata | no_chief_admin |
Zeitstempel
| Feld | Beschreibung |
|---|---|
| wikidata_person_timestamp | Letztes Wikidata-Update für diese Person |
| last_updated | Letztes Update des Eintrags |
| reference_date | Stichtag der Gesamtdaten |
Sync-Workflow
Empfohlenes Muster für einen Laravel-Consumer: vollständiger Erstimport über den Listendpunkt, danach tägliche Delta-Updates über den Changes-Endpunkt.
Erstimport
$cursor = null;
do {
$response = Http::withToken($apiKey)
->get('https://api.gemeindeverzeichnis.de/api/internal/gemeinden', [
'limit' => 500,
'cursor' => $cursor,
]);
$body = $response->json();
$cursor = $body['meta']['cursor'];
// Datensätze in Datenbank speichern
} while ($body['meta']['has_more']);Täglicher Delta-Sync
$since = now()->subDay()->toIso8601String();
$response = Http::withToken($apiKey)
->get('https://api.gemeindeverzeichnis.de/api/internal/gemeinden/changes', [
'since' => $since,
]);
$changed = $response->json()['data'];
// Nur geänderte Einträge aktualisierenFehlercodes
| Status | Bedeutung |
|---|---|
| 200 | Erfolg |
| 400 | Ungültiger Parameter (z.B. falsches AGS-Format oder fehlendes since) |
| 404 | Nicht gefunden — oder unautorisiert (interne Endpunkte geben bei ungültigem Key 404 zurück) |
| 500 | Interner Serverfehler |