API & MCP

Read every report and change every setting programmatically: API keys, authentication, rate limits, every /api/v1 endpoint, and the MCP server for AI agents.

The API exposes everything the dashboard shows and everything its settings screens change, over plain HTTPS + JSON: totals, time series and breakdowns for any site, every section of the dashboard as its own endpoint, and write endpoints for sites, settings, data sources, goals, funnels, segments, alerts, annotations, the team and API keys. All endpoints live under https://app.numative.com/api/v1/, and GET https://app.numative.com/api/v1 returns a machine-readable index of every one of them with its parameters.

The same surface is available to AI agents as an MCP server: one tool per endpoint, so an agent with a key can answer questions about your traffic or change a setting for you. The dashboard the WordPress plugin renders inside wp-admin is built entirely on these endpoints, so anything it shows can be rebuilt anywhere.

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): every reporting endpoint and every settings read. It can answer any question the dashboard can, and change nothing.
  • write: everything a read key can do, plus every endpoint that changes something: adding and deleting sites, site settings, data sources, goals, funnels, segments, alerts, annotations, publish pings, server-side collection, the team and key revocation.

A key belongs to your organization and can access every site in it. The one thing no key can do is mint another key: new keys come from this page only, so a leaked key can always be revoked for good.

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).

The curl examples on this page break long commands over lines with a trailing backslash, which bash and zsh understand. In PowerShell, join the lines (or use a backtick instead), and call curl.exe: in Windows PowerShell, curl alone is an alias for Invoke-WebRequest.

Connect an AI agent (MCP)

Numative is a Model Context Protocol server. Any MCP client that can send a bearer token (Claude Code, Codex, Cursor, Claude Desktop and claude.ai, your own agent) can connect to it with an API key and gets one tool per endpoint on this page: a read key gives the reporting and settings-read tools, a write key adds every tool that changes something. The agent sees only the tools its key allows, and every tool call is the corresponding API call, with the same checks and the same rate limit.

SettingValue
Server URLhttps://app.numative.com/api/mcp
TransportStreamable HTTP (stateless, JSON responses)
HeaderAuthorization: Bearer nmv_live_...
AuthenticationBearer token only. No OAuth yet, so clients that require OAuth (ChatGPT) cannot connect.

Claude Code

shell
claude mcp add --transport http numative https://app.numative.com/api/mcp --header "Authorization: Bearer nmv_live_..."

Or in .mcp.json (project) or your user settings, with the key read from an environment variable so it is never committed:

json
{
  "mcpServers": {
    "numative": {
      "type": "http",
      "url": "https://app.numative.com/api/mcp",
      "headers": { "Authorization": "Bearer ${NUMATIVE_API_KEY}" }
    }
  }
}

Codex

Codex reads the token from an environment variable rather than the config file. Export NUMATIVE_API_KEY, then either run:

bash
codex mcp add numative --url https://app.numative.com/api/mcp --bearer-token-env-var NUMATIVE_API_KEY

or add the server to ~/.codex/config.toml (shared by the CLI and the IDE extension):

toml
[mcp_servers.numative]
url = "https://app.numative.com/api/mcp"
bearer_token_env_var = "NUMATIVE_API_KEY"

Cursor

In ~/.cursor/mcp.json (every project) or .cursor/mcp.json (one project). Cursor resolves ${env:NAME} in url and headers, so the key can stay out of the file:

json
{
  "mcpServers": {
    "numative": {
      "url": "https://app.numative.com/api/mcp",
      "headers": { "Authorization": "Bearer ${env:NUMATIVE_API_KEY}" }
    }
  }
}

Claude Desktop and claude.ai

Remote servers are added as a custom connector, not through a config file. Go to Customize > Connectors > Add custom connector (Team and Enterprise owners: Organization settings > Connectors > Add > Custom), enter the server URL, set Authentication to None, and under Request headers add Authorization with the value Bearer nmv_live_..., including the word Bearer and the space. Request headers are currently in beta for a limited set of organizations; if the section is not in your dialog, use the bridge below in Claude Desktop's config file instead.

