River Discharge
POST /api/v1/rivers delivers hourly freshwater discharge at the mouth of every listed river
in a box (or at mouths passed with the request), for point or surface sources in an ocean model. A nested regional model otherwise
receives river water only through its open boundaries and initial state, so a river that enters
inside the domain is missing as a source and its freshwater decays over the run.
Method
The method is common practice for gauge-driven river forcing. The NOAA Operational Forecast Systems map US Geological Survey real-time gauges onto model cells with a per-river scale factor and fall back to climatology. Regional estuary models scale gauged flow to ungauged area by the ratio of drainage areas.
Gauges, derived for every request. Only a river’s mouth is listed. From its NHDPlus outlet flowline, forcingkit asks the USGS NLDI for every USGS site upstream, then keeps a site if:
USGS classes it as a stream site (
ST). Tidal streams (ST-TS) and estuaries (ES) are left out, because their flow reverses with the tide; on the Connecticut River that rules out Hartford and Middle Haddam;it has a drainage area, and a discharge record (15-minute or daily) covering the window;
it drains at least
min_shareof the mouth’s area (default 0.01), so headwater brooks drop out;no other kept site lies downstream of it, which leaves the lowest gauge on each branch.
The kept gauges then measure disjoint parts of the basin. The selection follows the window: a gauge that opened in 2010 is used from 2010 on, and one that closed is dropped from then on.
Discharge at the mouth. The sum of the river’s gauges, scaled by the drainage area at the mouth over the gauged area:
\[Q_\text{mouth} = \frac{A_\text{mouth}}{\sum_k A_k} \sum_k Q_k\]Gauge areas come from the USGS monitoring-location records; the mouth’s area is that of the outlet’s NLDI basin.
Time base. The 15-minute record from the USGS Water Data APIs (
continuous), averaged to hourly values centred on the hour. The store holdshours + 1records, fromstart_timetostart_time + hours.Gaps. A gap of up to
max_gap_hours(default 6) with values on both sides is interpolated. A longer gap takes that day’s mean from thedailyrecord. A day with neither is an error, unlessallow_climatologyis set; then it takes the USGS day-of-year median of daily flow. Every filled hour is flagged infill(0 measured, 1 interpolated, 2 daily mean, 3 climatology median), and the store’sprovenanceattribute counts them per river.Water properties. Salinity is zero. Water temperature (
temperature) comes from the first gauge on the river that measures it (USGS parameter 00010), and is NaN where none does.Provisional data. USGS values are provisional until approved, typically months later, and provisional values are revised. A store containing any provisional value is marked
provisional = Trueand is served from the cache for one day only; after that a request rebuilds it.Travel time. None by default. A mouth entry may set
lag_hours; gauge values are then taken that many hours earlier.
Mouths
Mouths are listed in forcingkit/data/rivers/*.json and in any *.json file in the directory
named by FORCINGKIT_RIVER_MOUTHS_DIR; a later entry with the same name replaces an earlier one.
A request may instead pass its own mouths. Adding a river needs a mouth entry and no code or
release:
{"mouths": [
{"name": "Connecticut", "lon": -72.3305, "lat": 41.2726, "comid": 7703046},
{"name": "Saugatuck", "lon": -73.36, "lat": 41.10, "trace_from": "USGS-01208990"}
]}
name,lon,lat(required): the point places the mouth for the bbox test.comid: the NHDPlus outlet flowline. Otherwisetrace_fromnames any NLDI feature on the river (a USGS site such asUSGS-01184000) and the outlet is the end of its downstream trace. Failing both, the point is snapped to the nearest flowline, with a warning: at an estuary the nearest flowline can be a short coastal one rather than the river.includeandexclude: USGS site numbers to pin or rule out where judgement beats the rule (for example a gauge below a dam whose releases should not drive the forcing). A pinned set that drains more than the mouth’s area is refused as double counting.lag_hours: travel time from the gauges to the mouth, applied to the gauge window.
The id of a store includes the mouth entries and min_share, but not the derived gauges, which
are recorded in the store instead. NLDI facts about a mouth (its outlet, basin area, upstream sites
and which sites lie downstream of which) are cached for 30 days under usgs/derivations in the
cache directory, so only the first request for a mouth takes long (about a minute for the
Connecticut, whose basin holds 1,375 USGS sites).
The packaged mouths are the rivers into Long Island Sound. For a request over the Sound in April 2026 the derivation chose:
River |
USGS gauges kept |
Gauged (mi²) |
Mouth (mi²) |
Scale |
|---|---|---|---|---|
Housatonic |
01205500 at Stevenson; 01208500 Naugatuck at Beacon Falls |
1,804 |
1,944 |
1.08 |
Quinnipiac |
01196500 at Wallingford; 01196620 Mill River near Hamden; 01196561 Muddy River near East Wallingford |
148 |
204 |
1.37 |
Connecticut |
01184000 at Thompsonville; 01189995 Farmington at Tariffville |
10,237 |
11,242 |
1.10 |
Thames |
01127000 Quinebaug at Jewett City; 011230695 Shetucket at Taftville; 01127500 Yantic at Yantic |
1,314 |
1,470 |
1.12 |
The Quinnipiac’s outlet basin is New Haven Harbor’s, so its scaled flow also stands for the
harbour’s other streams. The Housatonic at Stevenson is below a hydroelectric dam, so its hourly
flow follows the dam’s releases. The Bronx River gauge (01302020) has no record before the 2000s,
so a request for an earlier window fails unless skip_ungauged is set, which leaves such a
river out and lists it in the store’s skipped_rivers.
Request and store
curl -s -X POST localhost:9598/api/v1/rivers -H 'Content-Type: application/json' -d '{
"bbox": {"min_lon": -73.855, "min_lat": 40.75, "max_lon": -71.9, "max_lat": 41.45},
"start_time": "2026-04-02T00:00:00Z", "hours": 48}'
Optional fields: mouths, min_share (default 0.01), max_gap_hours (default 6),
allow_climatology and skip_ungauged (both default false), and cache_bust.
For that request (four rivers, 49 records), the mean discharges over the 48 hours were 1,553 m³/s for the Connecticut, 147 for the Housatonic, 148 for the Thames and 13 for the Quinnipiac.
The store (schema river-v1) has a river dimension:
discharge(time, river): m³ s⁻¹,water_volume_transport_in_river_channel.temperature(time, river): °C.fill(time, river): the flags above.mouth_lon,mouth_lat,scale_factor,salinity(river).Attributes:
river_names;provenance(JSON: per river, the gauges kept, the areas, the scale factor, the outlet, and the dropped candidates counted by reason);derivation_dropped(JSON: every dropped candidate with its reason);skipped_rivers;provisional; andbuilt_utc.
A JSON sidecar next to the store repeats the request, the mouths and the derivation. GET /api/v1/rivers/download/{zarr_id}
returns the store as a zip.
Credentials
None are required. A free USGS Water Data API key (USGS_API_KEY, from
api.waterdata.usgs.gov/signup) raises the hourly rate
limit; it is sent in the X-Api-Key header. forcingkit uses the modernized APIs, not the
legacy waterservices.usgs.gov, which USGS plans to retire in 2027.
Limits
Mouths are not found automatically within a box: each river needs a mouth entry. Finding coastal outlets from NHDPlus would remove that step.
The selection rule cannot tell a regulated gauge from a natural one; use
excludewhere dam operations should not reach the forcing.Gauges measure surface runoff. Groundwater discharge (most of Long Island’s freshwater input) is not represented.
A model that applies discharge as a surface freshwater flux on fixed levels dilutes salinity but adds no volume. Whether the volume is added depends on the model’s vertical coordinate, not on this store.