Everything the dashboard shows comes from the same API, and an API key reaches it from your own code. The API is described in OpenAPI:
- Reference: api.invisit.dev/openapi
- Machine-readable: api.invisit.dev/openapi/json
Authentication
Create a key under Settings → API keys. A key belongs to one site, not a person, and outlives whoever created it. Send it as a bearer token:
curl https://api.invisit.dev/v1/sites \
-H "Authorization: Bearer invisit_sk_..."Each key carries scopes that bound what it may do:
| Scope | Allows |
|---|---|
read |
Analytics, revenue and realtime. |
write |
Funnels, event definitions, annotations, links and feedback. |
manage |
The site’s settings. |
ingest |
Server events, crawler reads and proxied visitors. See the proxy. |
Give each key only what its job needs. A key that only proxies the tracker should carry ingest alone.
A first report
The site’s overview for October, compared with the month before:
curl "https://api.invisit.dev/v1/sites/<site id>/analytics/overview?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&compare=true" \
-H "Authorization: Bearer invisit_sk_..."Reports live under /v1/sites/{site_id}/analytics/: overview, timeseries, pages, sources, geography, devices, languages, outbound, custom-events, performance, crawlers, links, sessions, visits, visit-log and funnel among them. sessions counts engaged and bounced visits and their average active time. visit-log lists the visits that started in a range, newest first, with a next_cursor for the next page, and visits/{visit_id} returns one visit step by step at any age. Every report carries meta: its window, how fresh the data is, and whether it is exact.
Conventions
- Times. Instants are ISO-8601 UTC; ranges are half-open,
[from, to). Reports take atimezonefor day and hour boundaries. - Errors. One envelope everywhere:
{ "error": { "code", "message", "request_id", "details" } }. A validation failure listsdetails.issues[{ path, code, message }]. Quote therequest_idwhen you contact support. - Lists. Pages come as
items,next_cursorandhas_more, with list-wide context undermeta. - Caching. GET responses carry an
ETag; send it back inIf-None-Matchfor a304.
Rate limits
A key may make 50 requests a minute. Every answer reports the budget in RateLimit-* headers, and a refused request gets 429 with Retry-After. A read covers at most 400 days.
For assistants
Connecting an assistant? Use MCP instead of a key: it signs in as you, asks before it may write, and its tools wrap these endpoints in compact tables.