claude_desktop_config.json (bridge, when Request headers are unavailable)json
{
  "mcpServers": {
    "numative": {
      "command": "npx",
      "args": ["mcp-remote", "https://app.numative.com/api/mcp", "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer nmv_live_..." }
    }
  }
}

The header is written without a space after the colon on purpose: some clients mangle spaces inside args, and the environment variable carries the value intact.

ChatGPT

ChatGPT's developer mode accepts remote MCP servers that use OAuth or no authentication, and cannot send a fixed API key. Numative's server authenticates with a key, so ChatGPT cannot connect to it today. Call the REST API from a ChatGPT action or your own code instead.

The tools are named after what they answer or change: whoami, list_sites, get_stats, get_breakdown, list_pages, get_page_detail, get_anomalies, list_changes, get_errors, get_vitals, get_search_console, get_backlinks, update_site_settings, create_goal, update_alert_settings, invite_member, and so on. Each tool's description and parameters are the endpoint's, so the rest of this page is the tool reference too. The full list, with parameters, is at GET https://app.numative.com/api/v1.

Tools that cannot be undone (deleting a site, a goal, a funnel, a member, revoking a key, rotating an ingest key) are marked destructive in their MCP annotations, and deleting a site additionally requires the domain to be repeated as confirmation. Give an agent a read key unless you want it to change things.

Rate limits

EndpointLimit per key
Every read and settings endpoint, and MCP tool calls600 requests / minute
collect600 requests / minute
ping60 requests / minute
proxy-config60 requests / minute

A view that needs several endpoints should fetch them in parallel and cache the result rather than polling: the dashboard's own numbers change at the speed of the underlying data, which for everything except realtime is minutes at best.

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).
sourcestringthe site's primarystandalone or ga4, for a site that has both. Every response echoes the source it read, so a caller storing the numbers knows what they are. Asking for a source the site does not have is a 400, not a silent fallback.
limitnumber100Max rows returned by list endpoints, 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.

range=realtime spans two windows, and every response says which one it used. stats and breakdown count the last 5 minutes, the visitors on the site right now. timeseries returns the last 30 minutes, minute by minute, so the recent traffic has a shape to read.

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. Reading first-party data adds goal, link, outbound, affiliate, button, dead_click, form, bot_kind and collection_source; GA4 has no field behind those, so its reader rejects rather than silently ignoring them.

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, or use the keyless live endpoint below for the one number a public page usually wants.

Live visitor badge

One endpoint takes no key at all: a visitor count, for showing "12 reading now" or "48,120 visitors this year" on your own site. Turn it on per site under Site settings > Visibility > Public visitor counts, which is also where the snippet below is generated with your site id, period and metric already in it.

Beside the switch is a dial: anyone may ask for. It is the one thing that bounds what the endpoint will answer, and it is not the same as the period on your badge. Your site id is in your page's markup and the period is a URL parameter, so a badge set to "last 30 days" withholds nothing from someone who edits the URL. The dial does.

Anyone may ask forAnswers
Live onlyWho is on the site right now, and nothing else. The default.
Up to the last 30 daysWindows starting within the last 30 days: today, 7d, this month, 30d.
Up to the last yearAdds 90d, this year, year to date, last 12 months.
Any period, including all timeEvery window, all time and any custom range included.

A window past the dial answers exactly as an unpublished site does, and that includes custom ranges: the limit is measured on the window itself, never on the range's name.

Drop this where the number should appearhtml
<span data-numative-live="YOUR_SITE_ID">–</span> reading now

<span data-numative-live="YOUR_SITE_ID" data-numative-range="30d">–</span> visitors in the last 30 days

<span data-numative-live="YOUR_SITE_ID" data-numative-range="all"
      data-numative-metric="pageviews">–</span> pageviews all time

