Docs/REST API

REST API

Read the analytics of every site in your account from your own scripts, dashboards or spreadsheets.

Authentication

Create a key in Settings → API keys. It is shown once; only a hash is stored, so a lost key is revoked and replaced, never recovered. Send it as a bearer token:

curl https://kipstats.com/api/v1/sites \
  -H "Authorization: Bearer ks_your_api_key"

A key reads every site of the account that created it. Revoking it takes effect immediately.

Base URL and format

https://kipstats.com/api/v1 — JSON in, JSON out, all dates in UTC. A machine-readable description lives at /api/v1/openapi.json (OpenAPI 3.1).

Rate limits

1000 requests per hour per key. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Over the limit the API answers 429 with a Retry-After header.

Errors

{
  "statusCode": 404,
  "statusMessage": "Site not found"
}
StatusMeaning
400A parameter is malformed — the message says which.
401Missing, invalid or revoked key.
404That site does not belong to this account.
429Rate limit exceeded.

Common parameters

ParameterValues
periodtoday, 24h, 7d (default), 30d, 90d, month, 12mo
from / toYYYY-MM-DD (whole days, UTC) or ISO 8601. Overrides period. 400 days max.
limit1 to 100 (breakdowns), default 10
country, browser, device, referrer, pageFilters. page matches entry pages containing the string.
traffichumans (interaction proven), bots (declared robots), all (default)

The {site} segment accepts whichever identifier you have at hand: the domain (example.com), the tracking ID (kp_1a2b3c4d) or the internal ID.

Endpoints

EndpointReturns
GET /sitesEvery site of the account.
GET /sites/{site}/statsVisitors, pageviews, sessions, bounce rate, average duration. compare=previous adds the previous period and the % change.
GET /sites/{site}/timeseriesThe same over time. Hourly up to 2 days, daily beyond. event=signup returns that event's series instead.
GET /sites/{site}/breakdown/{dimension}Top values of a dimension (below).
GET /sites/{site}/frictionRage clicks, dead clicks, JS errors and abandoned form fields, ranked.
GET /sites/{site}/realtimeVisitors active in the last 5 minutes.

Dimensions: pages, exit-pages, referrers, channels, countries, browsers, os, devices, events, utm_source, utm_medium, utm_campaign, utm_term, utm_content.

Filters apply to session-based dimensions. pages and events are counted from their own tables and ignore them — the numbers are right, they are just not filtered.

Examples

# last 30 days, compared to the 30 before
curl -H "Authorization: Bearer $KIPSTATS_API_KEY" \
  "https://kipstats.com/api/v1/sites/example.com/stats?period=30d&compare=previous"

# top pages of a custom range
curl -H "Authorization: Bearer $KIPSTATS_API_KEY" \
  "https://kipstats.com/api/v1/sites/example.com/breakdown/pages?from=2026-08-01&to=2026-08-31&limit=25"

# humans only, from France
curl -H "Authorization: Bearer $KIPSTATS_API_KEY" \
  "https://kipstats.com/api/v1/sites/example.com/stats?traffic=humans&country=FR"

Response shapes

GET /sites/example.com/stats?period=30d&compare=previous
{
  "site": "example.com",
  "period": { "from": "2026-08-17T09:12:03.000Z", "to": "2026-09-16T09:12:03.000Z" },
  "data": {
    "visitors": 4821,
    "pageviews": 11934,
    "sessions": 6210,
    "bounceRate": 47,
    "avgDuration": 96
  },
  "previous": {
    "period": { "from": "2026-07-18T09:12:02.999Z", "to": "2026-08-17T09:12:02.999Z" },
    "data": { "visitors": 4410, "pageviews": 10802, "sessions": 5730, "bounceRate": 51, "avgDuration": 88 }
  },
  "change": { "visitors": 9, "pageviews": 10, "sessions": 8, "bounceRate": -8, "avgDuration": 9 }
}
GET /sites/example.com/breakdown/channels?period=30d
{
  "site": "example.com",
  "period": { "from": "…", "to": "…" },
  "dimension": "channels",
  "total": 6210,
  "data": [
    { "channel": "Search", "sessions": 3120, "percent": 50.2 },
    { "channel": "Direct", "sessions": 1890, "percent": 30.4 },
    { "channel": "AI assistants", "sessions": 640, "percent": 10.3 }
  ],
  "aiAssistants": [
    { "source": "ChatGPT", "sessions": 402 },
    { "source": "Perplexity", "sessions": 238 }
  ]
}
GET /sites/example.com/friction?period=7d&limit=3
{
  "site": "example.com",
  "period": { "from": "…", "to": "…" },
  "data": {
    "rageClicks": [
      { "path": "/checkout", "element": "Pay now", "sel": "button.pay", "count": 34 }
    ],
    "deadClicks": [
      { "path": "/pricing", "element": "Compare plans", "sel": "span.link", "count": 12 }
    ],
    "jsErrors": [
      { "message": "Cannot read properties of null", "src": "/_next/static/chunk.js:1", "path": "/checkout", "count": 8 }
    ],
    "formAbandons": [
      { "path": "/signup", "form": "form#register", "field": "company_vat", "count": 21 }
    ],
    "totals": { "rageClicks": 4, "deadClicks": 9, "jsErrors": 2, "formAbandons": 6 }
  }
}

Writing

This API reads. To record a fact from your server — a payment, a webhook — use the ingest endpoint with the site's own key: server-side events.