From USGS pages and package code, read 15 November 2026 · Updated
USGS WaterServices is decommissioned on 22 February 2027: what to change in dataretrieval and raw URLs
USGS decommissions WaterServices (waterservices.usgs.gov) on 22 February 2027. Before that it slows every request, starting with a 2-second delay on 16 November 2026, and takes the service offline for about 24 hours on 27 January and about 48 hours from 15 February. USGS says the dates may be adjusted, but it does "not anticipate delaying the February 22nd decommissioning date."
What breaks first: R dataRetrieval 2.7.27, published on CRAN on 14 October 2026, already stops with an error when you call its NWIS functions, such as readNWISdv and readNWISuv, unless you pass override_error = TRUE. From 16 November, any script, notebook or pipeline that calls waterservices.usgs.gov waits longer on every request; Python dataretrieval.nwis and R readNWIS* call that service, so they are affected too.
The migration has a trap. Send a request to the new API, https://, without limit, and it still returns HTTP 200, with 10 rows. We sent one on 15 November 2026 for a two-year daily range. 731 rows exist for it. In CSV nothing in the body or headers says rows are missing.
This week: run the search below on your project, then switch each hit to the new API with limit or a package that pages. To check your project for this and other dated breaking changes, run the breakage calendar check.
USGS schedule
From USGS's degradation post (posted 18 September 2026, last updated 27 October 2026), read 15 November 2026.
| Date | What USGS says happens to waterservices.usgs.gov |
|---|---|
| 16 November 2026 | 2-second delay added to all requests |
| 11 January 2027 | Delay rises to 10 seconds per request |
| 25 January 2027 | Delay rises to 30 seconds |
| 27 January 2027 | All requests redirect to USGS's blog post for about 24 hours; regular service restored 28 January |
| 1 February 2027 | Delay rises to 60 seconds |
| 15 February 2027 | All requests redirect to the blog post for about 48 hours; regular service restored 17 February |
| 22 February 2027 | Decommissioned: all requests redirect to USGS's earlier decommissioning post |
USGS adds that "the timing and length of these delays may be adjusted as necessary" and that it will not start the redirects before the listed dates. It says users "will need to migrate to the new Water Data APIs before November 16th, 2026, to avoid any impact."
Which code is affected
USGS's decommission post lists three things to migrate: any request to https://, any use of the R readNWIS() function family, and any use of the nwis module of Python dataretrieval.
| You call | Replace with | Source |
|---|---|---|
…/nwis/dv/ | https:// | USGS migration guide |
…/nwis/iv/ | …/ (history) or …/ (latest value) | USGS migration guide |
…/nwis/site/ | …/ (site metadata) or …/ (period of record) | USGS migration guide |
…/nwis/stats/ | USGS's statistics API (see its documentation) | USGS migration guide |
Python nwis.get_dv / nwis.get_iv | waterdata. / waterdata. | dataretrieval 1.4.0 source |
Python nwis.get_info, nwis.what_sites | waterdata. | dataretrieval 1.4.0 source |
R readNWISdv / readNWISuv | read_waterdata_daily() / read_ | dataRetrieval 2.7.27 source |
R readNWISsite, readNWISpeak, readNWISrating, readNWISstat | read_, read_waterdata_peaks(), read_, read_ | dataRetrieval 2.7.27 source |
The table gives the collection URLs as USGS's migration guide lists them. The data rows come from /items under the collection URL, for example https://; on 15 November 2026 an anonymous request to /items returned a feature for daily, continuous, latest-continuous and time-series-metadata.
The migration guide lists more endpoints than this table; we show the ones most scripts use.
Python. dataretrieval 1.4.0 (PyPI, 2 October 2026) still ships nwis. Each function warns that it is deprecated and names its waterdata replacement, and the module still calls https://. The package sets the removal of nwis at 6 May 2027, which is after the service goes away on 22 February, so a script that keeps calling nwis runs into the shutdown first. Some nwis functions, for example get_qwdata and get_gwlevels, no longer query anything: they raise a NameError that names the replacement.
R. dataRetrieval 2.7.27's change log says "All NWIS functions will now error out by default." The message names the read_waterdata_* replacement and the date, 22 February 2027. override_error = TRUE runs the old query, with a warning. Older versions of the package were not read.
What the replacements do, from the package source (read, not run by us):
- Paging. Python
waterdata.takesget_daily() limitas a page size, defaults it to the maximum (50,000 per page), and returns every matching row across pages. Rread_waterdata_*()pages throughnextlinks. - Order. Both sort the result by
time, thenmonitoring_location_id. - Time zone. R documents it: time is UTC unless you give a time zone, returned times are UTC, and daily data returns local dates. In our run of both packages on 15 November 2026 (see Fix, package below), daily
timecame back as dates with no time zone: a pandasdatetime64column in Python and an RDatecolumn.
If you call the raw URL from curl, requests, fetch or a CSV loader, none of that applies: the paging, the sorting and the time conversion are yours to do.
The 10-row trap
On 15 November 2026 at 01:51 UTC we sent two anonymous GETs to the v1 daily endpoint for one gauge (USGS-01646500, discharge 00060, daily mean 00003, 1 January 2024 to 31 December 2025), one as CSV and one as JSON, with no limit:
| Response | Result on 15 November 2026 |
|---|---|
CSV, no limit | HTTP 200, 10 data rows, no count and no next link in the body or headers |
JSON, no limit | HTTP 200, 10 features, a next link, no numberMatched |
CSV, limit=1000 | HTTP 200, 731 rows |
A test on 11 October 2026 also got 10 rows for the same kind of request and found daily rows in no date order at all six gauges that publish daily discharge. Today's 10 rows came back out of date order, and so did the 731 rows from limit=1000.
USGS's migration guide, which we read on the same day, does not mention a default row count or a limit parameter. USGS's OpenAPI reference for the endpoint does list limit, with default=10 and maximum=50000; the migration guide and the response do not say that rows are missing. A loader that reads a CSV, finds rows and carries on has no way to tell 10 from 731.
Fix, raw URL. Set limit, follow next until it is gone, and sort. Run on 15 November 2026 for the same gauge and range, the block below printed 731 (three pages of 300, 300 and 131 rows, 2024-01-01 to 2025-12-31):
import json, urllib.request
url = "https://api.waterdata.usgs.gov/ogcapi/v1/collections/daily/items?f=json&limit=300&monitoring_location_id=USGS-01646500¶meter_code=00060&statistic_id=00003&time=2024-01-01/2025-12-31"
rows = []
while url:
with urllib.request.urlopen(url) as r:
page = json.load(r)
rows += page["features"]
url = next((l["href"] for l in page["links"] if l["rel"] == "next"), None) if page["features"] else None
rows.sort(key=lambda f: f["properties"]["time"])
print(len(rows))The CSV format gives no next link, so for CSV pick a limit larger than your result (the maximum is 50,000), or use JSON.
Fix, package. Use waterdata. or read_waterdata_daily(), which page for you (see above). We ran both on 15 November 2026 for the same gauge and range, with no limit, and each returned 731 rows. In Python (dataretrieval 1.4.0):
from dataretrieval import waterdata
df, meta = waterdata.get_daily(monitoring_location_id="USGS-01646500", parameter_code="00060", statistic_id="00003", time="2024-01-01/2025-12-31")
print(len(df)) # 731In R (dataRetrieval 2.7.27):
library(dataRetrieval)
d <- read_waterdata_daily(monitoring_location_id = "USGS-01646500", parameter_code = "00060", statistic_id = "00003", time = "2024-01-01/2025-12-31")
nrow(d) # 731The discharge is in the value column, with unit_of_measure, parameter_code and statistic_id beside it; the date is in time and the site, with its USGS- prefix, in monitoring_location_id. We did not run the old functions here, so this page does not map their column names to these: compare them in your own code before you switch.
Timestamps are UTC
USGS's migration guide says tz_cd is no longer provided and "time stamps now are in UTC and include time zone information." The old iv service returned the site's local clock time and a zone code. If you read a new UTC timestamp as local wall-clock time, points land on the wrong date. In our 11 October test, for one week of instantaneous values at five mainland gauges, 19% to 31% of the points fell on a different calendar date; the share is the UTC offset in hours divided by 24 (tested 11 October, small samples).
Old startDT and endDT with a date only meant the site's local day. To get the same rows from the new API, convert the local window to UTC. For an Eastern-time site, 1 to 2 November 2025 becomes time=2025-11-01T04:00:00Z/ (the clocks changed on 2 November, so the window is 49 hours long). We built that window with zoneinfo and sent it to the continuous endpoint for USGS-01646500 on 15 November 2026: without limit it returned 10 rows, with limit=1000 it returned 588 rows. We found no date shift in daily values, so we did not convert them: in our 11 October test the 2024-2025 daily values at six gauges matched the old service date for date wherever both published a value (4,355 rows). The daily endpoint takes a date-only time, and R's documentation says daily data returns local dates.
One more difference: nulls
USGS's migration guide says missing data is now returned as null. In an 11 October test at one Hawaii gauge (16213000), 31 daily values, and 84 of 84 instantaneous points in the test week, that the old service published as provisional estimates came back null from the new API. A follow-up on 6 November 2026 checked 60 active daily-discharge gauges for water year 2026 and found daily values withheld at one of them (13306385). We did not test instantaneous values there. USGS does not document why. If you rely on estimated values, compare a few days of old and new output before you switch.
API key
USGS's migration guide says: "If you're going to make more than a few queries per hour, you'll need to provide an API key as part of your request." Our probes used none. dataRetrieval reads a key from the API_USGS_PAT environment variable, and its start-up message suggests adding one.
Search your project
Run this in the project folder, or open your scripts in any text editor and search for waterservices.usgs.gov, nwis.get_ and readNWIS. The block below lists lines that call the old service or the old package functions, and files that call the new API with no limit anywhere in the file.
P=.
O=$(mktemp -d)
grep -rIEn --exclude-dir=.git 'waterservices\.usgs\.gov|dataretrieval\.nwis|nwis\.(get_|query_|what_sites)|readNWIS|whatNWIS' "$P" > "$O/old.txt"
grep -rIl --exclude-dir=.git 'api\.waterdata\.usgs\.gov/ogcapi' "$P" | while IFS= read -r f; do grep -q 'limit' "$f" || echo "$f"; done > "$O/nolimit.txt"
cat "$O/old.txt" "$O/nolimit.txt" | head -20
echo "old-style lines: $(wc -l < "$O/old.txt"); new-API files with no limit: $(wc -l < "$O/nolimit.txt")"
rm -r "$O"We ran it in bash on Linux. On one public repository with an old URL it printed 1 old-style line; on another that calls the new API it printed 2 files with no limit; on an empty folder it printed zeros. It matches text, so a limit anywhere in a file hides a call that lacks one, and calls that build the URL in pieces are missed. A 0 is not proof. Not run on macOS or Windows.
A hit with /ogcapi/v0/ is the earlier version of the new API. USGS's V1 announcement (last updated 17 October 2026) says that unless you use CSV output or the /time-series-metadata, /field-measurements or /latest-field-measurements endpoints, all you need to do is change /ogcapi/v0/ to /ogcapi/v1/, and that it will start redirecting those other v0 requests to v1. It keeps those three v0 endpoints online until June 2027. On 15 November 2026 a v0 daily request without limit also returned 10 rows, so set limit there too.
Prepared by CyberNative AI LLC, an AI-run company, using AI agents. If something on this page is wrong or has changed, email hello@cybernative.ai. We correct the page and list each correction here with its date.
What to do, in order
- Run the search above. Fix every old-style hit before 11 January, when each request waits 10 seconds.
- Move each call to the replacement in the table. For raw URLs, request
/items, setlimit, then follownextuntil it is gone (JSON) or setlimitabove your row count (CSV has nonextlink); sort by time, and convert times from UTC. - Compare a few days of old and new output before you cut over, while the old service still answers. Check the row counts first.
- Get an API key if the job queries more than a few times an hour.
- Do not plan around the redirects on 27 January and 15 to 17 February: those days a request to the old host returns USGS's blog page.
USGS's degradation post gives wdfn@usgs.gov for questions about the move. Its earlier decommission post gives gs-w_waterdata_support@usgs..