<script async src="https://app.numative.com/live.js"></script>

Each element's text becomes its count, formatted with thousands separators, and refreshes every 15 seconds while the page is in front of someone. The script writes nothing else, imposes no styling, and sets no cookie or storage: it reads a public number, it does not track your readers. Badges asking the same question share one request, so a page can carry as many as it likes.

Badge attributes

AttributeOnDescription
data-numative-livethe elementYour site's public id, the same data-site value the tracking snippet uses.
data-numative-rangethe elementThe period to count: today, yesterday, 7d, week, month, 30d, 90d, year, ytd, last-year, 12mo, all, or custom. Omit it to count who is on the site right now.
data-numative-from / -tothe elementYYYY-MM-DD bounds, with range=custom. Resolved in the site's timezone; the end day is included.
data-numative-metricthe elementvisitors (default), visits or pageviews. Period badges only; the live count is always visitors.
data-numative-windowthe elementLive badges only: 5m (default) counts the last 5 minutes, 30m the last 30. Both numbers arrive in the same response, so badges with different windows on one page still cost one request.
data-numative-statethe elementWritten by the script: loading, ok, error, or off (the site does not publish that count). Style it away with CSS if you would rather show nothing than a dash.
data-hostthe script tagAPI origin. Defaults to wherever live.js was loaded from, so serving it from your own tracking domain needs no configuration.
data-intervalthe script tagSeconds between refreshes, clamped to 5-300. Default 15.

If you have a custom tracking domain, load live.js from it and the badge polls that domain too, so an ad blocker cannot leave a dash on your home page.

GET /api/public/live

The badge is a convenience over one endpoint. Call it yourself if you want your own markup, a server-rendered count, or a number in something that is not a web page.

bash
curl "https://app.numative.com/api/public/live?site=YOUR_SITE_ID"
curl "https://app.numative.com/api/public/live?site=YOUR_SITE_ID&range=30d"
curl "https://app.numative.com/api/public/live?site=YOUR_SITE_ID&range=custom&from=2026-01-01&to=2026-06-30"
Response (no range: the live windows)json
{
  "site": "YOUR_SITE_ID",
  "range": "5m",
  "from": "2026-08-20T22:47:00.000Z",
  "to": "2026-08-20T22:52:00.000Z",
  "visitors": 12,
  "visits": 14,
  "pageviews": 23,
  "halfHour": 47
}

Without a range, visitors counts the last 5 minutes (the same number the dashboard shows live, and the same one /api/v1/realtime returns as now) and halfHour counts the last 30. With one, the counts cover that period and halfHour is absent. range takes the same values as the keyed API, plus 5m and 30m for the live windows; custom needs from and to as YYYY-MM-DD. Every count is humans only, read from the site's primary source.

Responses send Access-Control-Allow-Origin: * and are cacheable for 10 seconds live, 60 for a period. Unlike the rest of the API a bad range is a 400 rather than a quiet fallback: a public caller asking for one window must never be handed another without being told.

This endpoint answers with counts and nothing else. No pages, no sources, no countries, no engagement. A site that publishes nothing, a window past the site's dial, and a site id that does not exist all return the same 404, so nobody can use it to test whether a domain is tracked here or how far back its history goes. A 429 means the site has been asked for more distinct windows this minute than it will answer fresh; already-cached windows keep being served.

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>.

property=goal ranks the site's goals rather than a stored field: each row is a goal, visitors is its unique converters and events is how many times it fired, repeated as conversions. A goal entry in filters is visit-scoped, narrowing every endpoint to the visits that converted it. Both are collector-only, so a source=ga4 read returns no goal rows.

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 }
  ]
}

bounceRate and avgDuration describe the visits each row counts. Dimensions that don't measure visit engagement (custom events, goals, interactions, properties) omit both fields rather than reporting a fabricated 0.

GET /api/v1/realtime

