Groundwater levels#

SGU publishes two kinds of groundwater level data, both available under client.levels:

  • Observed levels (client.levels.observed) are measurements from SGU’s network of monitoring stations.

  • Modeled levels (client.levels.modeled) are estimates from the SGU-HYPE model, for model areas covering Sweden.

Observed levels#

Finding stations#

Look up a station by its identifier (station_id, platsbeteckning) or its name (station_name, obsplatsnamn):

from sgu_client import SGUClient

with SGUClient() as client:
    station = client.levels.observed.get_station_by_name(station_id="95_2")
    # or several at once
    stations = client.levels.observed.get_stations_by_names(
        station_id=["95_2", "101_1"]
    )

To search for stations, use get_stations() with the filters described in Filtering:

# stations in southern Sweden
stations = client.levels.observed.get_stations(
    bbox=[12.0, 55.0, 16.0, 58.0],
    limit=50,
)

A station’s active_since and active_until are dates (midnight UTC, see Dates and times). active_until is None while the station is still active, or if the API sent a value that couldn’t be parsed (a warning is logged then):

live = [s for s in stations.features if s.properties.active_until is None]

Fetching measurements#

Fetch the measurements for one or more stations, optionally limited to a time range with tmin and tmax (ISO strings or datetime objects):

measurements = client.levels.observed.get_measurements_by_name(
    station_id="95_2",
    tmin="2024-10-01",
    tmax="2025-09-30",
)

# several stations in one request
measurements = client.levels.observed.get_measurements_by_names(
    station_id=["95_2", "101_1"],
    tmin="2020-01-01",
)

Measurements refer to their station by station_id, so that is the fastest way to fetch them. Passing station_name instead works, but costs an extra request to look up the station and logs a warning. If any of the station names, or any of the stations passed to get_stations_by_names(), is not found, the lookup raises SGUNotFoundError naming them instead of returning partial results.

Each measurement has levels in meters above sea level (water_level_masl_m) and below ground (water_level_below_ground_m). See GroundwaterMeasurementProperties for all fields.

Modeled levels#

Modeled levels are published per model area. Find areas by location, then fetch their levels, optionally limited to a date range with tmin and tmax as for observed measurements:

areas = client.levels.modeled.get_areas(bbox=[12.0, 55.0, 16.0, 58.0], limit=10)

levels = client.levels.modeled.get_levels_by_areas(
    [area.properties.area_id for area in areas.features],
    tmin="2024-01-01",
    tmax="2024-12-31",
)

If you have a coordinate rather than an area, get_levels_by_coords() finds the areas near it for you:

# Gothenburg
levels = client.levels.modeled.get_levels_by_coords(
    lat=57.7089, lon=11.9746, tmin="2024-01-01"
)

Modeled values are given as percentiles (0–100), separately for small and large groundwater resources. See ModeledGroundwaterLevelProperties for all fields.

Filtering#

The general query methods accept these filters:

Parameter

Description

tmin, tmax

Earliest and latest date (inclusive), as ISO strings or datetime objects

bbox

Bounding box as [min_lon, min_lat, max_lon, max_lat]

filter_expr

CQL filter using the Swedish API field names, e.g. "akvifer='JS'"

limit

Maximum number of features to return

get_measurements() and get_levels() accept all of them; get_stations() and get_areas() accept all but tmin and tmax. Use tmin/tmax to filter by date: they filter on the measurement’s observation date or the modeled level’s date. The raw OGC datetime parameter is still accepted, but tmin/tmax is the preferred way.

Results are paginated automatically, so limit can be larger than the API’s page size. The convenience methods (get_*_by_name(), get_levels_by_*()) build these filters for you, and forward any extra keyword arguments such as limit or bbox.