easysnowdata.stations.clients.AWDBClient#

class easysnowdata.stations.clients.AWDBClient(base_url: str = 'https://wcc.sc.egov.usda.gov/awdbRestApi/services/v1', timeout: int = 180, max_retries: int = 3, backoff: int = 6, session: Session | None = None)[source]#

Bases: object

Client for the USDA NRCS AWDB REST API v1.

Parameters:
  • base_url (str) – Base URL of the AWDB REST API. Override for staging / mirror endpoints.

  • timeout (int) – HTTP request timeout in seconds.

  • max_retries (int) – Number of retry attempts on transient server errors (5xx).

  • backoff (int) – Base backoff delay in seconds. Actual delay = backoff × attempt_number.

  • session (requests.Session or None) – Optional pre-configured session (useful for auth headers, proxies, etc.).

get_reference_data(tables: list[str] | str = 'all') dict[str, Any][source]#

Fetch AWDB reference/lookup tables.

Parameters:

tables (list[str] or str) – One or more reference table names, or "all" for everything. Common tables: "networks", "elements", "states", "counties", "durations", "units".

Returns:

dict – Parsed JSON response. Keys are table names; values are lists of {code, name, description} dicts.

Example

>>> client = AWDBClient()
>>> ref = client.get_reference_data(["networks", "elements"])
>>> ref["networks"][0]
{'code': 'SNTL', 'name': 'SNOTEL', ...}
get_stations(networks: list[str] | str | None = None, states: list[str] | str | None = None, huc: str | None = None, county_name: str | None = None, active_only: bool = False, station_triplets: list[str] | str | None = None) list[dict][source]#

List stations matching the given filters.

Returns only basic identification fields (no element inventory). Use get_metadata() to retrieve full metadata including elements.

Parameters:
  • networks (list[str] or str, optional) – Network code(s) to filter by, e.g. ["SNTL", "SNTLT"]. Defaults to all networks ("*").

  • states (list[str] or str, optional) – Two-letter state code(s), e.g. ["CO", "UT"].

  • huc (str, optional) – Hydrologic Unit Code prefix (2–12 digits). Stations whose HUC starts with this prefix are returned.

  • county_name (str, optional) – County name filter (case-insensitive prefix match).

  • active_only (bool) – If True, only return stations with no endDate.

  • station_triplets (list[str] or str, optional) – Explicit list of station triplets to retrieve. Overrides network and state filters when provided.

Returns:

list[dict] – List of station dicts with keys: stationTriplet, stationId, networkCode, name, stateCode, countyName, huc, latitude, longitude, elevation, beginDate, endDate.

Example

>>> stations = client.get_stations(networks=["SNTL"], states=["CO"])
>>> len(stations)
117
get_metadata(triplets: list[str] | str, elements: list[str] | str = '*', durations: list[str] | str = '*', include_forecast_point: bool = False, include_reservoir: bool = False, active_only: bool = False) dict | list[dict][source]#

Retrieve full station metadata including the element inventory.

Given a single triplet this returns one dict, which is the get_metadata(station_id) -> dict contract of DESIGN.md §3.4 that the other four clients implement. Given a list it returns one dict per station, which the inventory pipeline needs: AWDB carries ~4 000 stations and fetching them one at a time is not viable, so this client keeps the batch form the API supports.

Parameters:
  • triplets (list[str] or str) – Station triplet(s), e.g. "303:CO:SNTL" or a list.

  • elements (list[str] or str) – Element code(s) to filter the station element list by, e.g. ["WTEQ", "SNWD"]. Pass "*" for all elements.

  • durations (list[str] or str) – Duration name(s) to filter by, e.g. ["DAILY", "MONTHLY"]. Pass "*" for all durations.

  • include_forecast_point (bool) – Include forecast point metadata if available.

  • include_reservoir (bool) – Include reservoir metadata if available.

  • active_only (bool) – If True, only return active elements.

Returns:

dict or list[dict] – One dict when triplets is a single triplet, otherwise one dict per station. Each dict contains all AWDB metadata fields plus a stationElements list (one entry per matching element × duration combination). A single triplet that matches no station returns an empty dict.