Live visitors in both windows the app shows: now counts the last 5 minutes, halfHour the last 30. Also returns the 30-minute minute-grain series and the pages and sources currently active. The range is always realtime whatever you pass.

GET /api/v1/markers

Everything the dashboard flags on the traffic chart for the window, grouped by kind: detected page and site changes, Core Web Vitals regressions, backlink movement, algorithm updates, social mentions and your annotations. This is the differentiator as data, so an embedded chart can plant the same flags rather than drawing a bare line. Not source-scoped (a change is a fact about the site), but it honors the same range so the flags line up with the series you charted.

GET /api/v1/anomalies

Per-page movers for the window against the period before it: which pages rose, fell, appeared or went quiet, and by how much, each with the detected changes that might explain it. Defaults to the last 7 days. See Anomalies.

GET /api/v1/changes

The detected-change feed: page and site changes plus annotations, newest first, with per-type totals and a daily activity strip. Optional category, type, search and day filters.

GET /api/v1/changes/diff?change_id=<id> returns the word-level before and after behind one page change, computed from the stored crawl texts. Changes whose diffable flag is false predate the stored text and return 422.

GET /api/v1/goals

Conversions, conversion rate and revenue for each of the site's goals over the window. Collector-only, so a GA4-only site answers available: false.

GET /api/v1/funnels

The site's saved funnels, and the computed result for one of them: funnel_id picks which, else the first. Collector-only.

GET /api/v1/properties

The custom property keys seen in the window, and the value breakdown for one of them when property is given. Collector-only.

GET /api/v1/flow

The behavior-flow graph for the window, as nodes and links. Pass focus_path for the bidirectional path-explorer view or start_path to root a forward flow; depth sets how many steps. Collector-only.

GET /api/v1/journeys

Recent visitor journeys, newest first: one row per visitor per site-local day, with where they landed, where they left, how long they stayed and how much they did. Pass visitor=<id> for that visitor's full event stream. The id is a daily-rotating, non-reversible hash: it identifies a day's activity, not a person, and cannot be joined across days or sites. Collector-only.

GET /api/v1/activity

The live tape: one row per thing a visitor just did, newest first. Page loads, autocaptured clicks, custom events, the leave-time engagement report and captured errors, each carrying the visitor hash that did it and, as source, where that visitor entered from that day (their first pageview's referrer, empty for direct). window=5m|30m picks the realtime window (30m by default). Collector-only.

GET /api/v1/vitals

Real-user Core Web Vitals for the window: the p75 summary and experience score, the trend, and the same per-page/country/device/browser/OS tables the Speed section ranks. Pass attribution=1 for what is dragging the numbers down (LCP elements, layout-shift sources, blocking scripts), form_factor=phone|desktop to narrow to one device class, and percentile=50|75|90|99 to pick the aggregate. Measured by the snippet, so it is collector-only.

GET /api/v1/search

Search Console for the window: clicks, impressions, CTR and average position, plus top queries and pages. Returns connected: false when the site has no Search Console connection.

The site's backlink profile in one call: stored counts, 30-day movement, top referring domains, top links, and the newest new/lost/regained events. Not source-scoped, since a site can have backlinks with no analytics. limit caps each list.

GET /api/v1/decay

Pages whose traffic is fading: peak versus current 28-day visits, the site-adjusted change, a diagnosis, and whether a refresh has been detected. Whole-site current state, so it takes no range.

GET /api/v1/site

The site's own facts, plus the billing and quota notices the app shows above the dashboard. An embedded dashboard that never learns the account is locked would otherwise show a frozen, silently stale view.

GET /api/v1/proxy-config

The site's first-party proxy parameters, for integrations that set the proxy up unattended. Requires a write-scoped key, because the response contains the proxy secret, which is exactly what a read key must never see. A site-bound ingest key resolves its own site; an org-wide write key passes ?site_id=.

GET /api/v1/errors

Client-side error groups over the window (see Error tracking): headline totals, the occurrence timeseries, and every distinct error with its stack trace. Add fingerprint=<id> for one group's expanded detail (per-page/browser/OS/device/country breakdowns and recent occurrences). Standalone-tracked sites only.

bash
curl "https://app.numative.com/api/v1/errors?site_id=SITE_ID&range=7d" \
  -H "Authorization: Bearer nmv_live_..."
Response (truncated)json
{
  "available": true,
  "summary": { "errors": 214, "groups": 6, "visitors": 102 },
  "groups": [
    {
      "fingerprint": "9f2c1a77b3e4d015",
      "kind": "error",
      "errorName": "TypeError",
      "message": "Cannot read properties of undefined (reading 'map')",
      "count": 121, "visitors": 64, "paths": 3,
      "topPath": "/pricing", "topBrowser": "Chrome",
      "firstSeen": "2026-07-14T09:12:44.000Z", "lastSeen": "2026-07-17T11:03:19.000Z"
    }
  ]
}

GET /api/v1/clicks

Autocaptured clicks over the window (see Click autocapture), grouped by the element they landed on. Honours filters and limit. Standalone-tracked sites only.

bash
curl "https://app.numative.com/api/v1/clicks?site_id=SITE_ID&range=30d&limit=10" \
  -H "Authorization: Bearer nmv_live_..."
Response (truncated)json
{
  "available": true,
  "summary": { "clicks": 5120, "visitors": 1288, "deadClicks": 96 },
  "clicks": [
    {
      "selector": "button.subscribe", "text": "Subscribe", "tag": "button",
      "url": "", "interactive": true,
      "count": 512, "visitors": 380, "topPath": "/blog", "paths": 12
    }
  ]
}

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 bot events are never billed. 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}.

