API#

DWD#

DMO#

class DwdDmoRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Implementation of sites for dmo sites.

metadata

None

_values

None

issue: str | datetime.datetime | wetterdienst.provider.dwd.dmo.api.DwdForecastDate

None

station_group: Literal[single_stations, all_stations] | wetterdienst.provider.dwd.dmo.api.DwdDmoStationGroup | None

None

lead_time: Literal[short, long] | wetterdienst.provider.dwd.dmo.api.DwdDmoLeadTime | None

None

_coverage_cache: dict[str, set[str] | None]

‘field(…)’

_placemark_cache: dict[str, polars.DataFrame | None]

‘field(…)’

_url

‘https://www.dwd.de/DE/leistungen/opendata/help/schluessel_datenformate/kml/dmo_stationsliste_txt.asc…’

_base_columns: ClassVar

[‘resolution’, ‘dataset’, ‘station_id’, ‘icao_id’, ‘start_timestamp’, ‘end_timestamp’, ‘latitude’, ‘…

static adjust_datetime(datetime_: datetime.datetime) → datetime.datetime

Adjust datetime to DMO’s release hours, which are 00 and 12 UTC.

Datetime is floored to closest release time e.g. if hour is 14, it will be rounded to 12

classmethod discover(resolutions: str | wetterdienst.metadata.resolution.Resolution | wetterdienst.model.metadata.ResolutionModel | collections.abc.Sequence[str | wetterdienst.metadata.resolution.Resolution | wetterdienst.model.metadata.ResolutionModel] | None = None, datasets: str | wetterdienst.model.metadata.DatasetModel | collections.abc.Sequence[str | wetterdienst.model.metadata.DatasetModel] | None = None) → dict

Discover metadata as TimeseriesRequest.discover does, with each parameter’s lead times.

lead_times lists the lead times whose run carries the parameter, short before long, so a caller can offer only what the lead_time it sends will answer: values refuse a parameter asked for by name that the run does not carry (GH-1976). An added key, the others are as they were (GH-2256).

classmethod available_issues(station_id: str, settings: wetterdienst.settings.Settings, *, dataset: wetterdienst.model.metadata.DatasetModel | str = 'icon', station_group: wetterdienst.provider.dwd.dmo.api.DwdDmoStationGroup | str | None = None, lead_time: wetterdienst.provider.dwd.dmo.api.DwdDmoLeadTime | str | None = DwdDmoLeadTime.SHORT) → list[datetime.datetime]

Return the run start times DWD publishes for one product, in ascending UTC order.

Answers for a particular product, because the values path reads a particular product: this listed icon/single_stations/<id>/kmz/ whatever the request was for, and named issues that the request would then reject (GH-1956). Two ways, both measured against the live server: icon_eu’s all_stations publishes only the 078 lead time, so an issue advertised from a 168 file met IndexError: Unable to find a 168 h forecast within ...; and a station the shared catalogue listed for icon_eu without icon_eu covering it has no single-station directory, so every issue this advertised for it resolved to an empty frame – GH-1964, which narrowed the catalogue to the product. The directory comes from _dmo_kmz_path now, which is the one the values path reads.

The defaults are DwdDmoRequest’s own, so what this answers with no arguments is what a request built with no arguments accepts. lead_time defaulted to None until GH-2009, and so pooled the runs of both lead times: wetterdienst issues named runs of the 168 file that the default values request then rejected. Pass lead_time=None to list the runs of every lead time together, which is a question about the directory rather than about anything that can be asked for.

Args: station_id: The station to answer for, where the product is published per station. settings: The settings to list with. dataset: The DMO product – icon or icon_eu, or the DatasetModel itself. station_group: single_stations (the default) or all_stations. lead_time: short (078, the default) or long (168); None lists every lead time.

Returns: The run start times, tz-aware UTC, deduplicated and ascending.

__post_init__() → None

Post-initialize the DwdDmoRequest class.

_with_stations_the_catalogue_omits(df_dataset: polars.DataFrame, covered: set[str], dataset_name_original: str) → polars.DataFrame

Add the stations a product forecasts for that dmo_stationsliste_txt.asc does not list.

135 of them for icon, 132 for icon_eu (measured 2026-09-24), and they could not be asked for at all: absent from the catalogue, they were filtered out of every request even though their forecasts are published and fetch with HTTP 200. The catalogue is the only source of an ICAO id, so it stays the source for the stations it does list, and these are described from the run instead – with no ICAO id, which is already a value the catalogue produces for the stations it writes as ---- (GH-1966).

static _narrows_rather_than_empties(covered: set[str], df_raw: polars.DataFrame, dataset_name_original: str) → bool

Say whether a coverage listing names stations this catalogue has, so filtering by it narrows.

_covered_station_ids reads directory names, and a directory tree that stops being one directory per station still yields names: were single_stations/ reorganised into a subdirectory per lead time, the listing would come back as {"078", "168"}, pass the emptiness check, and filter every station in the catalogue out. The result is an empty stations frame with nothing raised and nothing logged – the same indistinguishable silence GH-1964 and GH-1947 are about, arriving through the very change that was meant to end it.

One station in common is enough: the products genuinely cover different subsets, so anything stricter would fire on the real disagreement this exists to represent.

_station_metadata_from_placemarks(dataset_name_original: str) → polars.DataFrame | None

Describe the stations a product forecasts for, from its newest all_stations run.

Read only when the catalogue is missing a station the product covers, so that a catalogue DWD completes stops costing anything, and cached per request for the same reason _covered_station_ids is.

_read_station_metadata_from_placemarks(dataset_name_original: str) → polars.DataFrame | None

Do the fetch _station_metadata_from_placemarks caches, or None if it could not be done.

_covered_station_ids(dataset_name_original: str) → set[str] | None

Read which stations one DMO product forecasts for, or None if that could not be read.

The catalogue at _url is one list for both products and matches neither. Of its 5811 stations icon covers 5622 and icon_eu 3556 (measured 2026-09-24), so a request for icon_eu advertised 2255 stations that can only ever answer with an empty frame – which from the caller’s side is indistinguishable from a station whose forecast is merely missing right now, and from the swallowed listing GH-1947 was about (GH-1964).

None rather than an empty set when the listing cannot be read, because the two mean opposite things: the caller keeps the whole catalogue for a listing it could not read, rather than answering that a product has no stations at all.

_read_covered_station_ids(dataset_name_original: str) → set[str] | None

Do the listing _covered_station_ids caches.

_all() → polars.LazyFrame

Get all stations from DMO.

Mosmix#

class DwdMosmixRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request MOSMIX data from the DWD server.

metadata

None

_values

None

issue: str | datetime.datetime | wetterdienst.provider.dwd.mosmix.api.DwdForecastDate

None

station_group: wetterdienst.provider.dwd.mosmix.api.DwdMosmixStationGroup

None

_url

None

_base_columns: ClassVar

[‘resolution’, ‘dataset’, ‘station_id’, ‘icao_id’, ‘start_timestamp’, ‘end_timestamp’, ‘latitude’, ‘…

classmethod available_issues(station_id: str, settings: wetterdienst.settings.Settings) → list[datetime.datetime]

Return datetimes for which MOSMIX L single-station files exist on DWD’s server.

The list is sorted in ascending order and contains only unique UTC datetimes. Only MOSMIX_L single-station files are considered; the LATEST symlink is excluded.

__post_init__() → None

Post-initialization of the DwdMosmixRequest class.

_all() → polars.LazyFrame

Read the MOSMIX station catalog from the DWD server and return a DataFrame.

Observation#

class DwdObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for DWD observation data.

metadata

None

_values

‘cast(…)’

_history

‘cast(…)’

_selects_by_period

True

property interval: portion.Interval | None

Interval of the request.

property _historical_interval: portion.Interval

Interval of historical data release schedule.

Historical data is typically release once in a year somewhere in the first few months with updated quality

property _recent_interval: portion.Interval

Interval of recent data release schedule.

Recent data is released every day somewhere after midnight with data reaching back 500 days.

property _now_interval: portion.Interval

Interval of now data release schedule.

Now data is released every hour (near real time) reaching back to beginning of the previous day.

_get_periods() → set[wetterdienst.metadata.period.Period] | None

Get periods based on the interval of the request.

static _parse_station_id(series: polars.Series) → polars.Series
filter_by_station_id(station_id: str | int | tuple[str, ...] | tuple[int, ...] | list[str] | list[int]) → wetterdienst.model.result.StationsResult

Filter by station id.

classmethod describe_fields(dataset: str | collections.abc.Sequence[str] | wetterdienst.model.metadata.ParameterSearch | wetterdienst.model.metadata.DatasetModel, period: str | wetterdienst.metadata.period.Period, language: Literal[en, de] = 'en') → dict

Describe fields of a dataset.

_all() → polars.LazyFrame

:return:

Radar#

class DwdRadarValues(parameter: str | wetterdienst.provider.dwd.radar.metadata.parameter.DwdRadarParameter, site: wetterdienst.provider.dwd.radar.sites.DwdRadarSite | None = None, fmt: wetterdienst.provider.dwd.radar.metadata.DwdRadarDataFormat | None = None, subset: wetterdienst.provider.dwd.radar.metadata.DwdRadarDataSubset | None = None, elevation: int | None = None, start: str | datetime.datetime | wetterdienst.provider.dwd.radar.metadata.parameter.DwdRadarDate | None = None, end: str | datetime.datetime | datetime.timedelta | None = None, resolution: str | wetterdienst.metadata.resolution.Resolution | wetterdienst.provider.dwd.radar.metadata.DwdRadarResolution | None = None, period: str | wetterdienst.metadata.period.Period | wetterdienst.provider.dwd.radar.metadata.DwdRadarPeriod | None = None, settings: wetterdienst.settings.Settings | None = None)

API for DWD radar data requests.

Request radar data from different places on the DWD data repository.

  • https://opendata.dwd.de/weather/radar/composite/

  • https://opendata.dwd.de/weather/radar/sites/

  • https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/

  • https://opendata.dwd.de/climate_environment/CDC/grids_germany/hourly/radolan/

  • https://opendata.dwd.de/climate_environment/CDC/grids_germany/5_minutes/radolan/

Initialization

Initialize the request object.

Args: parameter: requested parameter (e.g. RADOLAN_CDC) site: requested site (e.g. DX_REFLECTIVITY) fmt: requested format (e.g. BINARY) subset: requested subset (e.g. RADOLAN) elevation: requested elevation (e.g. 10) start: start of the requested data end: end of the requested data resolution: requested resolution (e.g. MINUTE_5) period: requested period (e.g. RECENT) settings: settings for the request

__new__(*args: object, **kwargs: object) → Self

Refuse a renamed argument by naming the new one, as TimeseriesRequest does.

__str__() → str

Return a string representation of the object.

__eq__(other: object) → bool

Compare two DwdRadarValues objects.

adjust_datetimes() → None

Adjust start and end attributes to match minute marks for RadarParameter.

  • RADOLAN_CDC is always published at HH:50. https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/recent/bin/

  • RW_REFLECTIVITY is published each 10 minutes. https://opendata.dwd.de/weather/radar/radolan/rw/

  • RQ_REFLECTIVITY is published each 15 minutes. https://opendata.dwd.de/weather/radar/radvor/rq/

  • All other radar formats are published in intervals of 5 minutes. https://opendata.dwd.de/weather/radar/composit/fx/ https://opendata.dwd.de/weather/radar/sites/dx/boo/

_attach_bufr(result: wetterdienst.provider.dwd.radar.api.RadarResult) → None

Attach the parsed BUFR contents to result.df when read_bufr is enabled.

No-op unless the request is for BUFR data and Settings.read_bufr is set. Requires the optional eccodes + pdbufr dependencies; a missing dependency or a parse error is logged and leaves result.df as None rather than failing the whole query.

query() → collections.abc.Iterator[wetterdienst.provider.dwd.radar.api.RadarResult]

Query radar data from the DWD server.

static _should_cache_download(url: str) → bool

Determine whether this specific result should be cached.

Here, we don’t want to cache any files containing “-latest-” in their filenames.

Args: url: URL of the file to be downloaded

Returns: Whether the file should be cached or not

_download_generic_data(url: str) → collections.abc.Iterator[wetterdienst.provider.dwd.radar.api.RadarResult]

Download radar data.

_download_radolan_data(url: str, start_date: datetime.datetime, end_date: datetime.datetime) → collections.abc.Iterator[wetterdienst.provider.dwd.radar.api.RadarResult]

Download RADOLAN_CDC data for a given datetime.

static _extract_radolan_data(archive_in_bytes: io.BytesIO) → collections.abc.Iterator[wetterdienst.provider.dwd.radar.api.RadarResult]

Extract the RADOLAN_CDC data from the archive.

Road#

class DwdRoadRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for DWD road weather data.

metadata

None

_values

None

_base_columns: ClassVar

(‘resolution’, ‘dataset’, ‘station_id’, ‘start_timestamp’, ‘end_timestamp’, ‘latitude’, ‘longitude’,…

_endpoint

‘https://www.dwd.de/DE/leistungen/opendata/help/stationen/sws_stations_xls.xlsx?__blob=publicationFil…’

_column_mapping: ClassVar

None

_dtypes: ClassVar

None

_all() → polars.LazyFrame

EA#

Hydrology#

class EAHydrologyRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for Environment Agency hydrology data.

metadata

None

_values

None

_url

‘https://environment.data.gov.uk/hydrology/id/stations.json’

_all() → polars.LazyFrame

Acquire all stations and filter for stations that have wanted resolution and parameter combinations.

Eaufrance#

Hubeau#

class HubeauRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for Eaufrance Hubeau data.

metadata

None

_values

None

_endpoint

None

_base_columns: ClassVar

()

_observation_dates(url: str) → polars.DataFrame

Read the timestamps one observations query carries.

Returns: Frame of station_id and timestamp, one row per observation.

_station_steps(station_ids: list[str]) → polars.DataFrame

Measure the interval each station transmits at.

Args: station_ids: The stations the referential lists, so that a station quiet during the first window can be asked about by name.

Returns: Frame of station_id and step, the station’s interval in minutes. A station that published nothing to measure is absent.

static _observations_url(start: datetime.datetime, end: datetime.datetime, grandeur: str, station_ids: list[str] | None = None) → str

Build one observations query, asking for the two fields an interval is measured from.

_site_elevations() → polars.DataFrame

Read the altitude of every hydrometric site.

Returns: Frame of code_site and elevation, null where the site publishes no altitude or one that cannot be the ground’s – see _ELEVATION_MIN. Empty when the referential cannot be read: the elevation is all it supplies, and a station list without one is better than none at all.

_all() → polars.LazyFrame

List each station under the resolution it transmits at.

ECCC#

Observation#

class EcccObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Download weather data from Environment and Climate Change Canada (ECCC).

  • https://www.canada.ca/en/environment-climate-change.html

  • https://www.canada.ca/en/services/environment/weather.html

Original code by Trevor James Smith. Thanks!

  • https://github.com/Zeitsperre/canada-climate-python

_endpoint

‘https://api.weather.gc.ca’

metadata

None

_values

‘cast(…)’

_timezone_mapping: ClassVar[dict]

None

_all() → polars.LazyFrame

Geosphere#

Observation#

class GeosphereObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for geosphere observation data.

metadata

None

_values

None

_endpoint

‘https://dataset.api.hub.geosphere.at/v1/station/historical/{dataset}/metadata/stations’

_all() → polars.LazyFrame

IMGW#

Hydrology#

class ImgwHydrologyRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for hydrological data from IMGW.

metadata

None

_values

None

_endpoint

‘https://dane.imgw.pl/datastore/getfiledown/Arch/Telemetria/Hydro/kody_stacji.csv’

_all() → polars.LazyFrame

:return:

Meteorology#

class ImgwMeteorologyRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request for meteorological data from the Institute of Meteorology and Water Management.

metadata

None

_values

None

_endpoint

‘https://dane.imgw.pl/datastore/getfiledown/Arch/Telemetria/Meteo/kody_stacji.csv’

_all() → polars.LazyFrame

Get all available stations.

Météo-France#

Synop#

class MeteoFranceSynopRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for Météo-France SYNOP data.

metadata

None

_values

None

_all() → polars.LazyFrame

Observation#

class MeteoFranceObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for Météo-France observation data.

metadata

None

_values

None

_all() → polars.LazyFrame
static _climate_stations(settings: wetterdienst.settings.Settings) → polars.DataFrame

Build the station list from Météo-France’s canonical station metadata registry.

MeteoSwiss#

Observation#

class MeteoswissObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for MeteoSwiss observation data.

metadata

None

_values

None

_station_endpoint

‘https://data.geo.admin.ch/ch.meteoschweiz.ogd-smn/ogd-smn_meta_stations.csv’

_all() → polars.LazyFrame

MET Norway#

Frost#

class MetnoFrostRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for MET Norway Frost API.

metadata

None

_values

None

_sources_url

‘https://frost.met.no/sources/v0.jsonld?types=SensorSystem&fields=id,name,validFrom,validTo,geometry,…’

classmethod is_configured() → bool
classmethod is_valid(settings: wetterdienst.settings.Settings | None = None) → bool
__post_init__() → None
_all() → polars.LazyFrame

NOAA#

GHCN#

class NoaaGhcnRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for NOAA GHCN data provider.

metadata

None

_values

None

_all() → polars.LazyFrame
static _take_daily_elevation(df: polars.LazyFrame) → polars.LazyFrame

Give a station’s hourly row the daily list’s elevation, where both lists mean one place.

Then every reader of the station gets one elevation, whichever row it takes (GH-2336). Only where the two lists put the station within 5 km of each other: for some ids they name different stations (MXM00076840 is ARRIAGA on the Chiapas coast in the hourly list and its data, TEMOSACHI 2000 km away and 1900 m higher in the daily one), and there each row keeps its own list’s height. Nor does a daily 0.0 replace a height the hourly list gives (GH-2362); both rows then keep their own as well. A daily list giving none leaves the hourly one.

_create_metaindex_for_ghcn_hourly() → polars.LazyFrame
_create_metaindex_for_ghcn_daily() → polars.LazyFrame

Acquire station listing for ghcn daily.

station listing

Variable

Columns

Type

Example

ID

1-11

Character

EI000003980

LATITUDE

13-20

Real

55.3717

LONGITUDE

22-30

Real

-7.3400

ELEVATION

32-37

Real

21.0

STATE

39-40

Character

NAME

42-71

Character

MALIN HEAD

GSN FLAG

73-75

Character

GSN

HCN/CRN FLAG

77-79

Character

WMO ID

81-85

Character

03980

inventory listing

Variable

Columns

Type

ID

1-11

CHARACTER

LATITUDE

13-20

REAL

LONGITUDE

22-30

REAL

ELEMENT

32-35

CHARACTER

FIRSTYEAR

37-40

INTEGER

LASTYEAR

42-45

INTEGER

National Weather Service#

Observation#

class NwsObservationRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for NWS observation.

metadata

None

_values

None

_endpoint

‘https://madis-data.ncep.noaa.gov/madisPublic1/data/stations/METARTable.txt’

_stations_filed_under_a_state_code

(‘PHBK’, ‘TIST’, ‘TISX’)

_elevation_missing

9999.0

_all() → polars.LazyFrame

WSV#

Observation#

class WsvPegelRequest

Bases: wetterdienst.model.request.TimeseriesRequest

Request class for WSV Pegelonline.

Pegelonline is a German river management facility and provider of river-based measurements for last 30 days.

metadata

None

_values

None

_endpoint

None

characteristic_values: ClassVar

None

_base_columns: ClassVar

()

_all() → polars.LazyFrame

Get stations for WSV Pegelonline.

It involves reading the REST API, doing some transformations and adding characteristic values in extra columns if given for each station.