Skip to main content
Data Connectors · Environment

Air Quality (UBA)

The node queries the Luftdaten API v4 of the German Environment Agency (Umweltbundesamt, UBA) for selected monitoring stations. On the first output it emits the latest reading per station as an AirQualityObserved entity, on the second output a list of hourly AirQualityForecast entities.

  • Data source
  • Trigger-based
  • AirQualityObserved
Node type
luftdaten
Package
@htag/node-red-contrib-hypertegrity-environment
Inputs / outputs
1 / 2
Data source
Luftdaten API v4 of the German Environment Agency

When to use this node​

The German Environment Agency publishes the readings of the official air monitoring networks of the federal states through an open API. The Air Quality node brings these values for the stations you select on a map into a Node-RED flow and maps them to the Smart Data Models AirQualityObserved and AirQualityForecast.

Typical use cases are:

  • Official air quality of a city or region transferred into an NGSI-LD context broker.
  • Particulate matter, nitrogen dioxide and ozone values evaluated in dashboards and alerting flows.
  • Forecasts for the coming hours, for example as guidance for cyclists or outdoor sports events.

How the node works​

1

Trigger

Any incoming message starts the fetch. The node never polls the API on its own.

2

Load station list

The master data of all stations is loaded and cached for one hour.

3

Fetch readings and forecast

For each station the readings from yesterday to today and the forecast for the lookahead window are requested.

4

Emit

Per station the latest reading leaves output 1 and the list of forecast hours leaves output 2.

Result: One AirQualityObserved entity and one array of AirQualityForecast entities per station, filtered to the selected pollutant components.

The node is trigger-based. Combine it with an Inject node that fires hourly, because UBA provides readings as hourly means. Stations are queried one after another; for each station the node sends its own pair of messages. If a single request fails, the node issues a warning and continues with the next station.

Configuration​

The edit dialog is divided into the tabs Stations and Settings.

Stations tab​

FieldTypeDefaultMeaning
NameTextemptyDisplay name of the node in the flow. Without a name the node shows "UBA Luftdaten".
Station SelectionMap with listno selectionSelect stations by clicking a map marker or a list entry. For each station an object with id, name, lat and lon is stored in the array stations.
Enter city or address / FindText and buttonemptyGeocodes the search term via the OpenStreetMap Nominatim service and draws a search circle around the hit. Clicking on the map also places the circle.
RadiusSlider 5 km to 100 km, step 5 km25 kmRadius of the search circle. Stations inside the circle are highlighted in the list.
Filter listTextemptyFilters the station list by name or station ID.
Select Visible / Clear AllButtons–Selects all stations currently listed, or removes the whole selection and resets the radius.

Settings tab​

FieldTypeDefaultMeaning
API Base URLTextsee belowBase URL of the UBA API. The node appends the paths stations/json, airquality/json and airqualityforecast/json.
ComponentsCheckboxesPM10, PM2.5, NO2, O3Pollutants included in the entities: PM10, PM2.5, NO2 (nitrogen dioxide), O3 (ozone), SO2 (sulfur dioxide), CO (carbon monoxide). Stored as the array components with the values PM10, PM25, NO2, O3, SO2, CO.
Lookahead (Hours)Number24Number of hours for which forecast data is requested and emitted.

Default values of the URL fields:

API Base URL: https://luftdaten.umweltbundesamt.de/api/air-data/v4/
Stations without readings

The station list contains all stations that delivered air quality data within the past year. Not every station measures every component. If a component is missing at a station, it is also missing in the entity.

Input​

The input only acts as a trigger. The node does not evaluate any property of the incoming message.

PropertyTypeMeaning
msganyEvery message starts a fetch of all configured stations. Content and properties of the message are not carried over into the output.

Output​

The node has two outputs labelled Current Air Quality and Air Quality Forecast. For each station it sends a pair of messages; if one of the two parts is missing for a station, the respective output stays empty for that station.

Output 1: Current readings​

msg.payload contains an AirQualityObserved entity with the latest available reading of the station. The pollutant values are flat numeric attributes, so the entity can be handed directly to the NGSI-LD Builder.

{
"id": "urn:ngsi-ld:AirQualityObserved:Germany-UBA-123",
"type": "AirQualityObserved",
"dateObserved": "2026-10-01T07:00:00+01:00",
"location": {"type": "Point", "coordinates": [7.4601, 51.5102]},
"address": {
"type": "Property",
"value": {"addressLocality": "Example Town Centre", "addressCountry": "DE"}
},
"source": "UBA Luftdaten API v4",
"PM10": 18,
"NO2": 22.5,
"PM25": 9,
"O3": 61
}
FieldMeaning
idurn:ngsi-ld:AirQualityObserved:Germany-UBA- followed by the numeric UBA station ID.
typeAlways AirQualityObserved.
dateObservedTimestamp of the reading from the API, with the time zone offset +01:00 appended.
locationGeoJSON point in the order longitude, latitude from the station master data. If missing, the value is [0, 0].
addressPlace information in NGSI-LD property format with the station name as addressLocality and DE as addressCountry.
sourceAlways UBA Luftdaten API v4.
PM10, PM25, NO2, O3, SO2, COReadings of the selected components as numbers. Only components that are selected and measured at the station appear.

