{
  "openapi": "3.1.0",
  "info": {
    "title": "Wahlergebnisse Niedersachsen",
    "version": "1.0.0",
    "description": "Kommunalwahlergebnisse der niedersächsischen Landkreise und kreisfreien Städte, aufbereitet aus den amtlichen Wahlpräsentationen (votemanager). Jede Zahl trägt einen Namen, jede Ebene hat dasselbe Format, alles gibt es auch als CSV. Der Kreis ist das erste Segment jedes Pfades. Nur Lesezugriffe, keine Anmeldung.",
    "contact": {
      "url": "https://github.com/levino/wahlergebnisse/issues"
    },
    "license": {
      "name": "Amtliche Ergebnisse der Wahlleitungen; Geodaten siehe /api/v1/",
      "url": "https://demo.wahlergebnisse.levinkeller.de/api/v1/"
    }
  },
  "servers": [
    {
      "url": "https://demo.wahlergebnisse.levinkeller.de/api/v1"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "Einstieg mit allen Pfaden und Lizenzen",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/termine": {
      "get": {
        "summary": "Verfügbare Wahltermine",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/kreise": {
      "get": {
        "summary": "Landkreise und kreisfreie Städte mit ihren Wahlleitungen",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/{kreis}": {
      "get": {
        "summary": "Ein Kreis mit seinen Wahlleitungen, den kreisweiten Wahltagen (termine) und den Wahltagen einzelner Wahlleitungen (behoerden[].termine)",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Unbekannter Kreis"
          }
        }
      }
    },
    "/{kreis}/{termin}": {
      "get": {
        "summary": "Überblick: Stand und Auszählfortschritt",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Unbekannter Termin"
          }
        }
      }
    },
    "/{kreis}/{termin}/behoerden": {
      "get": {
        "summary": "Wahlleitungen mit ihren Wahlen",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/{kreis}/{termin}/wahlen": {
      "get": {
        "summary": "Alle Wahlen des Termins",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "name": "behoerde",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Slug oder AGS"
          },
          {
            "name": "typ",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "landrat",
                "landrat-stichwahl",
                "kreistag",
                "buergermeister",
                "buergermeister-stichwahl",
                "rat",
                "ortsrat",
                "sonstige"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/{kreis}/{termin}/{behoerde}/{wahl}": {
      "get": {
        "summary": "Eine Wahl mit Gesamtergebnis",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "$ref": "#/components/schemas/BehoerdeParam"
          },
          {
            "$ref": "#/components/schemas/WahlParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Wahl"
                }
              }
            }
          },
          "404": {
            "description": "Unbekannt"
          }
        }
      }
    },
    "/{kreis}/{termin}/{behoerde}/{wahl}/gebiete": {
      "get": {
        "summary": "Alle Gebiete der Wahl mit Ergebnis (JSON oder CSV)",
        "description": "Mit format=csv kommt eine flache Tabelle: eine Zeile je Gebiet und Partei, Semikolon-getrennt, UTF-8 mit BOM. Die Bewerberinnen und Bewerber (kandidaten) stehen nur in der JSON-Fassung.",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "$ref": "#/components/schemas/BehoerdeParam"
          },
          {
            "$ref": "#/components/schemas/WahlParam"
          },
          {
            "name": "ebene",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "kreis",
                "gemeinde",
                "wahlbereich",
                "ortsteil",
                "wahlbezirk"
              ]
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/{kreis}/{termin}/{behoerde}/{wahl}/gebiete/{gebiet}": {
      "get": {
        "summary": "Ein einzelnes Gebiet",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "$ref": "#/components/schemas/BehoerdeParam"
          },
          {
            "$ref": "#/components/schemas/WahlParam"
          },
          {
            "name": "gebiet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Gebiets-Id aus der Gebiete-Liste, z. B. ebene_6_id_3119"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ergebnis"
                }
              }
            }
          }
        }
      }
    },
    "/{kreis}/{termin}/{behoerde}/wahlraeume": {
      "get": {
        "summary": "Wahllokale einer Behörde",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "$ref": "#/components/schemas/BehoerdeParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/{kreis}/{termin}/ereignisse": {
      "get": {
        "summary": "Ticker der eingegangenen Schnellmeldungen",
        "parameters": [
          {
            "$ref": "#/components/schemas/KreisParam"
          },
          {
            "$ref": "#/components/schemas/TerminParam"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          },
          {
            "name": "behoerde",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/geo/{datei}": {
      "get": {
        "summary": "Geodaten als GeoJSON",
        "parameters": [
          {
            "name": "datei",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "gemeinden.geojson",
                "ortsteile.geojson",
                "wahllokale.geojson"
              ]
            }
          },
          {
            "name": "kreis",
            "in": "query",
            "description": "5- oder 8-stelliger Kreisschlüssel; ohne ihn kommt ganz Niedersachsen",
            "schema": {
              "type": "string",
              "example": "03254"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {},
    "schemas": {
      "KreisParam": {
        "name": "kreis",
        "in": "path",
        "required": true,
        "description": "Landkreis oder kreisfreie Stadt, als Slug (hildesheim) oder 8-stelliger Schlüssel (03254000). Alle unter /kreise; Kreise mit vorhanden=false liefern keine Ergebnisse.",
        "schema": {
          "type": "string",
          "enum": [
            "braunschweig",
            "salzgitter",
            "wolfsburg",
            "gifhorn",
            "goslar",
            "helmstedt",
            "northeim",
            "peine",
            "wolfenbuettel",
            "goettingen",
            "region-hannover",
            "diepholz",
            "hameln-pyrmont",
            "hildesheim",
            "holzminden",
            "nienburg",
            "schaumburg",
            "celle",
            "cuxhaven",
            "harburg",
            "luechow-dannenberg",
            "lueneburg",
            "osterholz",
            "rotenburg",
            "heidekreis",
            "stade",
            "uelzen",
            "verden",
            "delmenhorst",
            "emden",
            "oldenburg-stadt",
            "osnabrueck-stadt",
            "wilhelmshaven",
            "ammerland",
            "aurich",
            "cloppenburg",
            "emsland",
            "friesland",
            "grafschaft-bentheim",
            "leer",
            "oldenburg-land",
            "osnabrueck-land",
            "vechta",
            "wesermarsch",
            "wittmund"
          ]
        }
      },
      "TerminParam": {
        "name": "termin",
        "in": "path",
        "required": true,
        "description": "Wahltermin. 2026: Landrats-, Kreistags-, Bürgermeister-, Rats- und Ortsratswahlen am 13. September 2026 – 2025-12-14: Ortsratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2025-10-12: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2025-10-05: Ortsratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2025-02-23: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2024-10-27: Ortsratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2024-06-09: Ortsratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2023-03-05: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2022-10-09: Landratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2022-03-06: Ortsratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2022-02-27: Kreistagswahl in 8 Wahlleitungen, Bürgermeisterwahl in 2 Wahlleitungen, Ratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2022-01-23: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2021-10-03: Bürgermeisterwahl in 4 Wahlleitungen, Ratswahl in 6 Wahlleitungen, Ortsratswahl in 4 Wahlleitungen – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2021: Kommunalwahlen am 12. September 2021 mit Stichwahlen am 26. September 2021 – amtliche Endergebnisse – 2020-11-08: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2020-10-25: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2020-09-20: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2020: Wahl des Bürgermeisters der Gemeinde Nordstemmen am 13. September 2020 mit Stichwahl am 27. September 2020 – 2020-02-23: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2020-02-09: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2019-12-01: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2019-09-15: Bürgermeisterwahl in 2 Wahlleitungen – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2019-09-01: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2019-05-26: Landratswahl in 114 Wahlleitungen, Bürgermeisterwahl in 47 Wahlleitungen – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2018-12-16: Bürgermeisterwahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2017-09-24: Bürgermeisterwahl in 2 Wahlleitungen – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2014-05-25: Bürgermeisterwahl in 4 Wahlleitungen – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert. – 2013-09-22: Landratswahl in einer Wahlleitung – die letzte Wahl dieser Ämter vor dem 13. September 2026 und damit ihr Vergleichswert.. Ein Termin gehört zu einer Ebene: Die kreisweiten Wahltage (Kommunalwahl, Landrats- und Kreistagswahl) stehen unter /{kreis} in 'termine'; Bürgermeister- und Oberbürgermeisterwahlen gehören zu einer einzelnen Wahlleitung und stehen dort bei ihr in 'behoerden[].termine'. Ein Termin auf der falschen Ebene antwortet mit 404 und nennt im Hinweis die Wahlleitung, bei der er liegt. Welche Wahlen ein Termin bei einer Wahlleitung umfasst, zeigt /{kreis}/{termin}/wahlen?behoerde=…",
        "schema": {
          "type": "string",
          "enum": [
            "2026",
            "2025-12-14",
            "2025-10-12",
            "2025-10-05",
            "2025-02-23",
            "2024-10-27",
            "2024-06-09",
            "2023-03-05",
            "2022-10-09",
            "2022-03-06",
            "2022-02-27",
            "2022-01-23",
            "2021-10-03",
            "2021",
            "2020-11-08",
            "2020-10-25",
            "2020-09-20",
            "2020",
            "2020-02-23",
            "2020-02-09",
            "2019-12-01",
            "2019-09-15",
            "2019-09-01",
            "2019-05-26",
            "2018-12-16",
            "2017-09-24",
            "2014-05-25",
            "2013-09-22"
          ]
        }
      },
      "BehoerdeParam": {
        "name": "behoerde",
        "in": "path",
        "required": true,
        "description": "Wahlleitung innerhalb des Kreises, als Slug (nordstemmen) oder AGS (03254026); die Kreisbehörde heißt kreis. Welche es gibt, zeigt /{kreis}/{termin}/behoerden.",
        "schema": {
          "type": "string"
        }
      },
      "WahlParam": {
        "name": "wahl",
        "in": "path",
        "required": true,
        "description": "Wahl-Slug, z. B. kreistag, landrat, buergermeister, rat, ortsrat-roessing",
        "schema": {
          "type": "string"
        }
      },
      "Partei": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "stabiler Schlüssel, z. B. cdu"
          },
          "kurz": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "farbe": {
            "type": "string"
          },
          "stimmen": {
            "type": "integer"
          },
          "prozent": {
            "type": "number"
          },
          "listenstimmen": {
            "type": "integer",
            "description": "Stimmen für die Liste als Ganzes"
          },
          "kandidatenstimmen": {
            "type": "integer",
            "description": "Summe der Stimmen für die einzelnen Bewerber dieser Partei – die Bezugsgröße von kandidaten[].prozentInPartei"
          },
          "sitze": {
            "type": "integer"
          },
          "kandidat": {
            "type": "object",
            "description": "Personenwahl (Landrat, Bürgermeister): die antretende Person",
            "properties": {
              "name": {
                "type": "string"
              },
              "partei": {
                "type": "string"
              }
            }
          },
          "kandidaten": {
            "type": "array",
            "description": "Bewerberinnen und Bewerber dieser Partei im abgefragten Gebiet, nach Stimmen absteigend. Nur bei Verhältniswahlen (Kreistag, Rat, Ortsrat); bei Personenwahlen steht die antretende Person in kandidat.",
            "items": {
              "$ref": "#/components/schemas/Kandidat"
            }
          }
        },
        "required": [
          "key",
          "kurz",
          "name",
          "stimmen",
          "prozent"
        ]
      },
      "Kandidat": {
        "type": "object",
        "description": "Eine Bewerberin oder ein Bewerber auf der Liste einer Partei, bezogen auf das abgefragte Gebiet. Listenplätze werden je Gebiet geführt: bei der Kreistagswahl stellt jede Partei in jedem Wahlbereich eine eigene Liste auf, dieselbe Person kann also anderswo einen anderen Platz haben oder gar nicht antreten.",
        "properties": {
          "name": {
            "type": "string",
            "description": "Name in der Schreibweise der Wahlleitung"
          },
          "stimmen": {
            "type": "integer",
            "description": "Stimmen, die im abgefragten Gebiet auf diese Person entfallen sind"
          },
          "prozent": {
            "type": [
              "number",
              "null"
            ],
            "description": "Anteil an ALLEN gültigen Stimmen des Gebiets, in Prozent. Das ist der Wert, mit dem sich Bewerber verschiedener Parteien vergleichen lassen. null, solange die Zahl der gültigen Stimmen nicht vorliegt."
          },
          "prozentInPartei": {
            "type": [
              "number",
              "null"
            ],
            "description": "Anteil an den Kandidatenstimmen der EIGENEN Partei, in Prozent – der Wert, den die amtliche Wahlpräsentation ausweist. Er ist leicht misszuverstehen: Er beschreibt nur, wie sich die Stimmen innerhalb einer Liste verteilen, nicht wie stark die Person im Gebiet abgeschnitten hat. Wer auf einer kurzen Liste die meisten Stimmen holt, steht hier schnell bei 40 %, obwohl das gemessen an allen gültigen Stimmen (siehe prozent) wenige Prozent sind. Für Vergleiche zwischen Parteien ist der Wert unbrauchbar; dafür ist prozent da."
          },
          "platz": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Platz auf dem Wahlvorschlag in diesem Gebiet. null, wenn kein Wahlvorschlag vorliegt oder der Platz nicht eindeutig bestimmbar ist (zwei Bewerber derselben Liste mit exakt gleicher Stimmenzahl)."
          },
          "gewaehlt": {
            "type": "boolean",
            "description": "true, wenn die Person laut amtlicher Gewähltenliste ein Mandat erhalten hat. Steht erst fest, wenn die Sitzverteilung vorliegt."
          }
        },
        "required": [
          "name",
          "stimmen",
          "prozent",
          "prozentInPartei",
          "platz",
          "gewaehlt"
        ]
      },
      "Ergebnis": {
        "type": "object",
        "properties": {
          "termin": {
            "type": "string"
          },
          "behoerde": {
            "type": "string"
          },
          "wahl": {
            "type": "string"
          },
          "gebiet": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "ebene": {
                "type": "string"
              }
            }
          },
          "leer": {
            "type": "boolean",
            "description": "true, solange keine Zahlen vorliegen"
          },
          "stand": {
            "type": "object",
            "properties": {
              "schnellmeldungen": {
                "type": "object",
                "properties": {
                  "eingegangen": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "erwartet": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  }
                }
              },
              "vollstaendig": {
                "type": "boolean"
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "z. B. Amtliches Endergebnis"
              },
              "datenstand": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "abgerufen": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "kennzahlen": {
            "type": "object",
            "properties": {
              "wahlberechtigte": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "waehler": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "wahlbeteiligung": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "ungueltig": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "gueltigeStimmzettel": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "gueltigeStimmen": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "bei Verhältniswahl bis zu drei je Stimmzettel"
              }
            }
          },
          "parteien": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Partei"
            }
          },
          "sitze": {
            "type": [
              "object",
              "null"
            ],
            "description": "Die von der Wahlleitung veröffentlichte Sitzverteilung. Null, solange sie keine veröffentlicht hat – am Wahlabend also den ganzen Abend über. Die Hochrechnung, die die Seiten in dieser Zeit anzeigen, steht bewusst nicht in der Schnittstelle: Sie ist eine Schätzung mit angesagter Unsicherheit und keine Zahl, die ein Skript weiterreichen sollte.",
            "properties": {
              "gesamt": {
                "type": "integer"
              },
              "verteilung": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "gewaehlte": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "Wahl": {
        "type": "object",
        "properties": {
          "termin": {
            "type": "string"
          },
          "behoerde": {
            "type": "object"
          },
          "slug": {
            "type": "string"
          },
          "typ": {
            "type": "string",
            "enum": [
              "landrat",
              "landrat-stichwahl",
              "kreistag",
              "buergermeister",
              "buergermeister-stichwahl",
              "rat",
              "ortsrat",
              "sonstige"
            ]
          },
          "typLabel": {
            "type": "string"
          },
          "titel": {
            "type": "string"
          },
          "gebiet": {
            "type": "string"
          },
          "personenwahl": {
            "type": "boolean"
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "ergebnis": {
            "$ref": "#/components/schemas/Ergebnis"
          },
          "ebenen": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ebene": {
                  "type": "string"
                },
                "anzahl": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    }
  }
}