API reference

Base URL: https://analyticsapi.specvista.com/api/v1. Bodies are JSON. The machine-readable contract is the OpenAPI document, which is checked against the API's routes in its test suite.

Authentication

EndpointsHeader
/ingest, /ingest/identify, /ingest/batch, /sdk/linked-domainsX-Api-Key: <project key>. Browsers may send X-Domain: <hostname> instead, for a web property with that domain
/forgetX-Api-Key with a backend property's key
/auth/me, /auth/password, /auth/profile, /projects, /analytics/...Authorization: Bearer <token>, where the token is user.token from /auth/login or /auth/register
/auth/login, /auth/register, /sdk.js, /openapi.json, /ingest/ignore-*None

Send events

POST /ingest

Stores one event, usually within a second. Returns 202 {"ok":true} straight away; processing happens afterwards, so an event that fails to store is dropped silently rather than rejected, and there is no way to see dropped events yet.

FieldTypeNotes
eventstringFor example signup.completed. Defaults to pageview. The API stores up to 100 characters and does not check the format; the Node.js SDK only sends names matching ^[a-z][a-z0-9._:-]{1,79}$
sourceweb, mobile or backendDefaults to web. Servers must send backend
userIdstringYour user id, up to 255 characters
sessionIdstringUp to 100 characters
propertiesobjectYour own structured fields. No secrets or free text
environmentdevelopment, preview or productionAnything except production is stored as internal and left out of reports
consentfull or essentialessential stores only the event, path, referrer, device type and country
url, path, referrerstringWeb context. UTM parameters are read from url
os, osVersion, appVersion, appBuild, deviceModelstringApp context
deviceType, browser, language, locale, timezone, screenWidth, screenHeightDevice context
utmSource, utmMedium, utmCampaign, utmContent, utmTermstringOverride values read from url
anonymousId, installId, deviceIdstringVisitor and install identifiers

Country, region and city come from the request IP address. Events whose user agent looks like a bot or script (crawlers, headless browsers, Playwright, Puppeteer, Selenium, curl, wget, python-requests, Go's HTTP client, uptime monitors) are stored as bot traffic unless source is backend. The default user agents of mobile apps (okhttp on Android, CFNetwork on iOS) are not. Events from Apple's network (17.0.0.0/8, which App Review uses) are stored as internal. Bot and internal events are left out of every report.

There is no idempotency key yet: a retried request stores a second event.

POST /ingest/batch

A JSON array of events, or {"events":[...]}. At most 100 are stored per request. Returns 202 {"ok":true,"queued":<number received>}.

POST /ingest/identify

{"userId":"user_123","email":"person@example.com","name":"Ada","traits":{"plan":"pro"}}. Attaches an identity to a user id so people show by name. Blank fields never overwrite stored ones. Add "emailStorage":"hash_only" to store only a salted hash of the email. Returns 202 {"ok":true}, or 202 {"ok":false,"error":"userId required"}.

POST /forget

{"userId":"user_123"} with a backend key, and optionally "projectId" for another property owned by the same account. Deletes the stored identity and strips the user id and personal properties from that user's events. Returns 204. Permanent. Limited per key per minute. Sending the event account.deleted with a userId from a backend key does the same.

GET /ingest/ignore-on, /ingest/ignore-off, /ingest/ignore-status

Set, clear or read the sa_ignore cookie in the calling browser. While it is set, ingest returns {"ok":true,"skipped":true} and stores nothing.

Account

Method and pathBodyResult
POST /auth/registeremail, password (8 or more characters), firstName, lastName201 {"user":{...,"token":"..."}}
POST /auth/loginemail, password201 {"user":{"id","uuid","email","firstName","lastName","token"}}
GET /auth/meThe account
PATCH /auth/passwordcurrentPassword, newPassword{"ok":true}
PATCH /auth/profilefirstName, lastNameThe account with a fresh token

Tokens last 90 days and cannot be revoked early; changing the password does not end existing tokens.

Properties

Method and pathBodyResult
GET /projectsYour properties, newest first, each with id, name, slug, apiKey, domain, type, createdAt
POST /projectsname (required), domain, type (web, mobile, backend, other)201 and the property
GET /projects/{id}The property
PATCH /projects/{id}name, domainThe property
DELETE /projects/{id}{"ok":true}
POST /projects/{id}/rotate-key201 and the property with a new apiKey. The old key stops at once

Reports

All GET, with a bearer token, period of 24h, 7d or 30d (default 30d). Bot and internal traffic is excluded.

PathReturns
/analytics/globalPer property: live, pageviewsToday, pageviewsPeriod; plus totals
/analytics/{id}/overviewtotal, pageviews, uniqueSessions, liveVisitors, trend, previous
/analytics/{id}/pageviews[{date, views}]; granularity is day, week or month
/analytics/{id}/top-pagesTop 10 [{path, views, sessions}]
/analytics/{id}/landing-pages{sessions, pages:[{path, sessions, bounced, bounceRate}]}
/analytics/{id}/top-eventsTop 10 non-pageview [{event, source, count}]
/analytics/{id}/event-namesUp to 50 [{event, count}]
/analytics/{id}/funnel?from=&to={fromEvent, toEvent, period, current, previous}, each with started, converted, rate
/analytics/{id}/referrersTop 10 [{referrer, visits}]
/analytics/{id}/channels[{channel, count}]: Organic, Direct, Referral, Social, Email, Paid
/analytics/{id}/utm-campaignsTop 20 [{campaign, source, medium, visits}]
/analytics/{id}/sources, /devices, /browsers, /os[{<field>, count}]
/analytics/{id}/countriesTop 50 [{countryCode, country, visits}]
/analytics/{id}/citiesTop 20 [{city, region, countryCode, country, visits}]
/analytics/{id}/bounce-rate{bounceRate} as a whole percentage
/analytics/{id}/new-vs-returning{new, returning} among identified users
/analytics/{id}/peopleUp to 500 user ids active in the period, with email and name where identified
/analytics/{id}/live-eventsUp to 20 events from the last hour
/analytics/{id}/exportCSV of up to 50,000 events
/analytics/tracking-healthWhether each property is still receiving events

Other

  • GET /sdk.js and GET /sdk.min.js: the browser script.
  • GET /sdk/linked-domains: domains of the same account, used by the script to link visitors across your sites.
  • GET /openapi.json: the OpenAPI 3.0 document.

Errors

Every error code, and what to do about it, is in Errors.