---
title: "Tutorial: USBR RISE data through the Western Water Datahub EDR API"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Tutorial: USBR RISE data through the Western Water Datahub EDR API}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

This is an end-to-end tutorial on finding, retrieving, downloading, and
reusing Bureau of Reclamation RISE data through the
[Western Water Datahub](https://api.wwdh.internetofwater.app) (WWDH)
EDR interface.

It is written for USBR staff who are comfortable with computers but are
not web developers. You do not need to install anything or write any
code. Every step works by opening a URL in a normal web browser. Where
a command-line alternative is useful, a `curl` version is shown too,
but it is always optional.

Key example URLs in this tutorial were checked against the live service
on June 10, 2026. The Datahub evolves, so if an example ever surprises
you, start again from the discovery steps in sections 3 and 4 — those
are the durable part.

## 1. The ideas you need (and none you don't)

**RISE** ([data.usbr.gov](https://data.usbr.gov/)) is Reclamation's
public data catalog: reservoir storage, elevations, releases, canal
flows, weather observations, and more, organized by *location* and
*parameter*.

**EDR** (OGC API - Environmental Data Retrieval) is an open standard
for asking environmental data questions with plain URLs: "what do you
have?", "what stations are in this box?", "give me parameter X at
location Y between these dates."

**WWDH** is a server that republishes RISE (and several other federal
water datasets) through that standard. The collection you will use is
called `rise-edr`.

Why go through WWDH instead of RISE directly? Two reasons:

1. The URL patterns are a standard. Once you can query `rise-edr`, you
   can query SNOTEL snowpack, USACE reservoir, and PRISM climate
   collections on the same server with the *same* URL grammar
   (section 10).
2. Every response is available in machine-friendly formats (JSON, CSV)
   *and* a human-readable HTML page, so you can browse first and
   automate later.

### Anatomy of a request URL

Every request in this tutorial is just a URL with this shape:

``` text
https://api.wwdh.internetofwater.app / collections/rise-edr/locations/3514 ? f=json & parameter-name=3
└──────────────── base ─────────────┘ └───────────── path ──────────────┘   └───────── query ────────┘
```

- The **base** is always `https://api.wwdh.internetofwater.app`.
- The **path** says what kind of thing you want (a list of locations,
  data at one location, a data cube over an area, ...).
- The **query** (everything after `?`, with pieces separated by `&`)
  refines the request: format, dates, parameters, bounding box.

### The format switch: `f=`

Almost every URL accepts an `f` parameter that picks the response
format. This is the single most useful thing to remember:

| `f=` | What you get | When to use it |
|---|---|---|
| `html` | A readable web page, often with a map or table | Browsing, orienting yourself, sharing a link with a colleague |
| `json` | Structured data (GeoJSON or CoverageJSON) | Downloads, scripts, GIS, Excel Power Query |
| `csv` | A spreadsheet-ready table (location/feature lists) | Direct download into Excel |

If you leave `f=` off, your browser usually gets the HTML page and
tools like `curl` usually get JSON. Being explicit avoids surprises.

The HTML pages are genuinely useful — the locations page renders a
map, and the items page renders a sortable table — but they are a
*window* onto the data, not the data itself. When you want to keep or
analyze the numbers, switch the same URL to `f=json` or `f=csv`.

### If you use curl

Always wrap the URL in single quotes, because `?` and `&` mean
something to the shell:

``` sh
curl -sS 'https://api.wwdh.internetofwater.app/collections/rise-edr?f=json'
```

Add `-o filename` to save the response to a file. That is all the curl
you need for this tutorial.

## 2. Five-minute orientation in the browser

Open these three pages in order, just to look around:

1. The landing page:
   <https://api.wwdh.internetofwater.app>
2. The list of all collections on the server:
   <https://api.wwdh.internetofwater.app/collections>
3. The RISE collection page:
   <https://api.wwdh.internetofwater.app/collections/rise-edr>

The RISE collection page shows the description ("a proxy of the RISE
API into the OGC API EDR format"), the spatial extent, the list of
parameters, and links to the four query types RISE supports. For the
complete technical reference of every route and parameter, see the
[Swagger/OpenAPI page](https://api.wwdh.internetofwater.app/openapi).

Here is the live collection page, embedded from the service (this and
the other embedded views below require an internet connection;
[open in its own tab](https://api.wwdh.internetofwater.app/collections/rise-edr)):

<iframe src="https://api.wwdh.internetofwater.app/collections/rise-edr"
  width="100%" height="480" loading="lazy"
  style="border:1px solid #ccc; border-radius:4px;"
  title="RISE collection page on the Western Water Datahub"></iframe>

## 3. Discover what RISE offers

The machine-readable version of the collection page is the anchor for
everything else:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr?f=json
```

Two blocks in that document matter most.

**`data_queries`** lists the query types this collection supports. As
of June 10, 2026, `rise-edr` advertises four:

| Query type | URL path | Question it answers |
|---|---|---|
| `locations` | `/collections/rise-edr/locations` | Where are the monitoring locations? What data does one location have? |
| `items` | `/collections/rise-edr/items` | Feature catalog: searchable, filterable list of RISE locations with their attributes |
| `cube` | `/collections/rise-edr/cube` | Give me data for everything inside this bounding box |
| `area` | `/collections/rise-edr/area` | Give me data for everything inside this polygon |

Do not assume other EDR routes (like `/position` or `/radius`) work
for RISE — a route that is not advertised in `data_queries` will
return an error. Other collections on the server advertise different
sets.

**`parameter_names`** lists every data variable you can request, keyed
by RISE's numeric parameter ids. Each entry carries a human-readable
name, description, and unit.

## 4. Find the parameter ids you need

RISE parameters are identified by *numbers*, not names. The URL takes
the number; the name and unit are there so humans can pick the right
number. A few examples from the live metadata:

| Parameter id | Name | Unit |
|---|---|---|
| `3` | Lake/Reservoir Storage (daily) | af |
| `1830` | Lake/Reservoir Release - Total | cfs |
| `1834` | Lake/Reservoir Elevation | ft |
| `1811` | Precipitation | in |
| `1777` | Water Temperature | °F |

There are hundreds more. Two ways to find the one you need:

**In the browser:** open
<https://api.wwdh.internetofwater.app/collections/rise-edr> and use
your browser's find-in-page (Ctrl+F / Cmd+F) on the parameter list.

**With curl and jq:** pull just the id/name/unit triples:

``` sh
curl -sS 'https://api.wwdh.internetofwater.app/collections/rise-edr?f=json' |
  jq '.parameter_names | to_entries[] | {id: .key, name: .value.name, unit: .value.unit.symbol.value}'
```

Note that the same concept (for example "Lake/Reservoir Storage") may
appear under several ids that differ by timestep or computation
method. Read the description text before committing to an id, and
remember: **labels are for humans; ids go in URLs.**

## 5. Find locations

You will usually start from a place, not a parameter: "what does RISE
monitor near here?" There are two complementary tools.

### 5a. The locations map

Open the locations page in a browser and you get a map of RISE
locations:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/locations
```

Constrain it with a bounding box. A `bbox` is four comma-separated
numbers — **longitude and latitude, in this order**:

``` text
min_lon,min_lat,max_lon,max_lat
```

(Remember that longitudes in the western U.S. are negative, and the
*minimum* longitude is the *westernmost* edge.) For example, the area
around the lower Colorado River near Lake Mead and Lake Havasu:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/locations?bbox=-115,33,-113,37
```

The live map
([open in its own tab](https://api.wwdh.internetofwater.app/collections/rise-edr/locations)):

<iframe src="https://api.wwdh.internetofwater.app/collections/rise-edr/locations"
  width="100%" height="500" loading="lazy"
  style="border:1px solid #ccc; border-radius:4px;"
  title="RISE locations map"></iframe>

Switch the same URL to `f=json` to get the locations as a GeoJSON
`FeatureCollection` — each feature has an `id` (the RISE location id
you will use in section 6), a point geometry, and properties such as
`locationName`, `locationTypeName`, `elevation`, `timezone`, and
`projectNames`.

### 5b. The items catalog: filter, browse, download

The `/items` route serves the same locations as a *searchable feature
catalog*. In a browser it renders as a table with one row per
location:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/items?f=html&bbox=-115,33,-111,35&locationTypeName=Lake%2FReservoir&limit=20
```

That request — checked live on June 10, 2026 — returns seven
lake/reservoir locations in a Lower Colorado bounding box, including
`3515` (Lake Havasu Parker Dam and Powerplant) and several Apache Lake
sites. The live view
([open in its own tab](https://api.wwdh.internetofwater.app/collections/rise-edr/items?f=html&bbox=-115,33,-111,35&locationTypeName=Lake%2FReservoir&limit=20)):

<iframe src="https://api.wwdh.internetofwater.app/collections/rise-edr/items?f=html&bbox=-115,33,-111,35&locationTypeName=Lake%2FReservoir&limit=20"
  width="100%" height="500" loading="lazy"
  style="border:1px solid #ccc; border-radius:4px;"
  title="Filtered RISE items table: lake/reservoir locations in a Lower Colorado bbox"></iframe>

Three things are happening in that URL:

- `bbox=-115,33,-111,35` limits the spatial window.
- `locationTypeName=Lake%2FReservoir` filters on a feature property.
  The `%2F` is a URL-encoded `/` (the value is literally
  `Lake/Reservoir`; a raw slash would confuse the URL).
- `limit=20` caps the number of rows. Start generous: some filters
  are applied *after* the limit, so a small limit can hide matches
  that a larger one finds.

Which property names can you filter on? Ask the queryables route:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/queryables?f=json
```

Any property listed there (such as `locationTypeName` or
`projectNames`) can be used as a query parameter on `/items`.

**Download the catalog as a spreadsheet:** change `f=html` to `f=csv`
and the same filtered table downloads as a CSV file that opens
directly in Excel — including useful engineering attributes like Total
Capacity, Active Capacity, and vertical datum notes where RISE has
them:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/items?f=csv&bbox=-115,33,-111,35&locationTypeName=Lake%2FReservoir&limit=20
```

**Inspect one location:** every row links to a single-feature page,
e.g. Lake Mead is location `3514`:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/items/3514
```

<iframe src="https://api.wwdh.internetofwater.app/collections/rise-edr/items/3514"
  width="100%" height="450" loading="lazy"
  style="border:1px solid #ccc; border-radius:4px;"
  title="Lake Mead Hoover Dam and Powerplant feature page"></iframe>

## 6. Get data for one location

Once you know a location id and a parameter id, the data request is
one URL: `/locations/{locationId}` plus a date range and parameter.
Lake Mead (`3514`), daily storage (`3`), January 2023:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/locations/3514?f=json&datetime=2023-01-01/2023-01-31&parameter-name=3
```

Or as curl, saving to a file:

``` sh
curl -sS -o lake-mead-jan2023.json \
  'https://api.wwdh.internetofwater.app/collections/rise-edr/locations/3514?f=json&datetime=2023-01-01/2023-01-31&parameter-name=3'
```

The two query parameters:

- `datetime=` takes an ISO date, or a range with a slash:
  `2023-01-01/2023-01-31`. Some providers accept an open end:
  `2023-01-01/..`. Without `datetime`, you may get the full period of
  record — fine for some series, enormous for others. Start narrow.
- `parameter-name=` takes one or more parameter ids,
  comma-separated: `parameter-name=3,1834`.

### Reading the response (CoverageJSON)

Data queries return **CoverageJSON**, a compact format that keeps the
time axis and the values in parallel arrays:

``` json
{
  "type": "Coverage",
  "domain": {
    "domainType": "PointSeries",
    "axes": {
      "x": {"values": [-114.7]},
      "y": {"values": [36.0]},
      "t": {"values": ["2023-01-01T07:00:00Z", "2023-01-02T07:00:00Z"]}
    }
  },
  "ranges": {
    "3": {
      "type": "NdArray",
      "axisNames": ["t"],
      "values": [8730345, 8732112]
    }
  }
}
```

To turn this into a table, pair them up positionally: the first time
in `domain.axes.t.values` goes with the first number in
`ranges.{parameter}.values`, the second with the second, and so on.
`axisNames` tells you which axis(es) the values run along — for a
single station it is just `["t"]`.

| t | value of parameter 3 (af) |
|---|---|
| 2023-01-01T07:00:00Z | 8730345 |
| 2023-01-02T07:00:00Z | 8732112 |

A note on CSV here: the server advertises `f=csv` on data queries, but
in testing the EDR data routes returned empty CSV bodies. For time
series, treat `f=json` as the dependable format and convert it
yourself (section 9), or use the `/items` CSV for catalog tables.

## 7. Get data for many locations at once: `/cube`

When you want every matching series inside a bounding box, use the
`cube` route instead of looping over locations. All RISE storage
(`3`) series in a small box around Lake Havasu, for calendar 2024:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/cube?f=json&bbox=-114.3,34.1,-113.5,34.4&datetime=2024-01-01/2024-12-31&parameter-name=3
```

This returns a `CoverageCollection`: a `coverages` array in which each
entry is one location's series, with its own `domain` (the x/y
coordinates and time axis) and `ranges` (the values). The example
above returns one coverage — Lake Havasu, 365 daily storage values.

Practical guidance for `cube`:

- Keep the bbox tight and the date range short on the first try, then
  widen one thing at a time. Each cube request fans out to the
  upstream RISE API, so big requests are slow and can time out.
- If a broad cube fails, fall back to the discovery pattern: find
  candidate ids with `/items` (section 5b), then request each one via
  `/locations/{id}` (section 6).

## 8. Get data inside a polygon: `/area`

If your area of interest is a watershed or management boundary rather
than a box, the `area` route accepts a polygon in WKT (well-known
text) via `coords=`. A readable WKT polygon:

``` text
POLYGON((-115 35,-113 35,-113 37,-115 37,-115 35))
```

URLs cannot contain spaces, so each space becomes `%20`:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/area?f=json&coords=POLYGON((-115%2035,-113%2035,-113%2037,-115%2037,-115%2035))&datetime=2024-01-01/2024-01-31&parameter-name=3
```

Two rules save most of the grief here: the polygon ring must close
(first coordinate repeated last), and every space must be encoded. For
complex boundaries, simplify the polygon first or use a tool that
encodes URLs for you — very long URLs can exceed browser and server
limits. If a box is close enough, prefer `cube`; it is simpler and
faster.

## 9. Downloading and using the data

You now know how to build the URLs. Getting the results into your
tools:

**Browser:** open the `f=json` URL and use Save Page As (Ctrl+S /
Cmd+S), or right-click a link and Save Link As. The `/items?f=csv`
URLs download directly as spreadsheet files.

**curl:** `curl -sS -o output.json '<URL>'`.

**Excel:** open `/items` CSV files directly. For JSON time series,
Data → Get Data → From Web in Excel's Power Query accepts the
`f=json` URL and can unpivot `domain.axes.t.values` against
`ranges.{id}.values`, though this takes some Power Query comfort —
many people find it easier to ask a script-writing colleague (or an AI
assistant) for a five-line converter from CoverageJSON to CSV.

**GIS:** QGIS and ArcGIS read GeoJSON natively. In QGIS, Layer → Add
Layer → Add Vector Layer, choose Protocol: HTTP, and paste a
`/locations?f=json` or `/items?f=json` URL to map RISE locations
directly — no download step needed.

**R:** this package (`edr4r`) wraps all of these routes;
`edr_locations()`, `edr_cube()`, and `covjson_to_tibble()` reproduce
this tutorial in a few lines. See the companion vignette,
*Western Water Datahub collection field guide*.

**Python:** `requests` plus the `covjson-pydantic` or plain
`json` module; the axis/range pairing logic in section 6 is a
five-line loop.

## 10. Interoperating beyond RISE

Everything above transfers. The same server hosts sibling collections
that answer to identical URL grammar — only the collection id, the
parameter ids, and the advertised query types change:

| Collection id | Source | Example use |
|---|---|---|
| `snotel-edr` | USDA NRCS SNOTEL | Snow water equivalent above your reservoir |
| `awdb-forecasts-edr` | USDA AWDB | Seasonal water supply forecasts |
| `usace-edr` | USACE Access2Water | Corps reservoir storage and outflow |
| `usgs-prism` | PRISM | Monthly precipitation and temperature grids |

Check each collection's `data_queries` before assuming a route exists
(for example, `usgs-prism` supports `position` and `cube`, not
`locations`).

### Translating concepts across agencies: `parameterGroups`

Parameter ids differ across agencies: RISE storage is `3`, USACE
storage is `Flood Storage` or `Conservation Storage`. WWDH publishes a
crosswalk in the top-level collections document:

``` text
https://api.wwdh.internetofwater.app/collections?f=json
```

Its `parameterGroups` array maps a shared concept to each collection's
ids. As checked on June 4, 2026, the `Lake/Reservoir Storage` group
included:

``` json
{
  "name": "Lake/Reservoir Storage",
  "members": {
    "rise-edr": ["1470", "56", "51", "47", "3"],
    "usace-edr": ["Flood Storage", "Conservation Storage",
                  "Percent Flood Pool", "Percent Conservation Pool"]
  }
}
```

So a cross-agency comparison of 2024 storage near Parker Dam is two
parallel cube requests over the same bbox:

``` text
https://api.wwdh.internetofwater.app/collections/rise-edr/cube?f=json&bbox=-114.3,34.1,-113.5,34.4&datetime=2024-01-01/2024-12-31&parameter-name=3

https://api.wwdh.internetofwater.app/collections/usace-edr/cube?f=json&bbox=-114.3,34.1,-113.5,34.4&datetime=2024-01-01/2024-12-31&parameter-name=Flood%20Storage
```

(The `%20` encodes the space in `Flood Storage` — USACE uses worded
ids.) When verified, the first returned Lake Havasu's 365 daily RISE
storage values and the second returned Alamo Dam's Corps storage
record for the same year. Expect timestep differences across agencies:
RISE returned daily values; the USACE series was irregular. Aligning
them is an analysis step, not an API option.

## 11. The whole workflow on one page

``` text
1. ORIENT      /collections/rise-edr                  (browser, f=html)
2. PARAMETERS  /collections/rise-edr?f=json           -> parameter_names: pick exact ids
3. LOCATIONS   /collections/rise-edr/items?f=html&bbox=...&{queryable}=...&limit=...
               -> browse table; switch to f=csv to download; note location ids
4. ONE SERIES  /collections/rise-edr/locations/{id}?f=json&datetime=...&parameter-name=...
5. MANY SERIES /collections/rise-edr/cube?f=json&bbox=...&datetime=...&parameter-name=...
6. CROSSWALK   /collections?f=json -> parameterGroups, then repeat 3-5 on the sibling collection
```

Start small — one location, one parameter, one month. Widen one
dimension at a time once the small request works.

## 12. Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| 404 on a query route | Route not advertised for this collection | Re-check `data_queries` in `/collections/rise-edr?f=json` |
| 400 on `/area` | WKT not URL-encoded or ring not closed | Encode spaces as `%20`; repeat the first coordinate at the end |
| 204 / empty body | Valid request, but nothing matched | Try a different parameter id, location, or a shorter date range |
| Empty CSV from a data query | CSV not populated for EDR data routes | Use `f=json` for time series; `f=csv` works on `/items` |
| 500 or very slow on `/cube` | Upstream RISE could not satisfy a broad query | Shrink bbox, shorten dates, request one parameter |
| Empty `features` on `/items` | Filter too narrow, or `limit` too small | Raise `limit`, drop filters one at a time, check the `f=html` view |
| Wrong or empty data for a parameter | Parameter id not collected at that location | Open the location in the items table; try a sibling id from section 4 |
| Shell error before any response | URL not quoted in the terminal | Wrap the whole URL in single quotes |
| Numbers but no meaning | Units unknown | Look up the id under `parameter_names` — units are in the metadata |

If you get stuck on what a route accepts, the
[OpenAPI page](https://api.wwdh.internetofwater.app/openapi) documents
every route and query parameter interactively, and every JSON document
on the server contains a `links` section pointing to its related
pages — when in doubt, follow the links.
