Public API
Read-only JSON of what the map shows: GSA and subbasin series, stored forecasts, the kriged surfaces and the context layers. No key needed.
https://groundwater-map-175591637760.us-west1.run.app/api/v1Endpoints
/api/v1: This list, as JSON: every v1 endpoint, the release id and the citation./api/v1/scenarios: The stored forecasts you can ask /api/v1/forecast for./api/v1/forecast: A stored forecast from today's levels: a preset, the NOAA outlook, or a drought replay./api/v1/gsa: GSA polygons, each GSA's depth history, its forecast range and its plan's MT./api/v1/basins: Bulletin 118 subbasins: polygons, depth history, forecast range./api/v1/summary_now: Per GSA, now: record lows and the plan's rule./api/v1/changes: What changed at each monthly refresh, per GSA./api/v1/hindcast: Per GSA: what the model forecast a year ago, and its record./api/v1/normal_low: Deepest month of a normal year per grid cell./api/v1/history: Monthly kriged surfaces of depth to groundwater since 2005./api/v1/wells: The map's wells, one list record each./api/v1/well_timeseries: Each well's monthly readings./api/v1/isw: Interconnected surface water: monitoring wells near rivers./api/v1/insar: Measured land subsidence per GSA (InSAR)./api/v1/recharge: Recharge opportunity: soils that take water, near a canal./api/v1/grayed: Subbasins with no forecast or a low-confidence note.
Conventions
- Depths are ft below ground surface; larger is deeper. Elevations are ft NAVD88.
- Months are
YYYY-MM. A series lines up with themonthslist beside it. nullmeans no value: no reading, outside the valley, or not computed.- GSA ids are DWR's i03 GSA ids, as strings. Subbasins are Bulletin 118 codes.
- Grids:
boundsgives the extent; row 0 is the north edge, column 0 the west edge. - The surfaces and GSA averages describe the main aquifer. Upper-zone wells and flagged wells are left out.
Versioning
- The fields listed here are frozen in v1: their name, units, and meaning will not change.
- v1 only grows: new fields, new endpoints, new parameter values.
- A breaking change goes to
/api/v2. v1 stays up beside it. - Fields not listed here are the app's own and can change or go away. Do not rely on them.
- The values change at each monthly refresh (new readings, rerun forecasts). The release id says which refresh you got.
Caching and limits
- Responses carry an
ETagandCache-Control: no-cache. SendIf-None-Matchand you get a 304 until the data change. - The data change about once a month, when a refresh goes live.
- Every v1 response carries
X-Data-Release, the release id. - Ask for gzip (
Accept-Encoding: gzip): the large responses shrink several-fold. - CORS: any origin may GET
/api/v1/*from a browser. No credentials. - Limit: 300 v1 requests per 10 minutes per address. Every request counts, 304s too. Each answer carries
X-RateLimit-LimitandX-RateLimit-Remaining. Over the limit you get a 429 withRetry-After(seconds). - The map's live custom forecast is not in v1. It is limited to 20 per 10 minutes per address.
- For the whole dataset, take the monthly files below instead of looping over the API.
Bulk downloads
Each monthly refresh is frozen as a release at https://groundwater-map-175591637760.us-west1.run.app/release: per-well forecast tables (CSV, Parquet), the wells as GeoJSON, the surfaces as GeoTIFF, GSA and subbasin summaries (CSV), a README, CITATION.cff, the licence, SHA-256 checksums, and one zip. A release's files do not change after later refreshes.
Licence and citation
The app's outputs (forecasts, kriged surfaces, averages, summaries, API answers) are licensed CC BY-NC 4.0 (cite Gray, W.). 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 published before the change to CC BY-NC 4.0 remain under CC BY 4.0. Each release's page gives its own licence. Cite:
Gray, W. (2026). Central Valley Groundwater Map and Forecast, release 2026-09.5cdfad9c. CC BY-NC 4.0.
Input data keep their own terms:
- Groundwater levels: California DWR, Periodic Groundwater Level Measurements (data.cnra.ca.gov, public domain); USGS CVHM2 observations
- Precipitation and temperature: PRISM Group, Oregon State University, https://prism.oregonstate.edu, accessed 2026-10-22
- Evapotranspiration: OpenET ensemble v2.1 (https://etdata.org), CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/); sampled at the wells
- Vegetation (NDVI): NASA MODIS MOD13A2 v061 (Didan, K., doi:10.5067/MODIS/MOD13A2.061), via NASA AppEEARS
- Crop cover: USDA NASS Cropland Data Layer
- River flows: USGS NWIS. Project flows and water-year indices: California DWR CDEC. Allocations: U.S. Bureau of Reclamation (CVP) and California DWR (SWP)
- GSA boundaries (i03) and groundwater basins (i08 Bulletin 118, critically overdrafted basins): California DWR, data.cnra.ca.gov, Creative Commons Attribution (http://www.opendefinition.org/licenses/cc-by)
- Ground elevation: USGS 3DEP 1/3 arc-second DEM and DWR surveyed station elevations. Data available from U.S. Geological Survey, National Geospatial Program. Recharge soils: Soil Survey Staff (2020). Gridded National Soil Survey Geographic (gNATSGO) Database for the Conterminous United States. USDA Natural Resources Conservation Service. https://nrcs.app.box.com/v/soils. Canals: (c) OpenStreetMap contributors (ODbL)
- NOAA outlook scenario: NOAA Climate Prediction Center long-lead outlooks. Disadvantaged communities: California DWR i16 (2023), CC BY. Domestic wells: DWR Well Completion Reports; dry wells: DWR Dry Well Reporting System
The model card lists each model input and its source.
GET /api/v1
This list, as JSON: every v1 endpoint, the release id and the citation.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1"
| Field | Type, units | Meaning |
|---|---|---|
version | string | "v1". |
release | string | null | Release id of the data served now, e.g. 2026-09.c1a3d688. |
licence | string | The outputs' licence, "CC BY-NC 4.0". The additional permission is under Licence and citation above. |
cite | string | Citation line, with the release id. |
docs | string | URL of this page. |
endpoints[] | object | path, summary, example, params (name, values) and fields (frozen names). |
GET /api/v1/scenarios
The stored forecasts you can ask /api/v1/forecast for.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/scenarios"
| Field | Type, units | Meaning |
|---|---|---|
first_month | YYYY-MM | First forecast month (the month after the last reading month). |
history_end | YYYY-MM | Last month with readings. |
presets | object | Preset name -> precipitation, % of normal: severe 50, moderate 70, normal 100, wet 130. |
horizons | int[] | Forecast lengths in months a preset or the outlook can be asked for. |
replays[] | object | key, label, months: a past drought year's weather and water supply from today's levels. |
outlook | object | null | NOAA CPC seasonal outlook scenario: issue (YYYY-MM), water_year, central_pct (% of normal precipitation), member_pct ([dry, wet] %), temp_delta_c, max_horizon (months). |
GET /api/v1/forecast
A stored forecast from today's levels: a preset, the NOAA outlook, or a drought replay.
| Parameter | Values | |
|---|---|---|
scenario | severe | moderate | normal | wet | outlook | Default normal. |
horizon | 6 | 12 | 24 | 36 | Months; default 12. The outlook stops at its max_horizon. |
replay | a key from /api/v1/scenarios | Instead of scenario; its length is fixed. |
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/forecast?scenario=normal&horizon=12" curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/forecast?scenario=outlook&horizon=12" curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/forecast?replay=WY2015"
| Field | Type, units | Meaning |
|---|---|---|
months | YYYY-MM[] | Forecast months. |
grids | [month][row][col] number | null | Kriged depth to groundwater, ft below ground. Rows run north to south, columns west to east; cell centres are evenly spaced from the bounds' edges. null outside the valley and in grayed subbasins. |
bounds | object | Grid extent: lat_min, lat_max, lon_min, lon_max (degrees, WGS84), nrows, ncols. |
gsa.<id> | number[] (ft below ground) | Area-weighted average of the grid per month. Keyed by GSA id (DWR i03 GSA id, as a string). |
basins.<code> | number[] (ft below ground) | The same per subbasin; null for a grayed subbasin. Keyed by Bulletin 118 subbasin code, e.g. 5-022.09. |
gsa_elev.<id> | number[] (ft, NAVD88) | Water-table elevation average per month (ground minus depth). null when the ground grid is not loaded. |
basins_elev.<code> | number[] (ft, NAVD88) | The same per subbasin. |
thresholds.<id> | object | n: the GSA's RMS wells with a recent reading; past[]: how many of them are forecast deeper than their minimum threshold (MT), per month. |
scenario | object | horizon_months, precip_pct (% of normal), inputs (other settings; {} for presets). Replays add analog (key), label, source_months; the outlook adds outlook (as in /api/v1/scenarios). |
The 80 % range of a GSA or subbasin average is in /api/v1/gsa and /api/v1/basins (uncertainty).
Months 1–24 are tested; later months are a what-if.
Per-well forecasts are in the bulk downloads.
GET /api/v1/gsa
GSA polygons, each GSA's depth history, its forecast range and its plan's MT.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/gsa"
| Field | Type, units | Meaning |
|---|---|---|
layer | GeoJSON FeatureCollection | Simplified GSA polygons (WGS84). properties: id, name, subbasin_code, subbasin, gsp_id, n_wells. |
series.months | YYYY-MM[] | Months of every series below, from 2005-01. |
series.gsas.<id>.avg | number[] (ft below ground) | Area-weighted average of the kriged surface per month. |
series.gsas.<id>.median | number[] | null (ft below ground) | Median of the readings inside the GSA that month. |
series.gsas.<id>.n_readings | int[] | Main-aquifer wells inside the GSA with a reading that month. |
series.gsas.<id>.n_wells | int | Main-aquifer wells inside the GSA. |
series.gsas.<id>.coverage | 0–1 | Share of the GSA's area the kriged surface covers. |
uncertainty.level | 0–1 | Coverage the band is built for (0.8). |
uncertainty.horizons | int | Months the band covers (12); later months have none. |
uncertainty.gsas.<id> | [lower[], upper[]] (ft) | Offsets added to the forecast average per month: the 80 % range is [avg + lower[k], avg + upper[k]]. From held-out backtests. |
uncertainty.default | [lower[], upper[]] (ft) | Offsets for a GSA without its own. |
thresholds.<id> | object | From the GSP: n_sites (RMS wells), mt_depth_median, mo_depth_median (ft below ground), pct_past_mt_now (share past MT in the latest readings, 0–1), gsp_id. |
Keyed by GSA id (DWR i03 GSA id, as a string). GSAs that overlap each hold their own wells.
GET /api/v1/basins
Bulletin 118 subbasins: polygons, depth history, forecast range.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/basins"
| Field | Type, units | Meaning |
|---|---|---|
layer | GeoJSON FeatureCollection | Subbasin polygons (WGS84). properties: id (subbasin code), name, basin, area_sqmi, critically_overdrafted, gsas (GSA ids). |
series.months | YYYY-MM[] | Months of every series below. |
series.basins.<code>.avg | number[] (ft below ground) | Area-weighted average of the kriged surface per month. |
series.basins.<code>.median | number[] | null (ft below ground) | Median of the readings inside that month. |
series.basins.<code>.n_readings | int[] | Main-aquifer wells with a reading that month. |
series.basins.<code>.n_wells | int | Main-aquifer wells inside. |
uncertainty.basins.<code> | [lower[], upper[]] (ft) | 80 % range offsets, as for GSAs. |
uncertainty.default | [lower[], upper[]] (ft) | Offsets for a subbasin without its own. |
GET /api/v1/summary_now
Per GSA, now: record lows and the plan's rule.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/summary_now"
| Field | Type, units | Meaning |
|---|---|---|
history_end | YYYY-MM | Last month with readings. |
months | int | Window for "now" (months up to history_end). |
gsa.<id>.subs_n | int | Main-aquifer wells with a long enough record to judge a record low. |
gsa.<id>.subs_now | int | Of those, wells whose deepest reading since 2005 falls in the window. |
gsa.<id>.subs_sites | string[] | Their site codes. |
gsa.<id>.ur_now | bool | null | The plan's undesirable-result rule is met in the latest readings; null when it cannot be judged. |
GET /api/v1/changes
What changed at each monthly refresh, per GSA.
| Parameter | Values | |
|---|---|---|
g | GSA id | One GSA's lines in every kept edition. Unknown id: 404. |
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/changes" curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/changes?g=40"
| Field | Type, units | Meaning |
|---|---|---|
editions[].history_end | YYYY-MM | Last reading month of that refresh. |
editions[].previous | YYYY-MM | null | The refresh it is compared with. |
editions[].built | ISO 8601 time | When it was built (UTC). |
editions[].outlook | object | null | The NOAA outlook used: issue, water_year, central_pct, member_pct. |
editions[].notable | object | Without g: GSA id -> count of notable items. |
gsa | string | With g: the GSA id. |
name | string | With g: the GSA name. |
editions[].items[] | object | With g: kind (a code such as level, rms_state, forecast; more may be added), text (one sentence), notable (bool). |
GET /api/v1/hindcast
Per GSA: what the model forecast a year ago, and its record.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/hindcast"
| Field | Type, units | Meaning |
|---|---|---|
origin | YYYY-MM | Month the year-ago forecast starts from. |
last_history_month | YYYY-MM | Last month with readings. |
trained_through | YYYY-MM | The tested model saw data up to this month only. |
leads | int[] | Lead times in months (1, 6, 12). |
gsas.<id>.path | [YYYY-MM, number][] (ft below ground) | The 12-month forecast from origin. |
gsas.<id>.ahead.<lead> | [YYYY-MM, number][] (ft below ground) | For each month, the forecast made lead months before. |
gsas.<id>.stats.<lead> | object | n, mae (ft), bias (ft; + = forecast too deep), against the GSA's kriged average. |
Given the weather and water supply that came, not a scenario.
GET /api/v1/normal_low
Deepest month of a normal year per grid cell.
| Parameter | Values | |
|---|---|---|
horizon | 1–36 | Months; default 12. |
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/normal_low?horizon=12"
| Field | Type, units | Meaning |
|---|---|---|
horizon | int | Months covered. |
months | YYYY-MM[] | Those months. |
grid | [row][col] number | null (ft below ground) | Deepest forecast depth over the months, normal-year preset; the same grid as /api/v1/history. |
GET /api/v1/history
Monthly kriged surfaces of depth to groundwater since 2005.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/history"
| Field | Type, units | Meaning |
|---|---|---|
months | YYYY-MM[] | Months, from 2005-01. |
grids | [month][row][col] number | null (ft below ground) | Kriged from main-aquifer wells. A well's reading is held up to 12 months. Rows north to south, columns west to east. |
bounds | object | Grid extent: lat_min, lat_max, lon_min, lon_max (degrees, WGS84), nrows, ncols. |
valley_mask | [row][col] 0 | 1 | 1 inside the Central Valley. |
Large: about 13 MB, 1.4 MB with gzip.
GET /api/v1/wells
The map's wells, one list record each.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/wells"
| Field | Type, units | Meaning |
|---|---|---|
[][0] | number | Latitude (WGS84). |
[][1] | number | Longitude. |
[][2] | number | null | Well depth, ft. |
[][3] | string | County. |
[][4] | string | DWR site code: the key in /api/v1/well_timeseries. |
[][5] | string | Zone: aquifer, or upper (perched or shallow; left out of the surfaces). |
[][9] | number | null | Ground elevation, ft NAVD88. |
[][11] | string | Position relative to the Corcoran Clay: above, below, spans, no_clay, unknown, or "". |
Positions not listed are the app's own. New fields are added at the end.
GET /api/v1/well_timeseries
Each well's monthly readings.
| Parameter | Values | |
|---|---|---|
site | site codes, comma-separated (up to 100) | Without it: every well. |
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/well_timeseries?site=361737N1194798W001"
| Field | Type, units | Meaning |
|---|---|---|
months | YYYY-MM[] | Months of every series, from 2005-01. |
wells.<site> | number | null [] (ft below ground) | The well's reading per month; null = no reading. |
GET /api/v1/isw
Interconnected surface water: monitoring wells near rivers.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/isw"
| Field | Type, units | Meaning |
|---|---|---|
fields | string[] | Names of the positions in each wells record (site, lat, lon, kind, river, dist_km, median_ft, last_depth, last_month, mt_depth, gsas, name, zone). New names are added at the end. mt_depth is always null here. |
wells[] | list | One record per well, in the order of fields. Depths ft below ground; river = index into rivers; dist_km to that reach. |
rivers[] | [name, [[lon, lat], ...]] | River reaches. |
near_km | number | Distance that counts as near a river, km. |
shallow_ft | number | Depth that counts as shallow, ft. |
history_end | YYYY-MM | Last month with readings. |
source | string | Data sources. |
GET /api/v1/insar
Measured land subsidence per GSA (InSAR).
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/insar"
| Field | Type, units | Meaning |
|---|---|---|
source | string | DWR / TRE ALTAMIRA InSAR, vertical displacement. |
first | YYYY-MM | First month of the record. |
last | YYYY-MM | Last month of the record. |
last12_from | YYYY-MM | Start of the last-12-months window. |
gsa.<id>.last12 | ft | Median vertical change over the last 12 months; negative = sinking. |
gsa.<id>.last12_p10 | ft | The most-subsided tenth of the GSA's pixels (10th percentile). |
gsa.<id>.since | ft | Median change since 2015-06-13. |
gsa.<id>.since_p10 | ft | 10th percentile since 2015-06-13. |
gsa.<id>.wy.<year> | ft | Median change in each water year (Oct–Sep). |
gsa.<id>.valid_frac | 0–1 | Share of the GSA's area with InSAR pixels. |
GET /api/v1/recharge
Recharge opportunity: soils that take water, near a canal.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/recharge"
| Field | Type, units | Meaning |
|---|---|---|
bounds | object | Grid extent: lat_min, lat_max, lon_min, lon_max (degrees, WGS84), nrows, ncols. |
cells[] | [cell, good, fair, km, sy] | Per grid cell (index = row × ncols + col): acres of good and fair soils, km to a canal, specific yield. |
gsa.<id>.acres | acres | GSA area. |
gsa.<id>.good | acres | Good soils. |
gsa.<id>.fair | acres | Fair soils. |
gsa.<id>.good_near | acres | Good soils within params.near_km of a canal. |
gsa.<id>.fair_near | acres | Fair soils near a canal. |
params | object | Thresholds: ksat_good_um_s, ksat_fair_um_s, resdept_min_cm, near_km. |
source | string | Data sources. |
GET /api/v1/grayed
Subbasins with no forecast or a low-confidence note.
curl --compressed "https://groundwater-map-175591637760.us-west1.run.app/api/v1/grayed"
| Field | Type, units | Meaning |
|---|---|---|
<code>.name | string | Subbasin name. |
<code>.status | string | grayed (no forecast) or low_confidence. |
<code>.message | string | Why. |