The REST API

Everything the site displays — 15-minute flow readings, statistics, analytics, comparisons, your tracked gages — is available programmatically under /api/v1/. This page is the orientation: who gets access, how authentication works, and what the rate limits mean. The endpoint-by-endpoint detail — parameters, response schemas, copy-paste curl and Python samples — lives in the interactive API reference, and this page won't duplicate it.

The reference is interactive: sign in and you can run GET requests against your own data right in the page, no key required.

Who gets access

Programmatic API access is a Research plan feature. On Research, the API console lets you provision named API keys, reveal or revoke them, watch today's usage, try requests live, and connect to the hosted MCP server. On other plans the console link shows current plans — and the entire reference is browsable without any plan at all, so you can evaluate the API before upgrading.

A key carries exactly the access your account has: the gages you can read through the API are the gages you track on the site, and plan limits (tracked-gage count, export formats) apply identically.

Authentication, in detail

Three credentials work; all of them are sent the same way on every request (except sessions, which ride the browser's cookie):

Authorization: Bearer <token>

API keys — for scripts, notebooks, and data pipelines

Long-lived tokens provisioned from the API console. Each key has a name, can be re-revealed later, and can be revoked individually — revocation takes effect on the next request. This is the right credential for anything unattended: cron jobs, notebooks, dashboards, MCP clients.

Treat keys like passwords: environment variables, not source code. If a key leaks, revoke it in the console — revocation is immediate. Moving to a plan without API access also revokes your keys permanently; if you upgrade again later, provision new ones from the console.

JWT access tokens — for short-lived, user-interactive tools

POST /api/v1/auth/token/ with a JSON body of {"username": "you@example.com", "password": "…"} — the username field accepts your email address, and the account must have a verified email — returns an access/refresh token pair. Access tokens expire after a few minutes; refresh via POST /api/v1/auth/token/refresh/. Useful when you'd rather not mint a durable key — but for anything that runs while you're not watching, prefer an API key.

Browser session — for the interactive reference

Same-origin requests from a signed-in browser authenticate with the session cookie automatically. This is what powers the try-it panel in the reference. Writes (POST/PATCH/DELETE) additionally require Django's CSRF header — the reference page handles that for you; your own same-origin JavaScript must send X-CSRFToken.

Quickstart

List the gages your account can read, then pull a month of readings:

curl -H "Authorization: Bearer $GAGELOG_API_KEY" \
     "https://gagelog.com/api/v1/sources/"

curl -H "Authorization: Bearer $GAGELOG_API_KEY" \
     "https://gagelog.com/api/v1/gages/?gage_key=USGS&start=2026-07-01&end=2026-08-01"

The same in Python, with pagination handled:

import os, requests

BASE = "https://gagelog.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['GAGELOG_API_KEY']}"}

def readings(gage_key, start, end):
    rows, url = [], f"{BASE}/gages/"
    params = {"gage_key": gage_key, "start": start, "end": end, "page_size": 5000}
    while url:
        resp = requests.get(url, headers=HEADERS, params=params, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        rows += [r for r in data["results"] if not r["flagged"]]
        url, params = data.get("next"), None   # "next" carries the query string
    return rows

july = readings("USGS", "2026-07-01", "2026-08-01")
print(f"{len(july)} readings, mean {sum(r['cfs'] for r in july)/len(july):.1f} cfs")

Conventions that apply everywhere:

  • Timestamps are UTC ISO-8601; flows are cubic feet per second (CFS).
  • Exclude "flagged": true readings from any statistic — they're sensor errors (negative flows and the like), and the site's own analytics skip them too.
  • List endpoints paginate with page/page_size (default 100, max 5000); follow the next link until null.
  • A water year runs Oct 1 – Sep 30 and is named for the year it ends in.

Rate limits & cost tiers

API-key traffic shares one daily quota (10,000 units/day by default), and every endpoint belongs to a cost tier that sets how many units a call consumes and how many calls per minute it allows:

TierPer-minute capQuota cost / callEndpoints
light120 / min1 unit Readings, sources, monitors, exports — everything not listed below
medium30 / min2 units Sensor analytics: summary, rolling, threshold, volume, data quality
heavy10 / min5 units Multi-gage summary

Those are the defaults — your plan's live numbers, today's count, and the reset time come from GET /api/v1/developer/usage/ (which is quota-exempt, so it still answers when you're over). Each endpoint's tier is badged in the reference and shown on the console's Usage tab. Over a limit you get 429 with a Retry-After header; the daily counter resets at midnight UTC. Browser-session and JWT calls don't count against the key quota.

What's in the API

The reference groups endpoints the same way:

FamilyWhat it covers
Flow data Tracked sources and what's available to track; 15-minute readings; server-side analytics (window stats, rolling averages, threshold compliance, volumes, trends, data quality).
Alerts & jobs Threshold/anomaly monitors and their events; async spreadsheet export builds; cold-storage archive state and restores.
Account & access JWT minting, profile and preferences, your plan and its limits, API keys and usage.

Errors you'll meet

StatusMeaning
401Bad, expired, or revoked credential — check the console.
403Your plan doesn't include that capability, or the gage isn't tracked.
429A per-minute cap or the daily quota was hit — the tier table above says which limit applies; wait out Retry-After.

The MCP server

Alongside REST, GageLog hosts a Model Context Protocol server — an open, vendor-neutral protocol for exposing an API as discoverable, self-describing tools. It mirrors the REST interface: readings, sources, and the full server-side analytics suite (window statistics, rolling averages, threshold compliance, volumes, trends, data quality, multi-gage comparisons) are callable as MCP tools, which makes it a natural fit for heavy analytics, research, and data-science workflows that orchestrate many queries.

https://gagelog.com/mcp

Authentication is the same Bearer API key you use for REST — sent as an Authorization: Bearer header by your MCP client. There is nothing separate to provision, and nothing separate to meter: MCP calls are executed against the REST API on your behalf, so they draw from the same daily quota and per-minute tiers described above, and GET /api/v1/developer/usage/ reports both. The transport is streamable HTTP (stateless), so any MCP-compatible client can connect with a URL and a header:

{
  "mcpServers": {
    "gagelog": {
      "url": "https://gagelog.com/mcp",
      "headers": { "Authorization": "Bearer <your key>" }
    }
  }
}

The tool set covers the same families as the reference: listing sources and gages, pulling 15-minute readings, every analytics endpoint, usage, and plans. Each tool documents its units (CFS), date format (YYYY-MM-DD), and cost tier, and an openapi resource exposes the full REST schema for anything not surfaced as a tool. Setup details live on the console's MCP server tab.