{
  "openapi": "3.1.0",
  "info": {
    "title": "Datenschaufenster – Luftqualität vor Ort, Standard-API",
    "version": "1.0.0",
    "description": "Tages- und Jahreswerte der Luftqualitätsmessstationen in Deutschland (NO₂, PM₁₀, PM₂,₅, O₃) je Station und Kreis.\n\n**Zugang nur mit Token.** Das Token wird per E-Mail an luftquali@m2dot.com angefordert (Name, Organisation, Verwendungszweck) und mit jedem Aufruf im Header `Authorization: Bearer <Token>` gesendet. Kontingent je Token: 10 Anfragen pro Sekunde, 10.000 pro Tag.\n\nQuellen: Umweltbundesamt mit Daten der Messnetze der Länder und des Bundes; Kreisgrenzen © GeoBasis-DE / BKG (dl-de/by-2-0). Daten des laufenden Jahres und des Vorjahres bis Juni sind vorläufig. Der Gesamtbestand steht ohne Token als Bulk-Datei bereit: https://luftquali.datenschaufenster.m2dot.cloud/data/downloads/luftquali_tageswerte.parquet",
    "contact": {
      "name": "M2. technology & project consulting GmbH",
      "email": "luftquali@m2dot.com",
      "url": "https://m2dot.com"
    }
  },
  "servers": [
    {
      "url": "https://luftquali.datenschaufenster.m2dot.cloud/api/v1"
    }
  ],
  "security": [
    {
      "token": []
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "Übersicht",
        "operationId": "uebersicht",
        "responses": {
          "200": {
            "description": "Links zu allen Endpunkten"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          }
        }
      }
    },
    "/meta": {
      "get": {
        "summary": "Datenstand, Messgrößen, Beurteilungswerte, Quellen",
        "operationId": "meta",
        "responses": {
          "200": {
            "description": "Manifest der Veröffentlichung"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          }
        }
      }
    },
    "/stationen": {
      "get": {
        "summary": "Messstationen mit Tageswerten im letzten Jahr",
        "operationId": "stationen",
        "parameters": [
          {
            "name": "land",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "example": "Berlin"
          },
          {
            "name": "kreis",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}(,\\d{5})*$"
            },
            "description": "Amtlicher Gemeindeschlüssel des Kreises (5-stellig), mehrere durch Komma",
            "example": "11000"
          },
          {
            "name": "art",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "Hintergrund",
                "Verkehr",
                "Industrie"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Suche in Name und Ort"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv",
                "geojson"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stationen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StationenAntwort"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/stationen/{code}": {
      "get": {
        "summary": "Eine Station mit letzten Tageswerten und allen Jahreswerten",
        "operationId": "station",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "DEBE010"
          }
        ],
        "responses": {
          "200": {
            "description": "Station"
          },
          "404": {
            "description": "Station nicht gefunden"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/tageswerte": {
      "get": {
        "summary": "Tageswerte im Langformat",
        "operationId": "tageswerte",
        "description": "Ohne `station` oder `kreis` sind `von` und `bis` mit höchstens 31 Tagen Abstand nötig. Sortierung: Datum, Station, Messgröße.",
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Stationskennung, mehrere durch Komma (höchstens 50)",
            "example": "DEBE010"
          },
          {
            "name": "kreis",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}(,\\d{5})*$"
            },
            "description": "Amtlicher Gemeindeschlüssel des Kreises (5-stellig), mehrere durch Komma",
            "example": "11000"
          },
          {
            "name": "messgroesse",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "no2_max1h, pm10, pm25, o3_max8h; mehrere durch Komma",
            "example": "pm10"
          },
          {
            "name": "von",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-01-01"
          },
          {
            "name": "bis",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1000
            },
            "description": "Zeilen je Seite"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Startzeile; der Link zur nächsten Seite steht in `naechste` bzw. im Header `Link`"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            },
            "description": "Ausgabeformat"
          }
        ],
        "responses": {
          "200": {
            "description": "Tageswerte",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TageswerteAntwort"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/jahreswerte": {
      "get": {
        "summary": "Jahreswerte je Station seit 2016",
        "operationId": "jahreswerte",
        "parameters": [
          {
            "name": "station",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Stationskennung, mehrere durch Komma (höchstens 50)",
            "example": "DEBE010"
          },
          {
            "name": "kreis",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}(,\\d{5})*$"
            },
            "description": "Amtlicher Gemeindeschlüssel des Kreises (5-stellig), mehrere durch Komma",
            "example": "11000"
          },
          {
            "name": "jahr",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "von_jahr",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "bis_jahr",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1000
            },
            "description": "Zeilen je Seite"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Startzeile; der Link zur nächsten Seite steht in `naechste` bzw. im Header `Link`"
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            },
            "description": "Ausgabeformat"
          }
        ],
        "responses": {
          "200": {
            "description": "Jahreswerte",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seite"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/kreise": {
      "get": {
        "summary": "Kennzahlen je Kreis (Jahresmittel des letzten vollständigen Jahres)",
        "operationId": "kreise",
        "parameters": [
          {
            "name": "kreis",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^\\d{5}(,\\d{5})*$"
            },
            "description": "Amtlicher Gemeindeschlüssel des Kreises (5-stellig), mehrere durch Komma",
            "example": "11000"
          },
          {
            "name": "nur_mit_messung",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "csv"
              ],
              "default": "json"
            },
            "description": "Ausgabeformat"
          }
        ],
        "responses": {
          "200": {
            "description": "Kreise",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Seite"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "token": {
        "type": "http",
        "scheme": "bearer",
        "description": "Token per E-Mail an luftquali@m2dot.com anfordern"
      }
    },
    "schemas": {
      "Fehler": {
        "type": "object",
        "properties": {
          "fehler": {
            "type": "string"
          },
          "hinweis": {
            "type": "string"
          }
        }
      },
      "Seite": {
        "type": "object",
        "properties": {
          "anzahl": {
            "type": "integer"
          },
          "gesamt": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "naechste": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "datenstand": {
            "type": "string",
            "format": "date"
          },
          "daten": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "quellen": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "Tageswert": {
        "type": "object",
        "properties": {
          "station": {
            "type": "string"
          },
          "stationsname": {
            "type": "string"
          },
          "kreis_ags": {
            "type": "string"
          },
          "kreis": {
            "type": "string"
          },
          "datum": {
            "type": "string",
            "format": "date"
          },
          "messgroesse": {
            "type": "string",
            "enum": [
              "no2_max1h",
              "pm10",
              "pm25",
              "o3_max8h"
            ]
          },
          "wert": {
            "type": "number"
          },
          "einheit": {
            "type": "string"
          }
        }
      },
      "TageswerteAntwort": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Seite"
          },
          {
            "properties": {
              "daten": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Tageswert"
                }
              }
            }
          }
        ]
      },
      "Station": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "ort": {
            "type": "string"
          },
          "land": {
            "type": "string"
          },
          "kreis_ags": {
            "type": "string"
          },
          "kreis": {
            "type": "string"
          },
          "art": {
            "type": "string"
          },
          "lage": {
            "type": "string"
          },
          "lon": {
            "type": "number"
          },
          "lat": {
            "type": "number"
          },
          "letzter_tag": {
            "type": "string",
            "format": "date"
          },
          "no2_max1h": {
            "type": [
              "number",
              "null"
            ]
          },
          "pm10": {
            "type": [
              "number",
              "null"
            ]
          },
          "pm25": {
            "type": [
              "number",
              "null"
            ]
          },
          "o3_max8h": {
            "type": [
              "number",
              "null"
            ]
          },
          "lqi": {
            "type": [
              "integer",
              "null"
            ]
          },
          "lqi_text": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "StationenAntwort": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Seite"
          },
          {
            "properties": {
              "daten": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Station"
                }
              }
            }
          }
        ]
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Token fehlt oder ist ungültig",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Fehler"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Token gesperrt, abgelaufen oder nicht freigegeben",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Fehler"
            }
          }
        }
      },
      "TooMany": {
        "description": "Drosselung oder Tageskontingent erreicht",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Fehler"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Ungültige Parameter",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Fehler"
            }
          }
        }
      }
    }
  }
}
