Getting started
What is this?
An embeddable panel showing live river conditions — streamflow, gauge height, a six-hour trend and a chart — from any of about 10,600 active USGS stream gauges in the United States. You pick your rivers, style it, and paste one snippet.
Do I need an account or an API key?
No. No sign-up, no email, no key, no quota. The widget reads USGS from your visitor’s browser, and USGS allows that anonymously.
Where do I paste it?
Anywhere that accepts HTML: an Elementor HTML widget, WordPress’s Custom HTML block,
a Squarespace code block, a Shopify theme file, or straight into a hand-written page.
If your platform strips <script>, use the iframe embed instead.
My old USGS graphs stopped working. Is that related?
Almost certainly. USGS switched off the old nwisweb/graph images, so
thousands of pages that embedded them now show 404s or blanks. Those were static image
tags with nowhere to push a fix.
The larger deadline is still ahead. waterservices.usgs.gov — the
legacy NWIS service behind a great many river tools — still works today, but USGS
has said it will be decommissioned in early 2027. Anything built on it
has a date on it. This widget already runs on the current
api.waterdata.usgs.gov, and because the script is served centrally, future
API changes can be handled without you re-pasting anything.
Script or iframe
Which should I use?
The script embed, almost always. Use the iframe only when your platform will not run a script, or when it only accepts a URL.
- Script embed
- Inherits your site’s typeface, resizes with whatever container it sits in, needs no fixed height, and is one small file shared by every widget on the page.
- iframe embed
- Works where scripts are blocked — Wix, free-tier Squarespace, Notion, Substack, locked-down intranets.
What do I give up with an iframe?
- Height is fixed and guessed. An iframe cannot size itself to its content, so the generated snippet estimates a height from your settings. Change the chart or add a gauge and you may need to adjust it.
- It cannot inherit your fonts. Styles do not cross the frame boundary, so it falls back to a system typeface.
- It is a second page load. Slightly slower, and one more request.
- It is invisible to your page. Nothing outside can style or measure it.
None of this is fatal — the iframe renders exactly the same card. It is simply a worse fit when a script is an option.
I pasted it and nothing appears.
- Check the page is not in a builder preview that blocks scripts. Save and view the live page.
- Check your platform did not strip the
<script>tag — view source and look for it. If it is gone, switch to the iframe embed. - Open the browser console. Without JavaScript the embed stays a working link to the USGS station page, which is the intended fallback rather than a failure.
Layout, grids and multiple widgets
Can I put several on one page?
Yes, as many as you like, each with its own gauges and styling — a hero river above the fold, a compact grid below it, one at the foot of a blog post. The loader is id-guarded, so pasting the snippet alongside every embed still loads the script exactly once. See a page doing all four.
One embed with six gauges, or six embeds?
Whichever suits the layout. One embed with several gauges lays them out in a responsive grid together; separate embeds let your own page decide where each sits.
One thing does differ, and it matters on a big page: an embed costs the same whether it holds one gauge or six. The readings for every gauge in an embed are fetched in a single request, so six rivers in one embed is one-sixth the traffic of six separate embeds. On a page with many gauges that is the difference between comfortably inside the USGS request allowance and repeatedly over it — see choosing a data source.
Why only six gauges in one embed?
Two reasons, and there is no limit on how many embeds a page can carry — so this caps the grid, not the page.
The first is that a row of more than six cards stops being scannable. The second is the reason six costs what one costs: every gauge in an embed is fetched together in a single request, and that request has a size limit — past roughly eight gauges a 14-day hydrograph would begin losing points to it. Six leaves room.
For more rivers, add a second embed. Two embeds of six is still only two requests, which is far cheaper than twelve separate ones — see choosing a data source.
What changes when one embed shows several gauges?
The short version: each card shows what its own gauge actually publishes. Gauges are not alike, and the grid does not pretend they are.
Readings that a gauge does not publish are left out, not blanked. If one river has a thermometer and the next does not, the first card shows water temperature and the second simply does not have that line. You will not see a dash or an empty slot, because a dash reads as a sensor that broke rather than one that was never fitted. It does mean cards in a grid can carry different numbers of readings and so differ slightly in height.
The 6-hour trend behaves the same way. It appears on the cards that have enough recent data for it and is quietly absent on the ones that do not.
A gauge that publishes none of what you asked for says so. Roughly one live USGS gauge in five reports gauge height but no streamflow, so a flow-only grid will meet one eventually. That card reads “This gauge does not report streamflow. It publishes gauge height.” rather than sitting there empty, and links to the station so you can check.
The readings you tick apply to the whole embed, never to one river. This is the part worth being clear about: the checkboxes choose what the embed asks for, and each card then shows whichever of those its own gauge publishes. Tick water temperature with four rivers selected and it appears on the ones with a thermometer and is absent on the rest. There is no way to show streamflow on one river and temperature on another inside a single embed — for that, give each river its own embed.
Water temperature is only switched off when none of the selected gauges reports it. Add a second river and the builder ticks everything the selection can show, on the grounds that a grid where one card silently lacks a line the others have is the confusing outcome. Untick anything you do not want.
The label applies to one gauge only. A custom label names a single river, so with several selected each card falls back to its own USGS station name. The builder disables the field and says so.
Everything else is shared. Theme, colors, chart style, units, columns and the footer settings apply to every card in the embed. If you need two rivers styled differently, use two embeds.
Is it responsive?
It sizes to its container rather than to the viewport, so the same embed works in a full-width band, a 300px sidebar or a phone without a media query on your end.
That distinction matters more than it sounds. Your theme almost certainly has breakpoints of its own, and what they change is how wide the column around the widget is. Because the widget watches that column rather than the window, it fits whatever your theme decides without the two sets of breakpoints arguing.
The same is true of the column setting: choosing three is a maximum, not a fixed grid. Cards narrow to fill the room available and drop to two, then one, as that room shrinks — in a sidebar on a wide screen exactly as on a phone.
Where the data comes from
Who serves the numbers?
The U.S. Geological Survey, read directly by your visitor’s browser from
api.waterdata.usgs.gov. We are not in the middle of it.
How fresh is it, and will it match the USGS site?
It matches exactly, and that is the reason for reading direct. USGS publishes on a roughly 15-minute cycle, and the widget shows whatever the API is serving at the moment the page loads. An angler who checks your page and then checks waterdata.usgs.gov or a river app sees the same number.
A cached middle layer would have been cheaper for us and would have made your page the one that looked wrong.
Does it update while the page is open?
Every 15 minutes by default, matching the publish cycle. Refreshing faster returns the same number twice. A visitor who reads and leaves makes one request.
What does “provisional” mean?
Real-time readings have not yet been reviewed and approved by USGS, and may be revised later. USGS requires that notice to travel with the data, so every card carries it and links to the source station. It is not a sign anything is wrong.
Can it show non-USGS gauges?
Not yet. Some rivers are gauged by dam operators, state agencies or NOAA’s river forecast centres, each with a different API. Only USGS stream gauges are supported today.
What you see in DevTools
Which requests does the widget make?
Open the Network tab and reload. On a page with one widget you will see five requests, and only two of them touch our domain:
| Request | Host | What it is |
|---|---|---|
/v1/widget.js | cdn.riverwidget.com | The script. About 19 KB, loaded once however many widgets are on the page. |
latest-continuous/items | api.waterdata.usgs.gov | The current reading — streamflow, gauge height, temperature. |
continuous/items | api.waterdata.usgs.gov | The last six hours, which the rising/falling arrow is calculated from. |
daily/items | api.waterdata.usgs.gov | Daily means for the chart. A hydrograph reads continuous instead. |
/api/v1/beacon | cdn.riverwidget.com | A 1×1 GIF that counts the render. data-beacon="0" removes it. |
The three USGS requests are batched across every gauge on the page. Eight rivers still cost three requests, not twenty-four.
Can I verify the number is real?
Yes, and this is the point of reading direct. Click the
latest-continuous request, open the response, and find
properties.value — that is the number rendered on the page, unmodified.
Compare it with the same gauge on
waterdata.usgs.gov
and it will match, because nothing sits between the two.
What about cookies and storage?
Application → Cookies, Local Storage, Session Storage and IndexedDB will all be empty for our domain. The widget writes nothing to the visitor's device. That is why embedding it adds nothing to your consent banner.
Does the widget need your API to work?
No. The readings come from USGS directly, so the only thing our domain provides is the
script file. Host widget.js yourself and everything renders with no request
to us at all — the counting pixel and the cached fallback simply do not happen.
See what happens if we disappear.
Should the readings come from USGS or from you?
Both are supported and the default is USGS. The honest version of this is a trade-off rather than a right answer, so here is each side.
Direct from USGS — the default, data-source="usgs". The
visitor’s browser fetches the readings itself.
- The number is whatever USGS is serving that minute — it matches waterdata.usgs.gov exactly.
- Your page does not depend on us being up for its numbers. If we vanished, the embeds keep reading USGS until USGS changes something.
- But USGS allows 1,000 requests per hour per IP address, and each embed costs up to three. A page with many separate embeds can exhaust a visitor’s allowance — most often your own, while building the page, or a visitor on a shared office or mobile-carrier address.
Through our API — data-source="proxy". We fetch from USGS
and the browser reads our cached copy.
- Zero USGS requests from the visitor’s machine, so no rate limit can reach them however many gauges the page shows.
- We read with an authenticated key and cache at the edge, so every visitor to every site shares one upstream request per gauge per 15 minutes. Cost scales with how many distinct gauges exist, not with your traffic.
- But readings can be up to 15 minutes behind USGS, and your numbers now depend on our service running. The embed still degrades to a working link to the station if we are unreachable, but it degrades to a link, not to numbers.
Which to pick. Leave it on the default unless a single page carries many separately-placed gauges — a river, a paragraph of copy, the next river. That layout is where the request count multiplies, and proxy mode is the better answer for it. One embed holding up to six gauges costs the same as one holding one, so combining them is usually the cheaper fix than switching.
You are not locked in either way: it is one attribute, and changing it changes nothing else about the card.
When does a request to your API appear?
Only when USGS cannot be reached. You would then see a fourth request to
cdn.riverwidget.com/api/v1/gauge, serving a cached copy up to fifteen
minutes old, and a console message beginning [River Widget] explaining
why. In normal operation that request does not happen at all.
How would I describe this to a client?
“It reads the government gauge data live, in the visitor's browser, straight from the source. We are not in the middle of it, so the number on your page is the same number the USGS website shows.”
For a developer: three batched requests to the USGS OGC API from the client, no proxy, no cache in between, no cookies, no third-party analytics. The only calls to our domain are the script itself and a counting pixel.
When a reading is missing
The card shows gauge height but no streamflow.
That gauge does not publish discharge. Plenty of stations measure stage only — the Bogachiel in Washington is one. Ask a gauge for a reading it does not carry and the field simply does not appear. The builder warns you about this when you pick your metrics.
Why is water temperature greyed out?
Only about one gauge in six carries a thermometer. The builder disables the option when your chosen gauge is not one of them.
It says the gauge is not reporting.
Many gauges, especially in the West, are pulled for the winter and returned in spring. Others go offline for maintenance or after high water damages equipment. These stay searchable and are labelled “quiet” in the builder rather than hidden — knowing a river has a gauge that is currently down is worth something.
It says “delayed”.
The last reading is more than three hours old. Usually a telemetry problem at the station rather than anything to do with your page.
Styling
How much can I change?
Seven colors independently — accent, numerals, body text, labels, card background, border, and hydrograph gridlines — plus corner radius, drop shadow, density, typeface and units. Start from a theme and override what clashes.
Can I remove the card entirely?
Yes. Turn off the background and border and the readings sit straight on your own
section. The bare theme goes further and inherits your colors.
Sparkline or hydrograph?
A sparkline is a bare trend line with no axes; it reads at a glance in a sidebar. A hydrograph is the chart anglers already know from USGS — a real axis in the gauge’s units, day labels, and the full 15-minute record rather than daily averages. Gridlines on it are optional; axis numbers stay either way.
Which timezone are times shown in?
The river’s, not the visitor’s. Someone planning a trip from Denver wants
lodge time. The builder bakes the gauge’s timezone into the embed;
data-tz overrides it.
Privacy
Does it set cookies?
No. No cookies, no local storage, no fingerprinting, no visitor identifier of any kind, and no third-party analytics. Embedding it adds nothing to your consent banner.
What do you count, then?
A single request per render tells us the embedding domain, the theme, which gauges and a
coarse country from Cloudflare. Never an IP address, a user agent, a session, or anything
about the page beyond its domain. data-beacon="0" switches it off.
Do you publish who uses it?
No. Only aggregate totals — the install count on the homepage. Which sites embed it is our customers’ business.
Licensing and the footer
Is it really free, including for client work?
Yes. Personal or commercial, on your own site or one you built for a client, and you may charge for the build. No sign-up and no quota.
Who owns the data?
Nobody. USGS data is a work of the U.S. Government and sits in the U.S. public domain, free to use, adapt and redistribute without permission. USGS asks to be credited as the source, which every card does and which is not removable.
And the code?
MIT. Use it, fork it, host your own copy. Machine-readable license.
What can I change in the footer?
The line under each reading has six parts, and the Footer panel in the builder controls most of them. It is worth being straight about which ones, and why the rest are not on the list.
Yours to turn off: the station ID, the “Station ID:” label, the timezone abbreviation, and the River Widget badge.
Yours to shorten: the reading time, from
Sep 10, 8:04 AM down to 9/10/2026 - 8:04am or
9/10/26 - 8a; and the provisional notice, from the full sentence to the
single word Provisional.
Not optional: that there is a reading time, that the reading is marked provisional, and the USGS credit. Also the “Delayed” and “Cached data” flags, which appear only when they are true.
Why can’t I remove the date or the provisional notice?
They are fixed for two different reasons, and only one of them is about rules.
The provisional notice is the rule. USGS publishes real-time readings before anyone has reviewed them, and revises them afterwards — sometimes substantially, after a flood or an equipment fault. They ask that the disclaimer travel with the numbers, and that is a condition of the data rather than a preference of ours. So it shortens, but it does not disappear.
The reading time is about what the number is for. A flow figure with no time beside it looks equally confident whether it arrived four minutes ago or last Tuesday. If a gauge goes offline mid-season, or the widget is showing a cached copy because the visitor’s network blocked USGS, the timestamp is the only thing on the card that says so. People read these numbers to decide whether to wade a river, launch a boat, or run a trip — USGS’s own guidance singles out decisions “that concern personal or public safety”. A stale number that looks current is worse than no number.
If the footer feels heavy, shorten it rather than removing it. 9/10/26 - 8a
and Provisional together are about a third of the default, and they still tell
a reader everything they need.
How do I know when something changes?
The changelog lists every release and, more usefully, says whether you need to do anything about it.
Most releases reach you on their own. The widget is served from one rolling URL, so a fix is live on every embed the next time a visitor loads the page — you never re-paste anything.
The exception is a release that adds a new option. Code can reach your embeds; markup
cannot. A new data- attribute does nothing until you add it, and your
existing embeds carry on unchanged in the meantime. Those releases are flagged
separately, and each new option says what it does, what it costs you and who it suits
— so you can tell at a glance whether it is worth the edit.
Can I remove your branding?
Yes. Add data-badge="0" to the embed and the small
“River Widget” link disappears, along with the one line it writes to the
browser console.
We would rather you left it, and it is worth saying why plainly. There is no advertising behind this and no sales team; the badge is how anyone finds it. A widget nobody can trace back to its source is a widget that eventually stops being maintained, which is precisely the thing that broke everyone’s river graphs in the first place.
But it is a request, not a condition. The code is MIT, so you could just as well take a copy and delete the badge yourself, and that would be entirely within your rights. We would rather ask you honestly than pretend to a restriction we have not reserved.
The USGS credit on each card is a different matter and is not removable. That one is a condition of using their data, not ours.
Technical
Which browsers?
Modern browsers get full style isolation via Shadow DOM. Older ones, back to IE11, get a scoped inline render. With JavaScript off the embed stays a working link to the USGS station page.
Will it slow my page down?
The script is about 19 KB compressed, loads asynchronously after your content, and has no dependencies. A card shows a skeleton until its numbers arrive, so nothing jumps.
What happens if you disappear?
Being straight about this, because it is the question that matters most and it is easy to answer too generously.
The script is served from our domain. If that domain went away — lapsed registration, closed account, deleted service — the script would stop loading and no widget would render at all. Not stale numbers: no numbers.
What remains is the embed itself, which is a plain link. Every snippet is an anchor pointing at that gauge's USGS station page, with the river's name as its text. So a page would show a working link where the panel used to be, rather than a broken box or an error. That is deliberate — it is the same fallback that covers a visitor with JavaScript disabled — but it is a graceful failure, not a working widget.
If that dependency is unacceptable for a site, remove it: the whole project is MIT
licensed and widget.js is a single file with no dependencies. Host it
yourself and the embed keeps working with no reference to us at all — the readings
come from USGS either way, and the widget does not need our API to render them. You lose
only the automatic fixes when USGS changes something, which is the thing that broke
everyone else's river widgets in the first place.
What if USGS goes down?
The widget falls back to our cached copy, up to fifteen minutes behind, which beats a blank card. If that is also unreachable, the embed stays a link to the station page.
Can I host it myself?
Yes — the whole project is MIT and deploys to Cloudflare Pages. Point
data-river-api at your own origin.
Something is broken
Widget not showing data? Start with the troubleshooting guide — it includes a one-click check of whether USGS is up.
A widget stopped showing numbers. What now?
Work through the troubleshooting guide. It splits on the question that matters first — is one widget affected or all of them — because the answers are entirely different. One is nearly always that gauge; all of them is the data source, the script, or the page.
It includes a button that makes a single live request to a USGS gauge when you press it, so you can tell in one click whether the data source is up before doing anything else.
Where do I find the error?
Open your browser’s developer tools, go to the Console, reload the page, and search
for River Widget. Everything the widget reports is prefixed that way.
For a fuller picture, type RiverWidget.diagnose() and press enter: it prints
the version, every embed on the page, what each one asked for, and the recent errors.
How do I report a bug or ask for a feature?
Through the support repository, which holds the issue tracker, release notes and
reporting instructions rather than code. Include the page URL, what you expected, what
you saw, and the RiverWidget.diagnose() output.
Open an issue, or see
the troubleshooting guide.