Central Valley Surfacewater Map

Public API

Read-only files of what the map shows: 924 measured series of canals, rivers, turnouts, reservoirs and the Delta at daily, weekly, monthly and water-year steps, the system ledgers, the tributary flow requirement verdicts and the data notes. Static JSON and CSV in a public bucket: no key, no sign-up, no rate limit of ours.

Base URL https://storage.googleapis.com/surfacewater-map-509303/api/v1/
Data 2000-10-01 to 2026-10-24, built 2026-10-30 17:18. Licensed CC BY-NC 4.0.

Endpoints

Measured, estimated, not publicly measured

Conventions

Versioning

Releases

Each deploy is a release, named by its time (UTC), and the site is frozen at snapshots/<release>/, the API files with it. A frozen copy never changes, so a number quoted from it can be checked later. The monthly data refresh is the monthly data release. /api/v1/release.json names the release you are reading and its frozen copy (api_snapshot). The per-series files are frozen only when they change (about once a month, with the data refresh), so a release's series files can sit in an earlier release's copy: series_snapshot says where. /api/v1/releases.json lists them all.

Use from code

import pandas as pd
sha_storage = pd.read_csv("https://storage.googleapis.com/surfacewater-map-509303/api/v1/series/SHA.storage.csv")
monthly = sha_storage[sha_storage.step == "month"]

Files are served gzip-compressed to clients that ask for it (curl --compressed; browsers and pandas do). Browser pages on other sites can fetch them (CORS: GET from any origin).

Licence and citation

The series, ledgers, verdicts and notes as compiled here are licensed CC BY-NC 4.0. Additional permission: public agencies, groundwater sustainability agencies, consultants working for them, and academic researchers may also use these outputs for commercial purposes, with the same credit. Other commercial use needs the author's written permission. Releases up to 2026-10-29T0510 remain under CC BY 4.0. Cite the release you used:

Gray, W. (2026). Central Valley Surfacewater Map, release <release>. CC BY-NC 4.0.

The source data keep their own terms: DWR (CDEC, Dayflow, Water Data Library, SWP reports, Bulletin 132); Reclamation (CVO reports); USGS (NWIS); U.S. Army Corps of Engineers (CWMS); State Water Resources Control Board; FERC; NMFS / USFWS (biological opinions). sources.json lists every source series.

The map’s line geometry (data/network.geojson, not part of this API) is built from OpenStreetMap ways and USGS NHD and is offered under the Open Database License 1.0, © OpenStreetMap contributors, not CC BY-NC.

Cost and limits

The files sit in a Google Cloud Storage bucket (us-west1), inside its Always Free tier (5 GB-months of storage, 100 GB a month of transfer to North America, 50,000 reads a month); beyond it about $0.02 per GB-month stored, $0.12 per GB transferred and $0.0004 per 1,000 reads (prices read 2026-10-21 from cloud.google.com/storage/pricing). There is no rate limit of ours, but please take the bulk you need once rather than polling: the data change about once a month.

GET /api/v1/index.jsonJSON

This list as JSON: every v1 endpoint, the licence, the data period and every series.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/index.json"
FieldType, unitsMeaning
versionstring"v1".
licencestring"CC BY-NC 4.0".
licence_urlstringURL of the licence.
licence_notestringThe additional permission for public agencies, GSAs, their consultants and academic researchers, and the earlier releases' licence.
citestringCitation line without a release id; release.json has the line with it.
docsstringURL of the docs page.
data.startYYYY-MM-DDFirst day of the record.
data.endYYYY-MM-DDLast day with data.
data.builtYYYY-MM-DD HH:MMWhen the map's data were built (Pacific time).
quality_codesobject1 = measured, 2 = estimated (an approximation flagged ~ or ? in the map's network, or a stand-in series), 3 = monthly record (a published monthly table, measured); null = no record (never zero).
endpoints[]objectname, path, summary, example, format and fields (the frozen names).
series[]objectEvery series, with the fields listed under series.csv.
countsobjectNumber of series by status.

GET /api/v1/release.jsonJSON

The release these files are, where its frozen copy is and how to cite it.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/release.json"
FieldType, unitsMeaning
releasestringThe deploy this copy came with (UTC time, e.g. 2026-11-06T1530).
api_releasestring | nullThe release whose frozen copy holds these API files except series/ (the same as release; null when the live API has no frozen copy).
api_snapshotstring | nullURL of that frozen copy's api/v1/.
series_releasestring | nullThe release whose frozen copy holds these exact series/ files: series files are frozen only when they change, so it can be an earlier release.
series_snapshotstring | nullURL of that frozen copy's api/v1/series/ (in a frozen copy, index.json's series file paths are relative to it).
builtYYYY-MM-DD HH:MMWhen the data were built.
data_endYYYY-MM-DDLast day with data.
citestringCitation line with api_release.

