georest.usgs.wqp

Client for the Water Quality Portal (WQP), run by the USGS and EPA.

The largest public store of US water-quality data: samples and field measurements from the USGS, EPA, states, tribes and hundreds of other organizations, at more than a million sites:

from georest.usgs import wqp

sites = wqp.find_sites(state="CO", county="069", site_type="Stream")
temps = wqp.get_results(sites=["USGS-09380000"], characteristic="Temperature, water",
                        start="2025-01-01", end="2025-12-31")
temps[["date", "value", "unit", "Result_MeasureStatusIdentifier"]]

This client reads the portal’s WQX 3.0 format. The portal’s original format stopped receiving USGS data in 2024: a 2025 search for USGS results that returns 130 rows in WQX 3.0 returns none in the original (checked 2026-10-28). WQX 3.0 queries are slower, often most of a minute.

Results are pandas DataFrames with every column the portal sends, plus lon and lat (standardized to NAD83) and, for results, date, value (the measurement as a number) and unit.

Copyright 2026 Ryan Rock and Ian Housman

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Module Attributes

TIMEOUT

WQX 3.0 queries routinely take most of a minute; allow five.

STATE_FIPS

State postal codes onto FIPS codes, which the portal's state and county filters use (US:08 for Colorado).

Functions

find_sites(*[, sites, state, county, bbox, ...])

Monitoring locations in the Water Quality Portal.

get_results(*[, sites, state, county, bbox, ...])

Water-quality results: samples and field measurements.

georest.usgs.wqp.TIMEOUT = 300

WQX 3.0 queries routinely take most of a minute; allow five.

georest.usgs.wqp.STATE_FIPS = {'AK': '02', 'AL': '01', 'AR': '05', 'AS': '60', 'AZ': '04', 'CA': '06', 'CO': '08', 'CT': '09', 'DC': '11', 'DE': '10', 'FL': '12', 'GA': '13', 'GU': '66', 'HI': '15', 'IA': '19', 'ID': '16', 'IL': '17', 'IN': '18', 'KS': '20', 'KY': '21', 'LA': '22', 'MA': '25', 'MD': '24', 'ME': '23', 'MI': '26', 'MN': '27', 'MO': '29', 'MP': '69', 'MS': '28', 'MT': '30', 'NC': '37', 'ND': '38', 'NE': '31', 'NH': '33', 'NJ': '34', 'NM': '35', 'NV': '32', 'NY': '36', 'OH': '39', 'OK': '40', 'OR': '41', 'PA': '42', 'PR': '72', 'RI': '44', 'SC': '45', 'SD': '46', 'TN': '47', 'TX': '48', 'UT': '49', 'VA': '51', 'VI': '78', 'VT': '50', 'WA': '53', 'WI': '55', 'WV': '54', 'WY': '56'}

State postal codes onto FIPS codes, which the portal’s state and county filters use (US:08 for Colorado).

georest.usgs.wqp.find_sites(*, sites: str | Iterable[str] | None = None, state: str | None = None, county: str | None = None, bbox: tuple | None = None, huc: str | None = None, site_type: str | Iterable[str] | None = None, characteristic: str | Iterable[str] | None = None, providers: str | Iterable[str] | None = None) → Any[source]

Monitoring locations in the Water Quality Portal.

Parameters:
  • sites – Site ids, e.g. "USGS-09380000".

  • state – A state postal code ("CO") or FIPS code ("08").

  • county – A county’s 3-digit FIPS code, with state.

  • bbox – (west, south, east, north) in decimal degrees.

  • huc – A hydrologic unit code (2 to 12 digits).

  • site_type – e.g. "Stream", "Lake, Reservoir, Impoundment", "Well", "Spring".

  • characteristic – Keep only sites with samples of this, e.g. "Nitrate" or "Temperature, water".

  • providers – "NWIS" (USGS), "STORET" (EPA and partners), or both.

Returns:

pandas.DataFrame, one row per site, with every WQX 3.0 location column (Location_Identifier, Location_Name, Location_Type, HUC codes, drainage area, …) plus lon and lat.

Example:

from georest.usgs import wqp

# Stream sites in Larimer County, Colorado, that measured nitrate
sites = wqp.find_sites(state="CO", county="069", site_type="Stream",
                       characteristic="Nitrate")
sites[["Location_Identifier", "Location_Name", "Org_FormalName", "lon", "lat"]]
georest.usgs.wqp.get_results(*, sites: str | Iterable[str] | None = None, state: str | None = None, county: str | None = None, bbox: tuple | None = None, huc: str | None = None, site_type: str | Iterable[str] | None = None, characteristic: str | Iterable[str] | None = None, providers: str | Iterable[str] | None = None, start: str | None = None, end: str | None = None, profile: str = 'basicPhysChem') → Any[source]

Water-quality results: samples and field measurements.

Parameters:
  • sites – Where to look, as for find_sites(). At least one place is required.

  • state – Where to look, as for find_sites(). At least one place is required.

  • county – Where to look, as for find_sites(). At least one place is required.

  • bbox – Where to look, as for find_sites(). At least one place is required.

  • huc – Where to look, as for find_sites(). At least one place is required.

  • site_type – Where to look, as for find_sites(). At least one place is required.

  • providers – Where to look, as for find_sites(). At least one place is required.

  • characteristic – What was measured, e.g. "Temperature, water", "Nitrate", "pH", "Escherichia coli".

  • start – Sample dates, "YYYY-MM-DD".

  • end – Sample dates, "YYYY-MM-DD".

  • profile – The portal’s column set: "basicPhysChem" (the default) or "fullPhysChem" for every lab and method detail.

Returns:

pandas.DataFrame, one row per result, with every WQX 3.0 column (site, activity, characteristic, measure and unit, status, detection limits, method, …) plus lon, lat, date, value (the measurement as a number, empty where the portal reports none, such as a non-detect; see Result_ResultDetectionCondition) and unit.

Example:

from georest.usgs import wqp

temps = wqp.get_results(sites="USGS-09380000", characteristic="Temperature, water",
                        start="2025-01-01", end="2025-12-31")
temps[["date", "value", "unit", "Result_MeasureStatusIdentifier"]]