[invisit](https://invisit.dev) · [Docs](https://invisit.dev/docs) · [llms.txt](https://invisit.dev/llms.txt) · [llms-full.txt](https://invisit.dev/llms-full.txt)

# REST API

> Read every report and manage sites, funnels, links and more over HTTP with a site API key. OpenAPI description, ETags and cursor pages.

Source: https://invisit.dev/docs/api/rest

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](https://api.invisit.dev/openapi)
- Machine-readable: [api.invisit.dev/openapi/json](https://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:

```sh title="Terminal"
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](https://invisit.dev/docs/server/sdk.md). |

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:

```sh title="Terminal"
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 a `timezone` for day and hour boundaries.
- **Errors.** One envelope everywhere: `{ "error": { "code", "message", "request_id", "details" } }`. A validation failure lists `details.issues[{ path, code, message }]`. Quote the `request_id` when you contact support.
- **Lists.** Pages come as `items`, `next_cursor` and `has_more`, with list-wide context under `meta`.
- **Caching.** GET responses carry an `ETag`; send it back in `If-None-Match` for a `304`.

## 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](https://invisit.dev/docs/api/mcp.md) instead of a key: it signs in as you, asks before it may write, and its tools wrap these endpoints in compact tables.