Written at deploy time. A local build carries the last deploy's record.

A frozen copy (snapshots/<release>/api/v1/) holds every file but series/ when its series files are the same as an earlier release's; series_snapshot says where they are.

GET /api/v1/releases.jsonJSON

Every release of the site and where its frozen copy is.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/releases.json"
FieldType, unitsMeaning
[].releasestringRelease id.
[].builtstringWhen its data were built.
[].data_startYYYY-MM-DDFirst day of its record.
[].data_endYYYY-MM-DDLast day with data.
[].deployedstringWhen it went live (UTC).
[].pathstringIts frozen site: snapshots/<release>/index.html.
[].apistring | nullIts frozen API: snapshots/<release>/api/v1/; null before the API existed.
[].seriesstring | nullWhere its series files are frozen: snapshots/<release>/api/v1/series/, an earlier release's when the series did not change; null before the API existed.

GET /api/v1/series.csvCSV

Every series as one table, one row each (the catalog of index.json).

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/series.csv"
ColumnType, unitsMeaning
idstringSeries id: a map line (e.g. swp_nba), a reservoir role (SHA.storage, SHA.inflow, SHA.release) or a Delta term (DELTA.inflow.Yolo_Bypass).
namestringName as on the map.
groupstringline, reservoir or delta.
kindstringcanal, river, turnout, checkpoint, delivery, tunnel, pump; reservoir_storage, reservoir_inflow, reservoir_release; delta_inflow, delta_exports, delta_use, delta_outflow, delta_channels.
systemstringSWP, CVP, Friant, Delta, River or Local.
mapstringdelivery (the Delivery Map) or tributaries (drawn on the Tributaries Map only).
unitstringcfs (a flow: the mean over the period) or af (reservoir storage: the last day of the period).
aggregationstringmean or end_of_period.
statusstringmeasured, not publicly measured (drawn on the map, no published measurement; no value files) or no record (a published source exists but holds no value in the period).
approximationboolSome values come from an expression flagged ~ or ?: those values carry quality 2 (estimated).
daily_recordstringHow a day's value is made: published series ids added or subtracted, tried in order ("|" = else). Empty when there is none.
monthly_recordstringThe published monthly table a month's value comes from where there is no daily record. Empty when there is none.
sourcesstring[]Ids of the published source series used (see sources.json).
data_notesstring[]Ids of the data notes about this series (see notes.json).
steps.<step>objectFor each step with a value (day, week, month, wy): first, last (periods) and n (values).
filesobjectjson and csv: paths of the series' files, relative to api/v1/. Absent when the status is not measured.
notestring | nullThe map's note on the line.

Lists are joined with spaces; steps.<step> becomes <step>_first, <step>_last and <step>_n columns; files becomes json and csv columns.

GET /api/v1/series/<id>.jsonJSON

