Python API#
Introduction#
The API offers access to different data products. They are outlined in more detail within the data chapter. Please also check out complete examples about how to use the API in the examples folder. In order to explore all features interactively, you might want to try the cli. For managing general settings, please refer to the settings chapter.
Available APIs#
The available APIs can be accessed by the top-level API Wetterdienst. This API also allows the user to discover the available APIs of each service included:
1from wetterdienst import Wetterdienst
2
3coverage = Wetterdienst.discover()
4coverage
{'aemet': {'observation': {'auth': True,
'configured': False,
'valid': False,
'date_required': True}},
'chmi': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'dmi': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'dwd': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'mosmix': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'dmo': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'road': {'auth': False,
'configured': True,
'valid': True,
'date_required': True},
'swsmos': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'radar': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'alerts': {'auth': False,
'configured': True,
'valid': True,
'date_required': False},
'derived': {'auth': False,
'configured': True,
'valid': True,
'date_required': False}},
'eccc': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'fmi': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'imgw': {'hydrology': {'auth': False,
'configured': True,
'valid': True,
'date_required': True},
'meteorology': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'ipma': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'knmi': {'observation': {'auth': True,
'configured': False,
'valid': False,
'date_required': True}},
'lhmt': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'noaa': {'ghcn': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'rmi': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'wsv': {'pegel': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'ea': {'hydrology': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'nws': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'eaufrance': {'hubeau': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'geosphere': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'meteofrance': {'synop': {'auth': False,
'configured': True,
'valid': True,
'date_required': True},
'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'meteoswiss': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': True}},
'metno': {'frost': {'auth': True,
'configured': False,
'valid': False,
'date_required': True}},
'metoffice': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': False}},
'smhi': {'observation': {'auth': False,
'configured': True,
'valid': True,
'date_required': False}}}
To load any of the available APIs pass the provider and the network of data to the wetterdienst api factory:
1from wetterdienst import Wetterdienst
2
3Wetterdienst(provider="dwd", network="observation")
wetterdienst.provider.dwd.observation.api.DwdObservationRequest
Request arguments#
A request is typically defined by three arguments:
parametersstart_dateend_date
Parameters can be requested in different ways e.g.
using a tuple of resolution and dataset
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3DwdObservationRequest(
4 parameters=("daily", "climate_summary") # will resolve to all parameters of kl
5)
DwdObservationRequest(parameters=[ParameterModel(name='wind_gust_max', name_original='fx', unit='meter_per_second', description='Daily maximum of windgust.'), ParameterModel(name='wind_speed', name_original='fm', unit='meter_per_second', description='Daily mean of wind velocity.'), ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rskf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='sunshine_duration', name_original='sdk', unit='hour', description='Daily sunshine duration.'), ParameterModel(name='snow_depth', name_original='shk_tag', unit='centimeter', description='Daily snow depth.'), ParameterModel(name='cloud_cover_total', name_original='nm', unit='one_eighth', description='Daily mean of cloud cover.'), ParameterModel(name='pressure_vapor', name_original='vpm', unit='hectopascal', description='Daily mean of vapor pressure.'), ParameterModel(name='pressure_air_site', name_original='pm', unit='hectopascal', description='Daily mean of pressure.'), ParameterModel(name='temperature_air_mean_2m', name_original='tmk', unit='degree_celsius', description='Daily mean of temperature.'), ParameterModel(name='humidity', name_original='upm', unit='percent', description='Daily mean of relative humidity.'), ParameterModel(name='temperature_air_max_2m', name_original='txk', unit='degree_celsius', description='Daily maximum of temperature at 2 m height.'), ParameterModel(name='temperature_air_min_2m', name_original='tnk', unit='degree_celsius', description='Daily minimum of temperature at 2m height.'), ParameterModel(name='temperature_air_min_0_05m', name_original='tgk', unit='degree_celsius', description='Daily minimum of air temperature at 5 cm above ground.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
using a tuple of resolution, dataset, parameter
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3DwdObservationRequest(
4 parameters=("daily", "climate_summary", "precipitation_height")
5)
DwdObservationRequest(parameters=[ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
the same with the original names
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3DwdObservationRequest(
4 parameters=("daily", "kl", "rsk")
5)
DwdObservationRequest(parameters=[ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
using the metadata model
1from wetterdienst.provider.dwd.observation import DwdObservationRequest, DwdObservationMetadata
2
3DwdObservationRequest(
4 parameters=DwdObservationMetadata.daily.kl # will resolve to all parameters of kl
5)
DwdObservationRequest(parameters=[ParameterModel(name='wind_gust_max', name_original='fx', unit='meter_per_second', description='Daily maximum of windgust.'), ParameterModel(name='wind_speed', name_original='fm', unit='meter_per_second', description='Daily mean of wind velocity.'), ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rskf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='sunshine_duration', name_original='sdk', unit='hour', description='Daily sunshine duration.'), ParameterModel(name='snow_depth', name_original='shk_tag', unit='centimeter', description='Daily snow depth.'), ParameterModel(name='cloud_cover_total', name_original='nm', unit='one_eighth', description='Daily mean of cloud cover.'), ParameterModel(name='pressure_vapor', name_original='vpm', unit='hectopascal', description='Daily mean of vapor pressure.'), ParameterModel(name='pressure_air_site', name_original='pm', unit='hectopascal', description='Daily mean of pressure.'), ParameterModel(name='temperature_air_mean_2m', name_original='tmk', unit='degree_celsius', description='Daily mean of temperature.'), ParameterModel(name='humidity', name_original='upm', unit='percent', description='Daily mean of relative humidity.'), ParameterModel(name='temperature_air_max_2m', name_original='txk', unit='degree_celsius', description='Daily maximum of temperature at 2 m height.'), ParameterModel(name='temperature_air_min_2m', name_original='tnk', unit='degree_celsius', description='Daily minimum of temperature at 2m height.'), ParameterModel(name='temperature_air_min_0_05m', name_original='tgk', unit='degree_celsius', description='Daily minimum of air temperature at 5 cm above ground.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
using a list of tuples
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3DwdObservationRequest(
4 parameters=[("daily", "climate_summary"), ("daily", "precipitation_more")]
5)
DwdObservationRequest(parameters=[ParameterModel(name='wind_gust_max', name_original='fx', unit='meter_per_second', description='Daily maximum of windgust.'), ParameterModel(name='wind_speed', name_original='fm', unit='meter_per_second', description='Daily mean of wind velocity.'), ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rskf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='sunshine_duration', name_original='sdk', unit='hour', description='Daily sunshine duration.'), ParameterModel(name='snow_depth', name_original='shk_tag', unit='centimeter', description='Daily snow depth.'), ParameterModel(name='cloud_cover_total', name_original='nm', unit='one_eighth', description='Daily mean of cloud cover.'), ParameterModel(name='pressure_vapor', name_original='vpm', unit='hectopascal', description='Daily mean of vapor pressure.'), ParameterModel(name='pressure_air_site', name_original='pm', unit='hectopascal', description='Daily mean of pressure.'), ParameterModel(name='temperature_air_mean_2m', name_original='tmk', unit='degree_celsius', description='Daily mean of temperature.'), ParameterModel(name='humidity', name_original='upm', unit='percent', description='Daily mean of relative humidity.'), ParameterModel(name='temperature_air_max_2m', name_original='txk', unit='degree_celsius', description='Daily maximum of temperature at 2 m height.'), ParameterModel(name='temperature_air_min_2m', name_original='tnk', unit='degree_celsius', description='Daily minimum of temperature at 2m height.'), ParameterModel(name='temperature_air_min_0_05m', name_original='tgk', unit='degree_celsius', description='Daily minimum of air temperature at 5 cm above ground.'), ParameterModel(name='precipitation_height', name_original='rs', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rsf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='snow_depth', name_original='sh_tag', unit='centimeter', description='Height of snow pack.'), ParameterModel(name='snow_depth_new', name_original='nsh_tag', unit='centimeter', description='Fresh snow depth.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
using a list of metadata
1from wetterdienst.provider.dwd.observation import DwdObservationRequest, DwdObservationMetadata
2
3DwdObservationRequest(
4 parameters=[DwdObservationMetadata.daily.kl, DwdObservationMetadata.daily.more_precip]
5)
DwdObservationRequest(parameters=[ParameterModel(name='wind_gust_max', name_original='fx', unit='meter_per_second', description='Daily maximum of windgust.'), ParameterModel(name='wind_speed', name_original='fm', unit='meter_per_second', description='Daily mean of wind velocity.'), ParameterModel(name='precipitation_height', name_original='rsk', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rskf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='sunshine_duration', name_original='sdk', unit='hour', description='Daily sunshine duration.'), ParameterModel(name='snow_depth', name_original='shk_tag', unit='centimeter', description='Daily snow depth.'), ParameterModel(name='cloud_cover_total', name_original='nm', unit='one_eighth', description='Daily mean of cloud cover.'), ParameterModel(name='pressure_vapor', name_original='vpm', unit='hectopascal', description='Daily mean of vapor pressure.'), ParameterModel(name='pressure_air_site', name_original='pm', unit='hectopascal', description='Daily mean of pressure.'), ParameterModel(name='temperature_air_mean_2m', name_original='tmk', unit='degree_celsius', description='Daily mean of temperature.'), ParameterModel(name='humidity', name_original='upm', unit='percent', description='Daily mean of relative humidity.'), ParameterModel(name='temperature_air_max_2m', name_original='txk', unit='degree_celsius', description='Daily maximum of temperature at 2 m height.'), ParameterModel(name='temperature_air_min_2m', name_original='tnk', unit='degree_celsius', description='Daily minimum of temperature at 2m height.'), ParameterModel(name='temperature_air_min_0_05m', name_original='tgk', unit='degree_celsius', description='Daily minimum of air temperature at 5 cm above ground.'), ParameterModel(name='precipitation_height', name_original='rs', unit='millimeter', description='Daily precipitation height.'), ParameterModel(name='precipitation_form', name_original='rsf', unit='dimensionless', description='Precipitation form.'), ParameterModel(name='snow_depth', name_original='sh_tag', unit='centimeter', description='Height of snow pack.'), ParameterModel(name='snow_depth_new', name_original='nsh_tag', unit='centimeter', description='Fresh snow depth.')], start_date=None, end_date=None, settings={"cache_disable": false, "cache_dir": "/home/docs/.cache/wetterdienst", "fsspec_client_kwargs": {"headers": {"User-Agent": "wetterdienst/0.133.0 (Linux)"}, "timeout": 30}, "auth": {"aemet": null, "knmi": null, "metno_frost": null, "ceda": null}, "use_certifi": false, "read_bufr": false, "ts_humanize": true, "ts_shape": "long", "ts_convert_units": true, "ts_unit_targets": {}, "ts_skip_empty": false, "ts_skip_threshold": 0.95, "ts_skip_criteria": "min", "ts_complete": false, "ts_drop_nulls": true, "ts_geo_station_distance_homogeneous": 40.0, "ts_geo_station_distance_heterogeneous": 20.0, "ts_geo_station_distance": {}, "ts_geo_station_distance_resolution_factors": {}, "ts_geo_use_nearby_station_distance": 1.0, "ts_geo_min_gain_of_value_pairs": 0.1, "ts_geo_num_additional_stations": 3}, periods={<Period.HISTORICAL: 'historical'>, <Period.NOW: 'now'>, <Period.RECENT: 'recent'>})
For some weather service one can select which period of the data is request with periods.
Valid periods are historical, recent and now. periods can be given as a list or a single value.
The value can be a string, the enumeration value or the enumeration name e.g.
by using the exact enumeration e.g.
1from wetterdienst.metadata.period import Period
2
3Period.HISTORICAL
<Period.HISTORICAL: 'historical'>
by using the enumeration name or value as string e.g.
"historical" or "HISTORICAL"
If a weather service has periods, the period argument typically can be used as replacement for the start_date and end_date arguments. In case both arguments are given they are used as a filter for the data.
Regarding the definition of requested parameters:
Data#
In case of the DWD, requests can be defined by either of period or
start_date and end_date. Use DwdObservationRequest.discover()
to discover available parameters based on the given filter arguments.
Stations#
all stations#
Get station information for a given dataset/parameter and period.
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4request = DwdObservationRequest(
5 parameters=("daily", "precipitation_more"),
6 start_date=dt.datetime(2020, 1, 1),
7 end_date=dt.datetime(2020, 1, 20)
8)
9stations = request.all()
10df = stations.df
11df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state |
|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str |
| "daily" | "precipitation_more" | "00001" | 1912-01-01 00:00:00 UTC | 1986-06-30 00:00:00 UTC | 47.8413 | 8.8493 | 478.0 | "Aach" | "Baden-Württemberg" |
| "daily" | "precipitation_more" | "00002" | 1951-01-01 00:00:00 UTC | 2006-12-31 00:00:00 UTC | 50.8066 | 6.0996 | 138.0 | "Aachen (Kläranlage)" | "Nordrhein-Westfalen" |
| "daily" | "precipitation_more" | "00003" | 1891-01-01 00:00:00 UTC | 2011-03-31 00:00:00 UTC | 50.7827 | 6.0941 | 202.0 | "Aachen" | "Nordrhein-Westfalen" |
| "daily" | "precipitation_more" | "00004" | 1951-01-01 00:00:00 UTC | 1979-10-31 00:00:00 UTC | 50.7683 | 6.1207 | 243.0 | "Aachen-Brand" | "Nordrhein-Westfalen" |
| "daily" | "precipitation_more" | "00006" | 1982-11-01 00:00:00 UTC | 2026-08-17 00:00:00 UTC | 48.8361 | 10.0598 | 455.0 | "Aalen-Unterrombach" | "Baden-Württemberg" |
| … | … | … | … | … | … | … | … | … | … |
| "daily" | "precipitation_more" | "20107" | 2025-03-27 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 49.9687 | 6.8272 | 311.0 | "Wittlich-Bergweiler" | "Rheinland-Pfalz" |
| "daily" | "precipitation_more" | "20108" | 2025-05-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 51.1543 | 6.5049 | 56.0 | "Mönchengladbach-Schelsen" | "Nordrhein-Westfalen" |
| "daily" | "precipitation_more" | "20110" | 1969-01-01 00:00:00 UTC | 2005-07-31 00:00:00 UTC | 52.3183 | 11.5677 | 64.0 | "Colbitz" | "Sachsen-Anhalt" |
| "daily" | "precipitation_more" | "20189" | 2025-10-01 00:00:00 UTC | 2026-04-30 00:00:00 UTC | 47.6759 | 12.47 | 687.0 | "Reit im Winkl (Kaiserweg)" | "Bayern" |
| "daily" | "precipitation_more" | "20191" | 2025-09-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 50.9238 | 14.357 | 375.0 | "Sebnitz-Hinterhermsdorf" | "Sachsen" |
The function returns a Polars DataFrame with information about the available stations.
filter by station id#
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4request = DwdObservationRequest(
5 parameters=("daily", "precipitation_more"),
6 start_date=dt.datetime(2020, 1, 1),
7 end_date=dt.datetime(2020, 1, 20)
8)
9stations = request.filter_by_station_id(station_id=("01048", ))
10df = stations.df
11df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state |
|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str |
| "daily" | "precipitation_more" | "01048" | 1926-04-25 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 51.1278 | 13.7543 | 228.0 | "Dresden-Klotzsche" | "Sachsen" |
filter by name#
Station name filtering uses fuzzy matching (case-insensitive, via rapidfuzz’s WRatio
scorer) so partial names, minor typos, and word-order variations are handled
automatically. In particular a bare place name resolves to its stations, e.g.
name="Kiel" matches Kiel-Holtenau and Kiel-Kronshagen.
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4request = DwdObservationRequest(
5 parameters=("daily", "precipitation_more"),
6 start_date=dt.datetime(2020, 1, 1),
7 end_date=dt.datetime(2020, 1, 20)
8)
9stations = request.filter_by_name(name="Dresden-Klotzsche")
10df = stations.df
11df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state |
|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str |
| "daily" | "precipitation_more" | "01048" | 1926-04-25 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 51.1278 | 13.7543 | 228.0 | "Dresden-Klotzsche" | "Sachsen" |
The threshold parameter (0–1, default 0.8) controls how strictly the name must
match. Lower values accept more typos; higher values require a closer match. The
rank parameter limits how many stations are returned (default 1).
Because WRatio is a partial matcher, a query that is a common sub-token matches many
stations (e.g. name="Bad" matches every “…, Bad” station). For an exact match you have two
options: set threshold=1.0, which keeps only matches scoring 100 (so name="Kiel" returns
nothing while name="Aach" returns just Aach); or, for strict string equality, use
filter_by_sql, e.g. filter_by_sql("name = 'Aach'").
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4request = DwdObservationRequest(
5 parameters=("daily", "precipitation_more"),
6 start_date=dt.datetime(2020, 1, 1),
7 end_date=dt.datetime(2020, 1, 20)
8)
9# case-insensitive, typo-tolerant, returns up to 3 matches
10stations = request.filter_by_name(name="dresden", rank=3, threshold=0.8)
11df = stations.df
12df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state |
|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str |
| "daily" | "precipitation_more" | "01047" | 1828-01-01 00:00:00 UTC | 1992-11-30 00:00:00 UTC | 51.0557 | 13.7274 | 112.0 | "Dresden (Mitte)" | "Sachsen" |
| "daily" | "precipitation_more" | "01048" | 1926-04-25 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 51.1278 | 13.7543 | 228.0 | "Dresden-Klotzsche" | "Sachsen" |
| "daily" | "precipitation_more" | "01049" | 1995-08-01 00:00:00 UTC | 2004-12-31 00:00:00 UTC | 51.0582 | 13.6694 | 147.0 | "Dresden-Leutewitz" | "Sachsen" |
filter by distance#
Distance in kilometers (default)
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4hamburg = (53.551086, 9.993682)
5request = DwdObservationRequest(
6 parameters=("hourly", "temperature_air"),
7 start_date=dt.datetime(2020, 1, 1),
8 end_date=dt.datetime(2020, 1, 20)
9)
10stations = request.filter_by_distance(latlon=hamburg, distance=30, unit="km")
11df = stations.df
12df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state | distance |
|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str | f64 |
| "hourly" | "temperature_air" | "01975" | 1949-01-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.6332 | 9.9881 | 11.0 | "Hamburg-Fuhlsbüttel" | "Hamburg" | 9.1381 |
| "hourly" | "temperature_air" | "01981" | 2005-03-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.4776 | 9.8957 | 4.0 | "Hamburg-Neuwiedenthal" | "Hamburg" | 10.4279 |
| "hourly" | "temperature_air" | "00052" | 1976-01-01 00:00:00 UTC | 1988-01-01 00:00:00 UTC | 53.6623 | 10.199 | 46.0 | "Ahrensburg-Wulfsdorf" | "Schleswig-Holstein" | 18.3417 |
| "hourly" | "temperature_air" | "00760" | 2017-12-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.3629 | 9.9435 | 83.0 | "Rosengarten-Klecken" | "Niedersachsen" | 21.1875 |
| "hourly" | "temperature_air" | "04039" | 1988-01-11 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.7331 | 9.8776 | 11.0 | "Quickborn" | "Schleswig-Holstein" | 21.6373 |
| "hourly" | "temperature_air" | "04857" | 2004-09-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.5534 | 9.6097 | 2.0 | "Mittelnkirchen-Hohenfelde" | "Niedersachsen" | 25.367 |
Distance in miles
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4hamburg = (53.551086, 9.993682)
5request = DwdObservationRequest(
6 parameters=("hourly", "temperature_air"),
7 start_date=dt.datetime(2020, 1, 1),
8 end_date=dt.datetime(2020, 1, 20)
9)
10stations = request.filter_by_distance(latlon=hamburg, distance=30, unit="mi")
11df = stations.df
12df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state | distance |
|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str | f64 |
| "hourly" | "temperature_air" | "01975" | 1949-01-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.6332 | 9.9881 | 11.0 | "Hamburg-Fuhlsbüttel" | "Hamburg" | 9.1381 |
| "hourly" | "temperature_air" | "01981" | 2005-03-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.4776 | 9.8957 | 4.0 | "Hamburg-Neuwiedenthal" | "Hamburg" | 10.4279 |
| "hourly" | "temperature_air" | "00052" | 1976-01-01 00:00:00 UTC | 1988-01-01 00:00:00 UTC | 53.6623 | 10.199 | 46.0 | "Ahrensburg-Wulfsdorf" | "Schleswig-Holstein" | 18.3417 |
| "hourly" | "temperature_air" | "00760" | 2017-12-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.3629 | 9.9435 | 83.0 | "Rosengarten-Klecken" | "Niedersachsen" | 21.1875 |
| "hourly" | "temperature_air" | "04039" | 1988-01-11 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.7331 | 9.8776 | 11.0 | "Quickborn" | "Schleswig-Holstein" | 21.6373 |
| "hourly" | "temperature_air" | "04857" | 2004-09-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.5534 | 9.6097 | 2.0 | "Mittelnkirchen-Hohenfelde" | "Niedersachsen" | 25.367 |
| "hourly" | "temperature_air" | "05280" | 2007-03-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.9224 | 10.2267 | 33.0 | "Wittenborn" | "Schleswig-Holstein" | 44.0408 |
| "hourly" | "temperature_air" | "01736" | 2002-01-24 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.5731 | 10.6797 | 26.0 | "Grambek" | "Schleswig-Holstein" | 45.3735 |
filter by rank#
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4hamburg = (53.551086, 9.993682)
5request = DwdObservationRequest(
6 parameters=("hourly", "temperature_air"),
7 start_date=dt.datetime(2020, 1, 1),
8 end_date=dt.datetime(2020, 1, 20)
9)
10stations = request.filter_by_rank(latlon=hamburg, rank=5)
11df = stations.df
12df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state | distance |
|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str | f64 |
| "hourly" | "temperature_air" | "01975" | 1949-01-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.6332 | 9.9881 | 11.0 | "Hamburg-Fuhlsbüttel" | "Hamburg" | 9.1381 |
| "hourly" | "temperature_air" | "01981" | 2005-03-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.4776 | 9.8957 | 4.0 | "Hamburg-Neuwiedenthal" | "Hamburg" | 10.4279 |
| "hourly" | "temperature_air" | "00052" | 1976-01-01 00:00:00 UTC | 1988-01-01 00:00:00 UTC | 53.6623 | 10.199 | 46.0 | "Ahrensburg-Wulfsdorf" | "Schleswig-Holstein" | 18.3417 |
| "hourly" | "temperature_air" | "00760" | 2017-12-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.3629 | 9.9435 | 83.0 | "Rosengarten-Klecken" | "Niedersachsen" | 21.1875 |
| "hourly" | "temperature_air" | "04039" | 1988-01-11 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 53.7331 | 9.8776 | 11.0 | "Quickborn" | "Schleswig-Holstein" | 21.6373 |
| … | … | … | … | … | … | … | … | … | … | … |
| "hourly" | "temperature_air" | "03730" | 1948-01-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 47.3984 | 10.2759 | 806.0 | "Oberstdorf" | "Bayern" | 684.4374 |
| "hourly" | "temperature_air" | "05792" | 1950-01-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 47.421 | 10.9848 | 2956.0 | "Zugspitze" | "Bayern" | 685.2131 |
| "hourly" | "temperature_air" | "07325" | 2011-09-01 00:00:00 UTC | 2015-12-31 00:00:00 UTC | 47.4165 | 10.9795 | 2650.0 | "Schneefernerhaus" | "Bayern" | 685.6732 |
| "hourly" | "temperature_air" | "00361" | 1948-01-01 00:00:00 UTC | 1976-01-01 00:00:00 UTC | 47.6344 | 13.0109 | 550.0 | "Berchtesgaden (KKst)" | "Bayern" | 691.3582 |
| "hourly" | "temperature_air" | "19856" | 2024-08-01 00:00:00 UTC | 2026-08-19 00:00:00 UTC | 47.6134 | 12.9819 | 625.0 | "Schönau am Königssee" | "Bayern" | 692.9712 |
Note
Unlike filter_by_distance, filter_by_rank returns all stations sorted by
distance in stations.df, not just rank rows. The rank limit is applied lazily
during value collection: Wetterdienst walks the distance-sorted stations and stops
once rank stations with data have been consumed (see ts_skip_empty and related
settings below). To see the rank closest stations that actually returned data, use
stations.values.all().df_stations rather than stations.df. Set
ts_skip_empty=False to take the rank closest stations regardless of availability.
filter by bbox#
1import datetime as dt
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4bbox = (8.9, 50.0, 8.91, 50.01)
5request = DwdObservationRequest(
6 parameters=("hourly", "temperature_air"),
7 start_date=dt.datetime(2020, 1, 1),
8 end_date=dt.datetime(2020, 1, 20)
9)
10stations = request.filter_by_bbox(*bbox)
11df = stations.df
12df
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state |
|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str |
Values#
Values are just an extension of requests. You can query data by using the
.query() method on the values object:
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2from wetterdienst import Settings
3
4# if no settings are provided, default settings are used which are
5# Settings(ts_shape="long", ts_humanize=True, ts_si_units=True)
6request = DwdObservationRequest(
7 parameters=[("daily", "kl"), ("daily", "solar")],
8 start_date="1990-01-01",
9 end_date="2020-01-01",
10)
11stations = request.filter_by_station_id(station_id=("00003", "01048"))
12
13# From here you can query data by station
14for result in stations.values.query():
15 # analyse the station here
16 break
17
18df = result.df.drop_nulls()
19df
| station_id | resolution | dataset | parameter | date | value | quality |
|---|---|---|---|---|---|---|
| str | str | str | str | datetime[μs, UTC] | f64 | f64 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-01 00:00:00 UTC | 1.0 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-02 00:00:00 UTC | 1.0 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-03 00:00:00 UTC | 0.5875 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-04 00:00:00 UTC | 0.75 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-05 00:00:00 UTC | 0.9625 | 10.0 |
| … | … | … | … | … | … | … |
| "00003" | "daily" | "climate_summary" | "wind_speed" | 2011-03-27 00:00:00 UTC | 1.7 | 10.0 |
| "00003" | "daily" | "climate_summary" | "wind_speed" | 2011-03-28 00:00:00 UTC | 1.7 | 10.0 |
| "00003" | "daily" | "climate_summary" | "wind_speed" | 2011-03-29 00:00:00 UTC | 1.6 | 10.0 |
| "00003" | "daily" | "climate_summary" | "wind_speed" | 2011-03-30 00:00:00 UTC | 3.8 | 10.0 |
| "00003" | "daily" | "climate_summary" | "wind_speed" | 2011-03-31 00:00:00 UTC | 7.0 | 10.0 |
Or you can query all data at once:
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2from wetterdienst import Settings
3
4# if no settings are provided, default settings are used which are
5# Settings(ts_shape="long", ts_humanize=True, ts_si_units=True)
6request = DwdObservationRequest(
7 parameters=[("daily", "kl"), ("daily", "solar")],
8 start_date="1990-01-01",
9 end_date="2020-01-01",
10)
11stations = request.filter_by_station_id(station_id=("00003", "01048"))
12df = stations.values.all().df.drop_nulls()
13df
| station_id | resolution | dataset | parameter | date | value | quality |
|---|---|---|---|---|---|---|
| enum | enum | enum | enum | datetime[μs, UTC] | f64 | f64 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-01 00:00:00 UTC | 1.0 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-02 00:00:00 UTC | 1.0 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-03 00:00:00 UTC | 0.5875 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-04 00:00:00 UTC | 0.75 | 10.0 |
| "00003" | "daily" | "climate_summary" | "cloud_cover_total" | 1990-01-05 00:00:00 UTC | 0.9625 | 10.0 |
| … | … | … | … | … | … | … |
| "01048" | "daily" | "solar" | "sunshine_duration" | 2019-12-28 00:00:00 UTC | 0.0 | 1.0 |
| "01048" | "daily" | "solar" | "sunshine_duration" | 2019-12-29 00:00:00 UTC | 22320.0 | 1.0 |
| "01048" | "daily" | "solar" | "sunshine_duration" | 2019-12-30 00:00:00 UTC | 5400.0 | 1.0 |
| "01048" | "daily" | "solar" | "sunshine_duration" | 2019-12-31 00:00:00 UTC | 3960.0 | 1.0 |
| "01048" | "daily" | "solar" | "sunshine_duration" | 2020-01-01 00:00:00 UTC | 14040.0 | 1.0 |
This gives us the most options to work with the data, getting multiple parameters at
once, parsed nicely into column structure with improved parameter names. Instead of
start_date and end_date you may as well want to use period to update your
database once in a while with a fixed set of records.
The metadata columns station_id, resolution, dataset and parameter of the
DataFrame returned by .values.all() are stored as polars
Enum
instead of String to reduce the memory footprint (these columns repeat on every row and
Enum stores them as small integer codes). Enum behaves like String for most grouping,
sorting and == comparisons, but it has a few sharp edges: it does not support the .str
namespace, it compares unequal to a String column (so joins need matching dtypes), and
is_in([...]) raises if any of the given values is not one of the column’s categories
(e.g. filtering for a parameter that is absent from the result) instead of returning an empty
selection. If you need plain String columns again — e.g. for .str operations,
is_in with possibly-absent values, strict dtype checks or when concatenating with a
String DataFrame — cast them back:
import polars as pl
# cast every Enum column back to String
df = df.with_columns(pl.col(pl.Enum).cast(pl.String))
This matters in particular for station_id: polars raises a SchemaError when a join
key is Enum on one side and String on the other. The stations DataFrame
(request.df) keeps station_id as String, so joining it — or any other String
station table — with the values DataFrame requires casting the join key first:
import polars as pl
# join values (Enum station_id) with a String station table
merged = df.with_columns(pl.col("station_id").cast(pl.String)).join(stations, on="station_id")
It also affects filtering by name with is_in. There is no “soft”/non-raising flag on
is_in — if any queried value is not a category of the Enum column it raises instead of
returning an empty selection. Cast the column to String for the comparison (an == check
against a single value, on the other hand, does handle absent values gracefully):
import polars as pl
# filter by parameters that may or may not be present in the result
subset = df.filter(pl.col("parameter").cast(pl.String).is_in(["temperature_air_mean_2m", "sunshine_duration"]))
In case you use filter_by_rank you may want to skip empty stations. We can use the Settings from
settings to achieve that:
1from wetterdienst import Settings
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4settings = Settings(ts_skip_empty=True, ts_skip_criteria="min", ts_skip_threshold=0.2)
5karlsruhe = (49.19780976647141, 8.135207205143768)
6request = DwdObservationRequest(
7 parameters=[("daily", "kl")],
8 start_date="2021-01-01",
9 end_date="2021-12-31",
10 settings=settings,
11)
12stations = request.filter_by_rank(latlon=karlsruhe, rank=2)
13values = stations.values.all()
14print(values.df.head())
15# df_stations has only stations that appear in the values
16values.df_stations
shape: (0, 7)
┌────────────┬────────────┬─────────┬───────────┬───────────────────┬───────┬─────────┐
│ station_id ┆ resolution ┆ dataset ┆ parameter ┆ date ┆ value ┆ quality │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ enum ┆ enum ┆ enum ┆ enum ┆ datetime[μs, UTC] ┆ f64 ┆ f64 │
╞════════════╪════════════╪═════════╪═══════════╪═══════════════════╪═══════╪═════════╡
└────────────┴────────────┴─────────┴───────────┴───────────────────┴───────┴─────────┘
| resolution | dataset | station_id | start_date | end_date | latitude | longitude | height | name | state | distance |
|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | datetime[μs, UTC] | datetime[μs, UTC] | f64 | f64 | f64 | str | str | f64 |
| "daily" | "climate_summary" | "01428" | 1948-01-01 00:00:00 UTC | 1957-12-31 00:00:00 UTC | 49.2275 | 8.0585 | 230.0 | "Frankweiler" | "Rheinland-Pfalz" | 6.4765 |
| "daily" | "climate_summary" | "00174" | 1984-03-01 00:00:00 UTC | 1987-12-31 00:00:00 UTC | 49.1886 | 7.9766 | 250.0 | "Annweiler-Bindersbach" | "Rheinland-Pfalz" | 11.5709 |
Interpolation & Summary#
Wetterdienst can derive a time series for an arbitrary location from the surrounding station network, either by spatial interpolation or by combining the nearest available stations. See the dedicated Interpolation & Summary chapter for the full explanation, supported parameters, settings, CLI and REST usage.
Format#
To Dict#
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3request = DwdObservationRequest(
4 parameters=("daily", "kl", "temperature_air_mean_2m"),
5 start_date="2020-01-01",
6 end_date="2020-01-02"
7)
8stations = request.filter_by_station_id(station_id="01048")
9values = stations.values.all()
10values.to_dict(with_metadata=True, with_stations=True)
{'metadata': {'provider': {'name_local': 'Deutscher Wetterdienst',
'name_english': 'German Weather Service',
'country': 'Germany',
'copyright': '© Deutscher Wetterdienst (DWD), Climate Data Center (CDC)',
'url': 'https://opendata.dwd.de/climate_environment/CDC/'},
'producer': {'name': 'wetterdienst',
'version': '0.133.0',
'repository': 'https://github.com/earthobservations/wetterdienst',
'documentation': 'https://wetterdienst.readthedocs.io',
'doi': '10.5281/zenodo.3960624'}},
'stations': [{'resolution': 'daily',
'dataset': 'climate_summary',
'station_id': '01048',
'start_date': '1934-01-01T00:00:00.000000+00:00',
'end_date': '2026-08-19T00:00:00.000000+00:00',
'latitude': 51.1278,
'longitude': 13.7543,
'height': 228.0,
'name': 'Dresden-Klotzsche',
'state': 'Sachsen'}],
'values': [{'station_id': '01048',
'resolution': 'daily',
'dataset': 'climate_summary',
'parameter': 'temperature_air_mean_2m',
'date': '2020-01-01T00:00:00.000000+00:00',
'value': 2.4,
'quality': 10.0},
{'station_id': '01048',
'resolution': 'daily',
'dataset': 'climate_summary',
'parameter': 'temperature_air_mean_2m',
'date': '2020-01-02T00:00:00.000000+00:00',
'value': 0.2,
'quality': 10.0}]}
To Json#
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3request = DwdObservationRequest(
4 parameters=("daily", "kl", "temperature_air_mean_2m"),
5 start_date="2020-01-01",
6 end_date="2020-01-02"
7)
8stations = request.filter_by_station_id(station_id="01048")
9values = stations.values.all()
10print(values.to_json(with_metadata=True, with_stations=True))
{
"metadata": {
"provider": {
"name_local": "Deutscher Wetterdienst",
"name_english": "German Weather Service",
"country": "Germany",
"copyright": "\u00a9 Deutscher Wetterdienst (DWD), Climate Data Center (CDC)",
"url": "https://opendata.dwd.de/climate_environment/CDC/"
},
"producer": {
"name": "wetterdienst",
"version": "0.133.0",
"repository": "https://github.com/earthobservations/wetterdienst",
"documentation": "https://wetterdienst.readthedocs.io",
"doi": "10.5281/zenodo.3960624"
}
},
"stations": [
{
"resolution": "daily",
"dataset": "climate_summary",
"station_id": "01048",
"start_date": "1934-01-01T00:00:00.000000+00:00",
"end_date": "2026-08-19T00:00:00.000000+00:00",
"latitude": 51.1278,
"longitude": 13.7543,
"height": 228.0,
"name": "Dresden-Klotzsche",
"state": "Sachsen"
}
],
"values": [
{
"station_id": "01048",
"resolution": "daily",
"dataset": "climate_summary",
"parameter": "temperature_air_mean_2m",
"date": "2020-01-01T00:00:00.000000+00:00",
"value": 2.4,
"quality": 10.0
},
{
"station_id": "01048",
"resolution": "daily",
"dataset": "climate_summary",
"parameter": "temperature_air_mean_2m",
"date": "2020-01-02T00:00:00.000000+00:00",
"value": 0.2,
"quality": 10.0
}
]
}
To Ogc Feature Collection#
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3request = DwdObservationRequest(
4 parameters=("daily", "kl", "temperature_air_mean_2m"),
5 start_date="2020-01-01",
6 end_date="2020-01-02"
7)
8stations = request.filter_by_station_id(station_id="01048")
9values = stations.values.all()
10values.to_ogc_feature_collection(with_metadata=True)
{'metadata': {'provider': {'name_local': 'Deutscher Wetterdienst',
'name_english': 'German Weather Service',
'country': 'Germany',
'copyright': '© Deutscher Wetterdienst (DWD), Climate Data Center (CDC)',
'url': 'https://opendata.dwd.de/climate_environment/CDC/'},
'producer': {'name': 'wetterdienst',
'version': '0.133.0',
'repository': 'https://github.com/earthobservations/wetterdienst',
'documentation': 'https://wetterdienst.readthedocs.io',
'doi': '10.5281/zenodo.3960624'}},
'data': {'type': 'FeatureCollection',
'features': [{'type': 'Feature',
'properties': {'resolution': 'daily',
'dataset': 'climate_summary',
'id': '01048',
'name': 'Dresden-Klotzsche',
'state': 'Sachsen',
'start_date': '1934-01-01T00:00:00.000000+00:00',
'end_date': '2026-08-19T00:00:00.000000+00:00'},
'geometry': {'type': 'Point', 'coordinates': [13.7543, 51.1278, 228.0]},
'values': [{'resolution': 'daily',
'dataset': 'climate_summary',
'parameter': 'temperature_air_mean_2m',
'date': '2020-01-01T00:00:00.000000+00:00',
'value': 2.4,
'quality': 10.0},
{'resolution': 'daily',
'dataset': 'climate_summary',
'parameter': 'temperature_air_mean_2m',
'date': '2020-01-02T00:00:00.000000+00:00',
'value': 0.2,
'quality': 10.0}]}]}}
To GeoJson#
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3request = DwdObservationRequest(
4 parameters=("daily", "kl", "temperature_air_mean_2m"),
5 start_date="2020-01-01",
6 end_date="2020-01-02"
7)
8stations = request.filter_by_station_id(station_id="01048")
9values = stations.values.all()
10print(values.to_geojson(with_metadata=True))
{
"metadata": {
"provider": {
"name_local": "Deutscher Wetterdienst",
"name_english": "German Weather Service",
"country": "Germany",
"copyright": "© Deutscher Wetterdienst (DWD), Climate Data Center (CDC)",
"url": "https://opendata.dwd.de/climate_environment/CDC/"
},
"producer": {
"name": "wetterdienst",
"version": "0.133.0",
"repository": "https://github.com/earthobservations/wetterdienst",
"documentation": "https://wetterdienst.readthedocs.io",
"doi": "10.5281/zenodo.3960624"
}
},
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"resolution": "daily",
"dataset": "climate_summary",
"id": "01048",
"name": "Dresden-Klotzsche",
"state": "Sachsen",
"start_date": "1934-01-01T00:00:00.000000+00:00",
"end_date": "2026-08-19T00:00:00.000000+00:00"
},
"geometry": {
"type": "Point",
"coordinates": [
13.7543,
51.1278,
228.0
]
},
"values": [
{
"resolution": "daily",
"dataset": "climate_summary",
"parameter": "temperature_air_mean_2m",
"date": "2020-01-01T00:00:00.000000+00:00",
"value": 2.4,
"quality": 10.0
},
{
"resolution": "daily",
"dataset": "climate_summary",
"parameter": "temperature_air_mean_2m",
"date": "2020-01-02T00:00:00.000000+00:00",
"value": 0.2,
"quality": 10.0
}
]
}
]
}
}
To CSV#
1from wetterdienst.provider.dwd.observation import DwdObservationRequest
2
3request = DwdObservationRequest(
4 parameters=("daily", "kl", "temperature_air_mean_2m"),
5 start_date="2020-01-01",
6 end_date="2020-01-02"
7)
8stations = request.filter_by_station_id(station_id="01048")
9values = stations.values.all()
10print(values.to_csv())
station_id,resolution,dataset,parameter,date,value,quality
01048,daily,climate_summary,temperature_air_mean_2m,2020-01-01T00:00:00.000000+00:00,2.4,10.0
01048,daily,climate_summary,temperature_air_mean_2m,2020-01-02T00:00:00.000000+00:00,0.2,10.0
SQL#
Querying data using SQL is provided by an in-memory DuckDB_ database. In order to explore what is possible, please have a look at the DuckDB SQL introduction.
The result data is provided through a virtual table called data.
1from wetterdienst import Settings
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4settings = Settings(ts_shape="long", ts_humanize=True, ts_convert_units=True) # defaults
5request = DwdObservationRequest(
6 parameters=("hourly", "temperature_air", "temperature_air_mean_2m"),
7 start_date="2019-01-01",
8 end_date="2020-01-01",
9 settings=settings
10)
11stations = request.filter_by_station_id(station_id=[1048])
12values = stations.values.all()
13df = values.filter_by_sql("parameter='temperature_air_mean_2m' AND value < -7.0;")
14df
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[26], line 13
9 settings=settings
10 )
11 stations = request.filter_by_station_id(station_id=[1048])
12 values = stations.values.all()
---> 13 df = values.filter_by_sql("parameter='temperature_air_mean_2m' AND value < -7.0;")
14 df
File ~/checkouts/readthedocs.org/user_builds/wetterdienst/checkouts/latest/src/wetterdienst/io/export.py:38, in ExportMixin.filter_by_sql(self, sql)
36 def filter_by_sql(self, sql: str) -> pl.DataFrame:
37 """Filter df using an SQL query WHERE clause."""
---> 38 self.df = self._filter_by_sql(self.df, sql)
39 return self.df
File ~/checkouts/readthedocs.org/user_builds/wetterdienst/checkouts/latest/src/wetterdienst/io/export.py:161, in ExportMixin._filter_by_sql(df, sql)
144 @staticmethod
145 def _filter_by_sql(df: pl.DataFrame, sql: str) -> pl.DataFrame:
146 """Filter df using an SQL query WHERE clause.
147
148 This implementation is based on DuckDB, so please
(...) 159
160 """
--> 161 import duckdb # noqa: PLC0415
163 df = df.with_columns(pl.col("date").dt.replace_time_zone(None)) # uses df from local scope
164 sql = f"FROM df WHERE {sql}"
ModuleNotFoundError: No module named 'duckdb'
Export#
Data can be exported to SQLite, DuckDB, InfluxDB, CrateDB and more targets. A target is identified by a connection string.
Examples:
sqlite:///dwd.sqlite?table=weather
duckdb:///dwd.duckdb?table=weather
influxdb://localhost/?database=dwd&table=weather
crate://localhost/?database=dwd&table=weather
parquet, feather, zarr#
file:///path/to/dwd.parquet
file:///path/to/dwd.feather
file:///path/to/dwd.zarr
from wetterdienst import Settings
from wetterdienst.provider.dwd.observation import DwdObservationRequest
request = DwdObservationRequest(
parameters=("hourly", "temperature_air"),
start_date="2019-01-01",
end_date="2020-01-01",
)
stations = request.filter_by_station_id(station_id=[1048, 1050])
stations.values.to_target("influxdb://localhost/?database=dwd&table=weather")
The previous example uses a batch approach meaning each station is written one by one. Also, it will automatically append data after the first batch.
You could also first collect all data and then write it at once:
from wetterdienst import Settings
from wetterdienst.provider.dwd.observation import DwdObservationRequest
request = DwdObservationRequest(
parameters=("hourly", "temperature_air"),
start_date="2019-01-01",
end_date="2020-01-01",
)
stations = request.filter_by_station_id(station_id=[1048, 1050])
stations.values.all().to_target("influxdb://localhost/?database=dwd&table=weather", if_exists="append")
You could also iterate over the stations and write them one by one:
from wetterdienst import Settings
from wetterdienst.provider.dwd.observation import DwdObservationRequest
request = DwdObservationRequest(
parameters=("hourly", "temperature_air"),
start_date="2019-01-01",
end_date="2020-01-01",
)
stations = request.filter_by_station_id(station_id=[1048, 1050])
for station in stations.values.query():
station.to_target("influxdb://localhost/?database=dwd&table=weather", if_exists="append")
The argument if_exists supports the following modes:
fail: Raise an error if the table/file already exists.replace: Drop the table/file before inserting new values.append: Insert new values to the existing table (not supported by files).skip: Do nothing if the table/file already exists.
Caching#
The backbone of wetterdienst uses fsspec caching. It requires to create a directory under /home for the
most cases. If you are not allowed to write into /home you will run into OSError. For this purpose you can set
an environment variable WD_CACHE_DIR to define the place where the caching directory should be created.
To find out where your cache is located you can use the following code:
1from wetterdienst import Settings
2
3settings = Settings()
4settings.cache_dir
Or similarly with the cli:
!wetterdienst cache
FSSPEC#
FSSPEC is used for flexible file caching. It relies on the two libraries requests and aiohttp. Aiohttp is used for asynchronous requests and may swallow some errors related to proxies, ssl or similar. Use the defined variable FSSPEC_CLIENT_KWARGS to pass your very own client kwargs to fsspec e.g.
1from wetterdienst import Settings
2from wetterdienst.provider.dwd.observation import DwdObservationRequest
3
4settings = Settings(fsspec_client_kwargs={"trust_env": True}) # use proxy from environment variables
5
6request = DwdObservationRequest(
7 parameters=("hourly", "temperature_air"),
8 settings=settings
9)
10stations = request.filter_by_station_id(station_id=[1048])
11stations