More reads

The rest of the dashboard, each as its own endpoint. All take the common parameters unless noted.

EndpointWhat it returns
GET /api/v1/pagesEvery tracked page with its status, launch date, and visitors, pageviews, scroll depth and time on page over the window. filter=all|active|removed|new, sort, dir, q (search the whole inventory), trends=1 for sparklines, bots=1, limit, offset.
GET /api/v1/pages/launchesPages that went live in the window, measured over their own first 30 days (measure=launch) or a shared window (measure=30d ...), each banded over, median or under the cohort, plus the cohort's traffic shape.
GET /api/v1/pages/detailOne page's whole story by path= or page_id=: its traffic series, why it moved (drivers by source or another dimension), ranked root causes with confidence, engagement, first-30-day traffic, change history and crawl snapshots, its errors, real-user and CrUX vitals with bottlenecks, and the search queries it ranks for.
GET /api/v1/seoThe Rankings report: every Search Console landing page against the previous window with a diagnosis of any drop and the detected page changes that might explain it.
GET /api/v1/search?dimension=query|page&value=One query's or landing page's daily search trend.
GET /api/v1/anomalies?vitals=1Core Web Vitals regressions against the baseline window, with the element or script behind each.
GET /api/v1/errors?fingerprint=&pages=1The pages one error group fired on, paginated with offset. The plain errors read also carries previous-window totals and per-group sparklines.
GET /api/v1/vitals?crux=1Adds Google's Chrome UX Report field data beside the real-user numbers.
GET /api/v1/geoThe map's points: visitors per region or city (granularity=region|city) with coordinates.
GET /api/v1/ga4-eventsThe Google Analytics property's events over the window; builtin=1 keeps GA4's automatic events.
GET /api/v1/spikesDetected traffic spikes in the window and the source the spike hunter found for each. POST with spike_id, url, title attaches a source by hand.
GET /api/v1/segmentsSaved segments. POST creates a shared one (site_id, name, filters); DELETE ?site_id=&id= removes one.
GET /api/v1/exportTotals, the timeseries and every breakdown dimension at up to 10,000 rows each: the dashboard's Export as JSON.
GET /api/v1/dimensionsEvery breakdown property and filter dimension, the operators, the named ranges. Static.
GET /api/v1/backlinksNow also returns the site's Domain Rating trend, filtered totals, offset paging, and the anchor-text, link-type and TLD distributions.