One series at every step: daily, weekly, monthly and water-year values with a quality per value.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/series/SHA.storage.json"
FieldType, unitsMeaning
idstringSeries id.
namestringName.
unitstringcfs or af.
aggregationstringmean or end_of_period.
quality_codesobject1 = measured, 2 = estimated (an approximation flagged ~ or ? in the map's network, or a stand-in series), 3 = monthly record (a published monthly table, measured); null = no record (never zero).
steps.day.startYYYY-MM-DDFirst day; values run one per day from it.
steps.<step>.periodsstring[]week: the Monday the week starts (YYYY-MM-DD); month: YYYY-MM; wy: WYyyyy (October of the year before to September).
steps.<step>.valuesnumber | null []One per day or period; null = no record.
steps.<step>.qualityint | null []One per value, codes as in quality_codes.
licencestring"CC BY-NC 4.0".
citestringCitation line (see release.json for the release id).

A week, month or water year has a value only when enough days have one (weekly 5 of 7, monthly 20 days, water year 330 days for flows; storage needs a reading in the last week). A monthly-only delivery line has month and water-year values only.

Values are rounded: daily and weekly flows to 1 cfs, monthly means to 0.01 cfs and water-year means to 0.001 cfs, so a month's or a year's mean times its days and 1.98347 gives its acre-feet to the whole acre-foot. Storage is acre-feet: daily to 1, monthly and water-year to 0.1.

GET /api/v1/series/<id>.csvCSV

One series as a long table: every step, one row per period with a value.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/series/swp_nba.csv"
ColumnType, unitsMeaning
stepstringday, week, month or wy.
periodstringAs in the JSON: the day (YYYY-MM-DD), the Monday a week starts (YYYY-MM-DD), the month (YYYY-MM) or the water year (WYyyyy, October of the year before to September).
valuenumberThe value; rows without one are left out.
unitstringcfs or af.
qualitystringmeasured, estimated or monthly record.

GET /api/v1/sources.jsonJSON

The published series the map is built from: gauges, plants and report tables.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/sources.json"
FieldType, unitsMeaning
[].series_idstringSource series id, as used in daily_record.
[].sourcestringcdec, cdec_h, usgs, dayflow, cvo_dout, swpops, wdl, cwms, ... (agency and system).
[].stationstringStation or site code at that source.
[].paramstringSensor or parameter code.
[].kindstringflow or storage.
[].namestringName.
[].notesstringNotes on the series.

GET /api/v1/accounting/index.jsonJSON

The system ledgers: which systems have one.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/accounting/index.json"
FieldType, unitsMeaning
[].systemstringswp, cvp, friant, sac: the <system> of the ledger paths.
[].namestringName.
[].shortstringShort name.
[].estimatesboolWhether estimates/ holds loss estimates for it.

GET /api/v1/accounting/<system>.jsonJSON

A system's annual ledger: per reach and water or calendar year, the measured terms and the unmeasured gain / loss.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/accounting/swp.json"
FieldType, unitsMeaning
systemstringSystem id.
namestringName.
unitstring"af" (acre-feet per period).
sourcestringWhere the numbers come from.
incompletestringWhat the record lacks.
reaches[]objectid, name, note, gaps and the other descriptions of each reach.
annual[].reachstringReach id.
annual[].periodstringWYyyyy or CYyyyy.
annual[].basisstringdaily, monthly or b132 (DWR Bulletin 132).
annual[].headaf | nullFlow measured into the reach at its head.
annual[].inflowaf | nullMeasured inflows along the reach.
annual[].deliveryaf | nullMeasured deliveries (turnouts, diversions).
annual[].branchaf | nullMeasured branch and spill outflows.
annual[].storage_changeaf | nullChange in the storage of reservoirs or pools on the reach.
annual[].tailaf | nullFlow measured at the reach's end.
annual[].residual_totalaf | nullUnmeasured gain (+) or loss (−): what the measured terms leave unexplained (evaporation, seepage, unlisted users, ungauged inflow, measurement error). null when a term is missing.
annual[].operator_lossaf | nullThe operator's own booked losses, as printed, for comparison (negative = loss).
annual[].qualitystringmeasured, estimated (a term is an approximation), incomplete (a term is missing) or suspect.
annual[].subs[]objectSub-reaches: from, to, residual (af).

Rows carry only the terms that apply to the reach; other measured terms (inflow_unlisted, delivery_federal, residual_cdec, points_dwr, combined) are described in the reach notes.

Nothing in a ledger is modelled. Estimated loss ranges are in estimates/.

GET /api/v1/accounting/<system>.csvCSV

The annual ledger as a table, one row per reach and period (sub-reaches left out).

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/accounting/cvp.csv"
ColumnType, unitsMeaning
systemstringSystem id.
reachstringReach id.
reach_namestringReach name.
periodstringWYyyyy or CYyyyy.
basisstringAs in the JSON.
headafAs in the JSON; blank = no record or does not apply.
inflowaf
deliveryaf
branchaf
storage_changeaf
tailaf
residual_totalaf
operator_lossaf
qualitystring

Further columns follow for the terms only some reaches have (inflow_unlisted, delivery_federal, residual_cdec, points_dwr, combined, combined_quality, note).

GET /api/v1/accounting/<system>_monthly.jsonJSON

A system's ledger by month: the same terms per reach and month.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/accounting/friant_monthly.json"
FieldType, unitsMeaning
systemstringSystem id.
unitstring"af".
monthsYYYY-MM[]Months of every list below.
reaches.<reach>.v.<term>af | null []head, inflow, delivery, branch, storage_change, tail and gain (the unmeasured gain (+) or loss (−)), per month.
reaches.<reach>.qstring[]Quality per month: measured, estimated, incomplete.
annual_extraobjectAnnual-only figures some reaches have (af).

GET /api/v1/accounting/<system>_monthly.csvCSV

The monthly ledger as a table, one row per reach and month.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/accounting/swp_monthly.csv"
ColumnType, unitsMeaning
systemstring
reachstring
monthYYYY-MM
headafTerms as in the JSON; blank = no record or does not apply.
inflowaf
deliveryaf
branchaf
storage_changeaf
tailaf
gainafUnmeasured gain (+) or loss (−).
qualitystring

GET /api/v1/estimates/index.jsonJSON

Estimates, not measurements: loss and open-term ranges, in their own files.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/estimates/index.json"
FieldType, unitsMeaning
layerstring"estimate".
labelstringWhat an estimate is here.
files[]objectname, path, system, about: one file per estimate set; every file says layer: "estimate" at its top.

Estimates are modelled from published inputs (reference evapotranspiration, rain, canal geometry, dry-year analogues). They are never added into a series, a ledger row or a total, and they are not part of the measured files.

The file layouts are the map's own (as the map loads them) and may change; the top-level layer, system and label fields are frozen.

GET /api/v1/tributaries/index.jsonJSON

Tributary flow requirements: the rivers, compliance points, instruments and requirements the Tributaries Map judges.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/tributaries/index.json"
FieldType, unitsMeaning
monthsYYYY-MM[]Months the verdict counts cover.
data_endYYYY-MM-DDLast day judged.
rivers[]objectid (the <river> of the river paths), name, basin, upper (an upper-watershed system), points (id, name, reach, gauge, reqs), files.
requirements.<id>objecttitle, instrument, section (judged, listed, proposed, reference), judge_label, reason (why it is not judged), gauge, compliance_point, mode (min, band, ramp, rmean, volume, event), effective (from, to), cite, data_notes.
instruments.<id>objectlabel, name, agency, kind.
gauges.<key>objectlabel, series (the source series id), operator.
excusestringWhat a shortfall means here: the requirement as written; it may have been excused or varied.

A requirement is judged against the daily mean at its compliance gauge, as written. A shortfall can have been excused or varied (a FERC order, a temporary urgency change); the event markers on the map show the ones on record.

GET /api/v1/tributaries/<river>.csvCSV

One river: per judged requirement and month, the day counts behind the verdict.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/tributaries/mokelumne.csv"
ColumnType, unitsMeaning
riverstringRiver id.
compliance_pointstringPoint id.
requirementstringRequirement id.
monthYYYY-MM
days_in_forceintDays the requirement applied.
days_metint
days_below_as_writtenintDays below the minimum as written.
days_above_bandintDays above a maximum or band.
days_nd_year_type_unknownintNot determinable: the year type was not published.
days_nd_no_gauge_valueintNot determinable: no gauge value.
days_nd_not_yet_publishedintNot determinable: the data are not yet published.
days_nd_other_clauseintNot determinable: a clause of the rule (see the requirement).
days_nd_temporary_varianceintNot determinable: a FERC-approved temporary variance.
days_provisionalintDays judged on provisional data.
days_usgs_estimatedintDays whose gauge value USGS marks estimated.
days_exceptedintDays inside an exception period of the rule.
days_above_written_range_notedintDays above the written range, noted.
lowestcfs | afLowest daily mean (a volume requirement: af to date; an event requirement, mode event: the highest mean over its stated hours in the window, judged once on the window's last day).
lowest_dayintDay of the month of the lowest value.
requirement_lowcfs | afLowest required value in the month (the lowest year type it could be).
requirement_highcfs | afHighest required value in the month.
largest_shortfall_pctnumberLargest shortfall of a day below, % of the minimum.

Only judged requirements have rows; the rest are listed in index.json with the reason.

api/v1/tributaries/<river>.json has the same counts as lists per requirement (months, and one list per column above).

GET /api/v1/notes.jsonJSON

The data notes: every source defect and pipeline fix found, with evidence, resolution and live status.

curl --compressed "https://storage.googleapis.com/surfacewater-map-509303/api/v1/notes.json"
FieldType, unitsMeaning
builtYYYY-MM-DDWhen the notes were built.
issues[].idstringNote id, as in the series' data_notes.
issues[].titlestring
issues[].kindstringsource_defect, pipeline_bug, ...
issues[].sourcestringThe source it is about.
issues[].affectsstring[]What it affects.
issues[].periodsstring[]Periods affected.
issues[].evidencestring
issues[].resolutionstring
issues[].statusstring
issues[].livestringWhether the fix is on the public site.
issues[].featuresstring[]Series ids it is about.
issues[].reachesstring[]Ledger reaches it is about.
issues[].requirementsstring[]Tributary requirement ids it is about.