Output 2: Forecast​

msg.payload contains an array of AirQualityForecast entities, one per forecast hour within the lookahead window.

[
{
"id": "urn:ngsi-ld:AirQualityForecast:Germany-UBA-123",
"type": "AirQualityForecast",
"dateIssued": "2026-10-01T08:15:00.000Z",
"validFrom": "2026-10-01T09:00:00+01:00",
"validTo": "2026-10-01T10:00:00+01:00",
"location": {"type": "Point", "coordinates": [7.4601, 51.5102]},
"address": {
"type": "Property",
"value": {"addressLocality": "Example Town Centre", "addressCountry": "DE"}
},
"source": "UBA Luftdaten API v4",
"PM10": 17,
"NO2": 20,
"O3": 58
}
]
FieldMeaning
idurn:ngsi-ld:AirQualityForecast:Germany-UBA- followed by the station ID.
typeAlways AirQualityForecast.
dateIssuedTime of the fetch in ISO 8601 format (UTC).
validFrom, validToStart and end of the forecast hour from the API, with +01:00 appended.
location, address, sourceAs on the first output.
ComponentsForecast values of the selected components as numbers.

The entities carry no unit information. The units of the UBA API apply, which are described per component in the API documentation.

Example flow​

The package ships with an example flow. Import it in Node-RED via Menu → Import → Examples → @htag/node-red-contrib-hypertegrity-environment → lufdaten-api. Alternatively download lufdaten-api.json and import it via Menu → Import.

Step 1

Inject

An Inject node triggers the fetch manually. For continuous operation set an hourly interval.

Step 2

Air Quality

The node is preconfigured without stations. Select at least one station in the dialog before deploying.

Step 3

Two Debug nodes

'Current Data' shows the AirQualityObserved entities, 'Forecast Data' the forecast arrays.

Show example flow as JSON
[
{"id": "tab1", "type": "tab", "label": "Luftdaten Api", "disabled": false, "info": ""},
{"id": "inject1", "type": "inject", "z": "tab1", "name": "", "props": [{"p": "payload"}, {"p": "topic", "vt": "str"}], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 120, "y": 140, "wires": [["uba1"]]},
{"id": "uba1", "type": "luftdaten", "z": "tab1", "name": "", "stations": [], "components": ["PM10", "NO2", "PM25", "O3"], "apiBaseUrl": "https://luftdaten.umweltbundesamt.de/api/air-data/v4/", "lookahead": 24, "x": 300, "y": 140, "wires": [["debug1"], ["debug2"]]},
{"id": "debug1", "type": "debug", "z": "tab1", "name": "Current Data", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 510, "y": 100, "wires": []},
{"id": "debug2", "type": "debug", "z": "tab1", "name": "Forecast Data", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 520, "y": 160, "wires": []}
]

Status and errors​

IndicatorMeaning
Red ring "no station selected"A fetch was triggered but no station is configured. The message "No station selected" is reported via node.error.
Blue dot "fetching..."A fetch is in progress.
Green dot "Obs: n, Forecast: m"The fetch finished; n observation entities and m forecast entities were sent.
Red dot "error"The fetch failed as a whole, for example because the station list could not be loaded. The message "Processing failed: …" is reported via node.error.
Warning "Observed data fetch failed for …"The readings request of a single station failed after three attempts. The remaining stations are still processed.
Warning "Forecast fetch failed for …"The forecast request of a single station failed after three attempts.

Errors are reported via node.error together with the triggering message and can be handled with a Catch node.

Notes​

  • No API key required: The UBA Luftdaten API is freely accessible. The node uses a timeout of 15 seconds and retries each request up to three times with a short pause.
  • Station cache: The station list is cached process-wide for one hour, shared across all node instances. Restarting Node-RED clears the cache.
  • External services in the editor: The address search sends the search term directly from the browser to the OpenStreetMap Nominatim service; the Leaflet map library is loaded from unpkg.com. The dialog loads the station list via the admin endpoint /luftdaten/stations of the Node-RED server.
  • Units: Values are passed on without unit and without conversion (concentrations in metric units according to the UBA API, usually µg/m³).
  • Personal data: The data describes official monitoring stations and contains no personal information.

References​