Stats API

Read your stats programmatically: API keys, authentication, rate limits and every /api/v1 endpoint.

The Stats API exposes the same numbers as the dashboard over plain HTTPS + JSON: totals, time series, and breakdowns for any site, plus write endpoints for annotations, publish pings, and server-side bot collection. All endpoints live under https://app.numative.com/api/v1/.

Create an API key

Go to Account > API, give the key a name, and pick a scope. The full key is shown once at creation; only its SHA-256 hash is stored, so copy it immediately. Keys look like nmv_live_... and can be revoked at any time from the same page.

  • read (default): the reporting endpoints (stats, timeseries, breakdown) and listing annotations.
  • write: everything a read key can do, plus creating and deleting annotations, publish pings, and server-side collection.

A key belongs to your organization and can access every site in it.

Authentication

Send the key in the Authorization header on every request:

http
Authorization: Bearer nmv_live_...

An x-api-key: nmv_live_... header is also accepted. For platforms that can only configure a fixed URL (log drains), a ?key= query parameter works too, but URLs may be logged by the platform, so only use it with a write-only ingest key, never a read key.

Errors are returned as JSON: {"error": "message"} with status 400 (bad input), 401 (missing/invalid key), 403 (scope), 404 (site not found for this key), or 429 (rate limited).

Rate limits

EndpointLimit per key
stats, timeseries, breakdown, annotations600 requests / minute
collect600 requests / minute
ping60 requests / minute

Common query parameters

The three read endpoints share these parameters. The requested site must belong to the key's organization.

ParameterTypeDefaultDescription
site_idstringrequiredThe site UUID or the public snippet id (the data-site value); both are accepted.
rangestring30dOne of: realtime, today, yesterday, 7d, week, month, 30d, 90d, year, ytd, last-year, 12mo, all, custom. Relative ranges resolve in the site's timezone.
fromdate-Start date (YYYY-MM-DD), used with range=custom.
todate-End date (YYYY-MM-DD), used with range=custom.
filtersJSONnoneURL-encoded JSON array of segment filters (see below).
limitnumber100Max rows returned by breakdown, clamped to 1-1000.

The response bucket size (interval) is derived from the range (minute for realtime, hour for today/yesterday, day and up for longer windows) and echoed in every response's range object.

Filters

A filter is an object with dimension, operator, and value keys, where value is an array of strings and operator is one of is, is_not, contains, or matches. Dimensions: path, entry_page, exit_page, source, channel, referrer, utm_source, utm_medium, utm_campaign, utm_term, utm_content, country, region, city, browser, os, device, screen_class, hostname, event, plus prop:<key> for custom properties.

Example filters value (before URL encoding)text
[{"dimension":"country","operator":"is","value":["US","CA"]}]
All /api/v1 responses send Access-Control-Allow-Origin: *, so the API is callable from anywhere, including browsers. Do not embed API keys in client-side code, though: anyone who can read the page can read the key. Call the API from your server.

GET /api/v1/stats

Headline totals for the window.

bash
curl "https://app.numative.com/api/v1/stats?site_id=SITE_ID&range=30d" \
  -H "Authorization: Bearer nmv_live_..."
Responsejson
{
  "range": { "from": "2026-06-02T14:00:00.000Z", "to": "2026-07-02T14:00:00.000Z", "interval": "day" },
  "totals": {
    "visitors": 4210,
    "visits": 5124,
    "pageviews": 9876,
    "bounceRate": 0.41,
    "avgDuration": 74.2,
    "viewsPerVisit": 1.93,
    "events": 312
  }
}

bounceRate is a fraction (0 to 1), avgDuration is seconds, and events counts custom (non-pageview) events.

GET /api/v1/timeseries

Visitors, visits, and pageviews bucketed over time.

bash
curl "https://app.numative.com/api/v1/timeseries?site_id=SITE_ID&range=7d" \
  -H "Authorization: Bearer nmv_live_..."
Responsejson
{
  "range": { "from": "2026-06-25T14:00:00.000Z", "to": "2026-07-02T14:00:00.000Z", "interval": "day" },
  "series": [
    { "timestamp": "2026-06-25T00:00:00.000Z", "visitors": 512, "visits": 590, "pageviews": 1204 },
    { "timestamp": "2026-06-26T00:00:00.000Z", "visitors": 486, "visits": 553, "pageviews": 1131 }
  ]
}

