Handling errors
Every failed request raises a subclass of
DataRetrievalError, so a single except
clause handles any failure regardless of which service you called:
import dataretrieval
try:
df, md = dataretrieval.waterdata.get_daily(
monitoring_location_id="USGS-05427718"
)
except dataretrieval.DataRetrievalError:
... # any request failure: error status, connection loss, too-large, ...
Connection-level failures (timeouts, DNS, refused connections) are wrapped as
NetworkError, so the clause above covers them
too – you never have to catch an httpx exception. A no-data result is not an
error: the modern getters return an empty DataFrame when nothing matches, so
check df.empty rather than catching anything.
Branch without knowing the concrete type
Every DataRetrievalError exposes three
read-anywhere fields, so you rarely need to import the specific subclasses:
.status_code– the HTTP status, orNonewhen the failure carried no response (a connection error, an over-long URL, …)..retry_after– seconds the server asked you to wait (itsRetry-Afterheader), orNone..retryable–Truewhen re-issuing the same request might succeed (a 429 / 5xx, or a connection failure);Falseotherwise.
except dataretrieval.DataRetrievalError as e:
if e.status_code == 404:
... # not found
elif e.retryable:
... # transient -- see the retry recipe below
else:
raise
Retry transient failures with backoff
.retryable and .retry_after make a backoff loop type-agnostic – it
covers rate limits (429), server errors (5xx), and connection failures alike,
honoring the server’s Retry-After hint when present:
import time
import dataretrieval
for attempt in range(5):
try:
df, md = dataretrieval.waterdata.get_continuous(
monitoring_location_id=sites
)
break
except dataretrieval.DataRetrievalError as e:
if not e.retryable or attempt == 4:
raise
time.sleep(e.retry_after or 2 ** attempt)
Resume a large Water Data request
The Water Data getters transparently split an over-large request into chunks.
When a transient failure interrupts one mid-stream, the work already completed
is preserved: catch ChunkInterrupted and call exc.call.resume() once the
condition clears – only the unfinished sub-requests are re-issued.
import time
from dataretrieval.waterdata import get_daily
from dataretrieval.waterdata.chunking import ChunkInterrupted
try:
df, md = get_daily(monitoring_location_id=long_list_of_sites)
except ChunkInterrupted as exc:
while True:
time.sleep(exc.retry_after or 5 * 60)
try:
df, md = exc.call.resume()
break
except ChunkInterrupted as again:
exc = again
The full taxonomy
See dataretrieval.exceptions for the complete class tree and per-type details.