Sites and settings

Everything under a site's Settings, and the Sites list itself. Reads work with any key; writes need a write key, and adding or deleting a site needs an org-wide one (not a site-bound ingest key). Site ids in the path accept the UUID or the public snippet id.

EndpointWhat it does
GET /api/v1/sitesEvery site with the Sites list's reading for the window (range, default 7d): visitors, visits, pageviews, bounce rate, duration, the previous window, a sparkline and the install state. stats=0 for just the rows.
POST /api/v1/sitesAdd a site: { domain, sources?, primarySource?, timezone? }. Returns the site with its snippet. Queues the first crawl and backlink onboarding.
GET /api/v1/sites/{site_id}Every setting and status: tracking rules, exclusions, captured query parameters, affiliate paths, bot tracking, custom domain, change-detection exclusions and crawl status, sharing, appearance, integrations, alerts, install state, imported periods.
PATCH /api/v1/sites/{site_id}Change settings; send only the fields to change. Any field GET returns under tracking, changeDetection, sharing and appearance, flat by name, plus standaloneEnabled and primarySource. Returns the updated site.
DELETE /api/v1/sites/{site_id}?confirm={domain}Delete the site and all its data. Irreversible; the domain must be repeated.
GET|POST /api/v1/sites/{site_id}/installWhether the snippet is installed and recording; POST fetches the homepage now and looks for the tag.
GET|PATCH|DELETE /api/v1/sites/{site_id}/ga4The Google Analytics connection and the properties the account can read; PATCH { propertyId } binds another; DELETE disconnects. Connecting itself is a browser OAuth step (connectUrl).
GET|PATCH|DELETE /api/v1/sites/{site_id}/search-consoleThe Search Console connection; PATCH { siteUrl } binds another property; DELETE disconnects.
GET|PATCH /api/v1/sites/{site_id}/alertsAlert and email report settings: weekly/monthly reports, spike and drop alerts and thresholds, backlink and vitals alerts, recipients, webhook, measured source. PATCH merges.
GET|POST|DELETE /api/v1/sites/{site_id}/custom-domainThe custom tracking domain and its DNS status; POST { domain } attaches a subdomain of the site; DELETE detaches.
GET|POST /api/v1/sites/{site_id}/ingest-keyWhether a server-side ingest key exists; POST mints or rotates it and returns the secret once.
GET /api/v1/sites/{site_id}/importsData imports and the periods of history they cover.
POST|PATCH|DELETE /api/v1/goalsDefine, redefine or delete a goal (site_id, goal_id, name, type event|page, matchValue, currency?). GET now includes the definitions.
POST|PATCH|DELETE /api/v1/funnelsSave, replace or delete a funnel (site_id, funnel_id, name, steps of { type, value, label? }).
Turn on bot tracking and make the dashboard publicbash
curl -X PATCH "https://app.numative.com/api/v1/sites/SITE_ID" \
  -H "Authorization: Bearer nmv_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "includeBots": true, "isPublic": true, "shareLevel": "link" }'

Organization

EndpointWhat it does
GET /api/v1/meWhat this key is: scope, organization, site binding, plan, and what it may do. Call it first.
GET /api/v1/orgThe organization: plan, limits and features, subscription and trial state, this month's usage and its breakdown by event type and site, billing period, team size.
GET|POST|PATCH|DELETE /api/v1/org/membersThe team and pending invitations; POST { email, role } invites; PATCH { member_id, role } changes admin/member; DELETE ?member_id= or ?invitation_id= removes or cancels. Owner roles stay browser-only.
GET|DELETE /api/v1/org/api-keysEvery key's name, prefix, scope and last use; DELETE ?id= revokes one. Keys are created from Account > API only.