GET /api/v1/breakdown

Ranked rows for one dimension. Takes the common parameters plus:

ParameterTypeDefaultDescription
propertystringrequiredThe dimension to rank by: any dimension from the filters list, or prop:<key>.
bash
curl "https://app.numative.com/api/v1/breakdown?site_id=SITE_ID&property=source&range=30d&limit=5" \
  -H "Authorization: Bearer nmv_live_..."
Responsejson
{
  "range": { "from": "2026-06-02T14:00:00.000Z", "to": "2026-07-02T14:00:00.000Z", "interval": "day" },
  "property": "source",
  "results": [
    { "value": "google.com", "visitors": 1810, "visits": 2011, "pageviews": 3902, "bounceRate": 0.38, "avgDuration": 81.5 },
    { "value": "news.ycombinator.com", "visitors": 640, "visits": 655, "pageviews": 1105, "bounceRate": 0.61, "avgDuration": 42.0 }
  ]
}

POST /api/v1/collect

Server-side collection of bot and AI-crawler visits, the requests a JavaScript tracker never sees. Requires a write-scoped key. Only bot traffic is stored; human and junk hits are dropped, so nothing is double-counted against the JS tracker. Stored events count toward your event quota. The in-app setup card (your site's Settings, under AI crawler tracking) has ready-made snippets for WordPress, Cloudflare Workers, Vercel log drains, and Node built on this endpoint.

Body fieldTypeDefaultDescription
site_idstringrequiredThe site's public snippet id.
eventsarrayrequiredUp to 500 hits per request. A single hit may also be sent as the body itself, without the events wrapper.
events[].pathstringrequiredRequest path, e.g. /pricing.
events[].userAgentstringrequiredThe visitor's User-Agent (user_agent and ua are accepted as aliases).
events[].ipstring-Client IP, used for bot verification (clientIp is an alias).
events[].referrerstring-Referrer URL (referer is an alias).
events[].hostnamestring-Request hostname (host is an alias).
events[].timestampstringnowISO 8601 time of the hit.
bash
curl -X POST "https://app.numative.com/api/v1/collect" \
  -H "Authorization: Bearer nmv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "YOUR_SITE_ID",
    "events": [
      { "path": "/blog/post", "userAgent": "GPTBot/1.0", "ip": "20.15.240.64" }
    ]
  }'
Responsejson
{ "ingested": 1, "dropped": 0 }

POST /api/v1/ping

Instant single-page recrawl, for publish and deploy hooks. Requires a write-scoped key. The body is a single absolute url whose hostname must match one of your organization's sites (its domain, a subdomain, or an allowed host). See Change detection for where pings fit.

Body fieldTypeDefaultDescription
urlstringrequiredAbsolute http(s) URL of the page that changed.
bash
curl -X POST "https://app.numative.com/api/v1/ping" \
  -H "Authorization: Bearer nmv_live_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/blog/new-post"}'
Responsejson
{ "accepted": true, "path": "/blog/new-post" }

Returns 503 when the crawler is unavailable, so a hook can retry.

Annotations: /api/v1/annotations

Chart annotations (the emerald markers) can be managed from CI or a deploy pipeline. Listing works with any key; creating and deleting require a write-scoped key.

POST (create)

Body fieldTypeDefaultDescription
siteIdUUID-The site UUID. Either siteId or domain is required.
domainstring-The site's domain, as an alternative to siteId.
titlestringrequiredMarker title, at most 200 characters.
atstringnowISO 8601 timestamp the marker is placed at.
kindstringnoteOne of: deploy, campaign, email, content, note, spike_source, other.
bodystring-Optional longer description shown on hover.
urlstring-Optional link.
bash
curl -X POST "https://app.numative.com/api/v1/annotations" \
  -H "Authorization: Bearer nmv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.com",
    "kind": "deploy",
    "title": "v2.4.0 released",
    "url": "https://github.com/acme/site/releases/v2.4.0"
  }'

The created annotation row is returned as JSON.

GET (list)

GET /api/v1/annotations?siteId=<uuid> lists a site's annotations, newest first, up to 500. Optional from and to ISO 8601 timestamps bound the window. Note that this endpoint takes the site UUID, not the public snippet id.

DELETE

DELETE /api/v1/annotations?id=<uuid> removes one annotation and returns {"deleted": true}.