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
| Endpoints | Header |
|---|---|
/ingest, /ingest/identify, /ingest/batch, /sdk/linked-domains | X-Api-Key: <project key>. Browsers may send X-Domain: <hostname> instead, for a web property with that domain |
/forget | X-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.
| Field | Type | Notes |
|---|---|---|
event | string | For 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}$ |
source | web, mobile or backend | Defaults to web. Servers must send backend |
userId | string | Your user id, up to 255 characters |
sessionId | string | Up to 100 characters |
properties | object | Your own structured fields. No secrets or free text |
environment | development, preview or production | Anything except production is stored as internal and left out of reports |
consent | full or essential | essential stores only the event, path, referrer, device type and country |
url, path, referrer | string | Web context. UTM parameters are read from url |
os, osVersion, appVersion, appBuild, deviceModel | string | App context |
deviceType, browser, language, locale, timezone, screenWidth, screenHeight | Device context | |
utmSource, utmMedium, utmCampaign, utmContent, utmTerm | string | Override values read from url |
anonymousId, installId, deviceId | string | Visitor 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 path | Body | Result |
|---|---|---|
POST /auth/register | email, password (8 or more characters), firstName, lastName | 201 {"user":{...,"token":"..."}} |
POST /auth/login | email, password | 201 {"user":{"id","uuid","email","firstName","lastName","token"}} |
GET /auth/me | The account | |
PATCH /auth/password | currentPassword, newPassword | {"ok":true} |
PATCH /auth/profile | firstName, lastName | The 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 path | Body | Result |
|---|---|---|
GET /projects | Your properties, newest first, each with id, name, slug, apiKey, domain, type, createdAt | |
POST /projects | name (required), domain, type (web, mobile, backend, other) | 201 and the property |
GET /projects/{id} | The property | |
PATCH /projects/{id} | name, domain | The property |
DELETE /projects/{id} | {"ok":true} | |
POST /projects/{id}/rotate-key | 201 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.
| Path | Returns |
|---|---|
/analytics/global | Per property: live, pageviewsToday, pageviewsPeriod; plus totals |
/analytics/{id}/overview | total, pageviews, uniqueSessions, liveVisitors, trend, previous |
/analytics/{id}/pageviews | [{date, views}]; granularity is day, week or month |
/analytics/{id}/top-pages | Top 10 [{path, views, sessions}] |
/analytics/{id}/landing-pages | {sessions, pages:[{path, sessions, bounced, bounceRate}]} |
/analytics/{id}/top-events | Top 10 non-pageview [{event, source, count}] |
/analytics/{id}/event-names | Up to 50 [{event, count}] |
/analytics/{id}/funnel?from=&to= | {fromEvent, toEvent, period, current, previous}, each with started, converted, rate |
/analytics/{id}/referrers | Top 10 [{referrer, visits}] |
/analytics/{id}/channels | [{channel, count}]: Organic, Direct, Referral, Social, Email, Paid |
/analytics/{id}/utm-campaigns | Top 20 [{campaign, source, medium, visits}] |
/analytics/{id}/sources, /devices, /browsers, /os | [{<field>, count}] |
/analytics/{id}/countries | Top 50 [{countryCode, country, visits}] |
/analytics/{id}/cities | Top 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}/people | Up to 500 user ids active in the period, with email and name where identified |
/analytics/{id}/live-events | Up to 20 events from the last hour |
/analytics/{id}/export | CSV of up to 50,000 events |
/analytics/tracking-health | Whether each property is still receiving events |
Other
GET /sdk.jsandGET /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.