Notes

Only stations that have at least one matching element are returned. Requests are automatically split into batches of at most 150 triplets and then split further on request-size errors to avoid URL-length and payload-size limits.

Example

>>> meta = client.get_metadata(
...     ["303:CO:SNTL", "713:CO:SNTL"],
...     elements=["WTEQ", "SNWD"],
...     durations=["DAILY"],
... )
>>> meta[0]["stationElements"][0]["elementCode"]
'SNWD'
get_data_by_water_year(triplets: list[str] | str, elements: list[str] | str, water_year: int, duration: str = 'DAILY', **kwargs) list[dict][source]#

Convenience wrapper: fetch data for a single water year.

A water year runs from October 1 of the previous calendar year to September 30 of water_year. For example, WY2024 spans 2023-10-01 through 2024-09-30.

Parameters:
  • triplets (list[str] or str)

  • elements (list[str] or str)

  • water_year (int) – The water year integer (e.g., 2024).

  • duration (str)

  • **kwargs – Additional keyword arguments passed to _get_data_awdb().

Returns:

list[dict] – Same structure as _get_data_awdb() (nested AWDB payload, not the flat records of get_data()).

get_normals(triplets: list[str] | str, elements: list[str] | str, duration: str = 'DAILY', normal_period: str = '1991-2020', central_tendency_type: str = 'MEDIAN') list[dict][source]#

Retrieve climatological normals (medians or averages) for stations.

Parameters:
  • triplets (list[str] or str)

  • elements (list[str] or str)

  • duration (str)

  • normal_period (str) – Reference period string: "1991-2020", "1981-2010", or "1971-2000".

  • central_tendency_type (str) – "MEDIAN" or "AVERAGE".

Returns:

list[dict] – Same structure as _get_data_awdb() (nested AWDB payload) with median/average fields alongside value in the values list.

Example

>>> norms = client.get_normals(
...     "303:CO:SNTL", ["WTEQ"], normal_period="1991-2020"
... )
get_all_stations(active_only: bool = False, bbox: tuple[float, float, float, float] | None = None) list[dict][source]#

Standardized station list.

Parameters:
  • active_only (bool) – If True, only return active stations.

  • bbox (tuple, optional) – (min_lon, min_lat, max_lon, max_lat) bounding box filter.

Returns:

list[dict] – Station dicts (same schema as get_stations()).

get_data(station_ids: list[str] | str | None = None, variables: list[str] | str | None = None, bbox: tuple[float, float, float, float] | None = None, begin_date: str | date | None = None, end_date: str | date | None = None, interval: str = 'daily', include_flags: bool = False) list[dict][source]#

Standardized data fetch — returns a flat list of observation records.

Parameters:
  • station_ids (list[str] or str or None) – AWDB station triplet(s), e.g. "303:CO:SNTL". Required unless bbox is provided.

  • variables (list[str] or str or None) – Element codes (e.g. "WTEQ") or standardized types (e.g. "swe"). None returns all elements in VARIABLES.

  • bbox (tuple, optional) – (min_lon, min_lat, max_lon, max_lat). Alternative to station_ids; fetches data for all stations in the box.

  • begin_date (str or date, optional) – Start date ("YYYY-MM-DD"). Defaults to earliest available.

  • end_date (str or date, optional) – End date (inclusive). Defaults to today.

  • interval (str) – Temporal resolution: "daily", "hourly", "monthly", etc. Mapped to the AWDB duration parameter.

  • include_flags (bool) – Reserved; the AWDB REST API does not return per-value QC flags.

Returns:

list[dict]

Flat list of observation records:

{
    "station_id": "303:CO:SNTL",
    "date": "2024-01-15",
    "variable": "WTEQ",
    "type": "swe",
    "value": 14.2,
    "units": "cm",
    "interval": "daily",
    # "flag": None  (only present when include_flags=True)
}

Raises:
  • ValueError – If neither station_ids nor bbox is provided.

  • AWDBError – If a batch request fails after all retries.