GET /v1/metrics/*

Last updated Aug 12, 2026View as MarkdownAgent setup

The read API: the same numbers the dashboard shows, for your own pricing pages, admin panels, and investor updates.

curl "https://api.datagauge.dev/v1/metrics/mau?date_from=2026-06-19&date_to=2026-07-18" \
  -H "Authorization: Bearer $DATAGAUGE_KEY"

Two credentials can read. An sk_live_ key is scoped to one app, so the project is implied. A dg_live_ control token with the metrics:read scope is org-scoped and reads any app in the org, so it must name one: add ?project=proj_.... That is the app's public_id from GET /v1/apps, the same value the billing webhook path calls {project_id}, and not the app's app_id. Public pk_live_ keys can never read.

Both credentials are plane-confined. An sk_test_ key reads its app's test plane; a dg_test_ token reads only test projects (name one with ?project=<test_public_id>). A dg_live_ token reads only live projects; a project in the other plane is 404 unknown project, the same as one outside your org.

MetricReturnsWindow
maudistinct active usersinclusive dates, default last 30 days
daudaily series [{day, dau}]same
signupsdistinct users who signed upsame
revenue[{day, currency, revenue}], minor units, earned onlysame
volume[{day, currency, volume}], marketplace gross processedsame
mrr[{currency, mrr}], currentwindowless

Distinct counts (mau, dau, signups) are HyperLogLog-precise (~0.5% at scale) and immune to duplicate events by construction. Money endpoints are exact.

How far behind ingest these numbers are

A write is not readable here for about five minutes. Two delays stack, and neither is a fault:

StageDelay
Ingest batches before it stores. A trickle sender's batch waits out the timeout rather than filling.up to 30 s
Metric reads are cached at the edge (x-cache: hit|miss).300 s

The cache is the one that surprises people, because it latches: read a metric one second after writing and you pin the pre-write answer for the next five minutes, however many times you ask.

This matters most when you are learning the reserved names: someone checking whether payment really needs props.amount sends one, reads back zero revenue, and cannot tell a rejected event from a queued one. Read the ingest response instead: a rejected event is 422, or a 202 carrying a non-empty errors[], and it says so at the moment you send it rather than five minutes later.

If the upstream is rate-limited or unavailable we serve the last known value rather than fail: x-cache: stale, with x-cache-age giving its age in seconds.

When a read is refused: 429

Two different ceilings can refuse a read, and both answer 429. They are not the same event and the right response to them is opposite, so branch on x-datagauge-throttle rather than on the status code.

x-datagauge-throttleWhat happenedWhat to do
rate_limitYour plan's per-minute read ceiling.Retry. Retry-After gives the exact seconds to wait, computed from the oldest request still inside the window, not a fixed backoff.
allowance:queriesThe org's agent-query allowance for the billing period is spent.Do not retry: it cannot succeed until the period rolls over. x-datagauge-reset is the ISO 8601 instant it returns.
allowance:raw_scansThe sub-limit on the three metrics that scan raw events (event_names, event_count, event_series) is spent.Stop asking for those three; x-datagauge-reset again says when they return. Every other metric is still served.

Retry-After is present on rate_limit and deliberately absent from an allowance refusal, where the honest value would be days. The body carries the same facts for a client that parses bodies:

{ "ok": false, "reason": "allowance", "scope": "queries",
  "reset_at": "2026-08-01T00:00:00.000Z",
  "error": "agent query allowance exhausted for this billing period" }

The per-minute ceiling is per organization: an sk_live_ key's traffic and a dg_live_ token's traffic add up against the same figure, and issuing more credentials of either kind does not raise it. A cache hit (x-cache: hit) never consults either limit.

Only dg_live_ reads spend the period allowance; that is the agent surface, and the MCP server is one of its clients, so a tool call and a direct token read cost the same thing. sk_live_ reads and your own dashboard never spend it.

Treat an x-datagauge-throttle value you do not recognize as a bare 429. The three above are the whole vocabulary; matching a new label by prefix is how a client ends up retrying something it should have stopped on.

A machine-readable description of the whole API (this read plane plus the ingest endpoints) lives at https://datagauge.dev/openapi.json (OpenAPI 3.1), on the docs site rather than on api.datagauge.dev. The 429 is described there too, with the header vocabulary as an enum.