docs / reference

API reference

The ingest API that every producer calls, and the read API behind the dashboard. Everything is under the /v1 prefix at app.knockstat.com.

Ingest API

EndpointCallerKeyBodySuccess
POST /v1/eBrowser script and packagesSite key, in the body as kOne event, JSON sent as text/plainAlways 204
POST /v1/batchServers, the agentIngest key, in a header{"events": [...], "sent_at": "..."}202 + per-event result
POST /v1/logsVector, Fluent Bit, ad-hoc shippersIngest key, in a headerRaw log lines, one per line202 + per-line result

The two key kinds are explained in site key and ingest key. The ingest key is sent as Authorization: Bearer ksi_.... The browser script manages its own requests; most people never call /v1/e directly.

POST /v1/e

The browser beacon, sent with navigator.sendBeacon so there is no CORS preflight. Maximum body 16 KiB. The body is an event plus the site key:

{
  "k": "kst_xxxx",
  "uuid": "0192f3b0-7c1a-7000-8000-5d1e8f3a9b22",
  "event": "$pageview",
  "request": { "host": "example.com", "path": "/pricing/?utm_source=hn", "referrer": "https://news.ycombinator.com/" },
  "properties": { "$screen_width": 1440 }
}

The answer is always 204 with an empty body, including for an unknown key, a disallowed origin, an invalid event or a rate limit: a beacon cannot act on an error, and a uniform answer does not reveal which keys exist. If the site has allowed origins, a request whose Origin is not on the list is dropped. Ingest takes the user agent from the request and the visitor’s IP from the connection, behind Cloudflare from its headers.

POST /v1/batch

Events as JSON, up to 1,000 per request, gzip accepted. Fields, a worked curl and retries are in Server events and the HTTP API. The answer lists accepted and every rejection with its index, field and error.

POST /v1/logs

Raw access-log lines, one per \n, parsed with the same parsers as the agent. Give the format in ?format= or the X-Knockstat-Format header (values in Log formats). Each parsed line becomes a $request event whose id comes from the line, so a resent request does not double-count. At most 5,000 lines per request; index in the answer is the zero-based line number, and an unparseable line is rejected with field: "line".

curl -sS "https://app.knockstat.com/v1/logs?format=combined_host" \
  -H "Authorization: Bearer $KNOCKSTAT_INGEST_KEY" \
  -H "Content-Type: text/plain" \
  --data-binary @access.log

With ?envelope=vector or ?envelope=fluentbit (or the X-Knockstat-Envelope header) the body is the shipper’s JSON records, one per line or as an array, and ingest reads the line from message (Vector) or log, else message (Fluent Bit). The envelope is never guessed. See Vector and Fluent Bit.

Limits

LimitValueOver the limit
Events per batch1 to 1,000Batch rejected (400)
Body size1 MiB compressed, 8 MiB once decompressed413
Log lines per /v1/logs request5,000400
Event name1 to 200 characters, no control charactersEvent rejected
Properties per event64Event rejected
Property key1 to 128 charactersEvent rejected
Property valuestring (at most 1,024 characters), finite number, boolean or null; no objects or arraysEvent rejected
distinct_id200 charactersEvent rejected
timestampAt most 10 minutes ahead of ingest's clock, after sent_at correctionEvent rejected
request.path, referrer, user_agent, host4,096, 2,048, 1,024 and 253 charactersTruncated, not rejected

Characters are Unicode scalar values, not bytes.

Status codes

CodeMeaningWhat to do
202Accepted. The body lists accepted and rejected.Drop the accepted events from your buffer.
204Beacon received, whatever the outcome (/v1/e only).Nothing.
400Body is not valid JSON, the batch is empty or too large, or the format is unknown.Fix the request. Do not retry it unchanged.
401Missing, unknown or revoked ingest key.Stop and check the key. Do not retry.
413Body over the size limit.Split the batch and resend.
415Unsupported Content-Encoding.Send gzip or no encoding.
429Over the key's rate limit (rate_limited), or the organization is past its plan's monthly quota (quota_exceeded). Retry-After gives seconds.Keep the events, wait at least Retry-After, resend.
503Ingest cannot store events right now. Retry-After is given.Back off with jitter, keep events buffered, resend.

Errors other than beacons share one body shape, where error is one of invalid_body, batch_too_large, unknown_format, unauthorized, payload_too_large, unsupported_encoding, rate_limited, quota_exceeded or unavailable:

{ "error": "rate_limited", "message": "ingest key ksi_7c0e… is over 500 requests per second" }

Rate limits

Limits apply per key, as token buckets counted in events: a batch of 500 events costs 500 tokens, a beacon costs 1, and a /v1/logs call costs one per line. These are the defaults; your plan can change them.

KeySustainedBurst
Site key (kst_)100 events/s1,000
Ingest key (ksi_)5,000 events/s50,000

A beacon over its limit is dropped silently. A batch or logs call over its limit gets 429 with rate_limited and a Retry-After in whole seconds. Nothing in a refused call is stored, so resend the same batch.

Monthly quota

A plan may cap events per calendar month (UTC), with a grace above the cap. Past the cap events are still stored. Past the grace, a batch or logs call gets 429 with quota_exceeded and nothing in it is stored; beacons are dropped silently. The count behind the quota trails by up to five minutes. Your plan and this month’s events are shown under Settings → Plan and billing.

Versioning

The /v1 prefix covers this contract. New optional fields, log formats and response fields are added within v1; a changed meaning, a removed field or a tighter limit moves to /v2, and /v1 keeps running until deployed SDKs and agents have moved.

Read API

On a self-hosted install the same API also accepts Authorization: Bearer <key>, with keys the operator sets in the gateway’s config file ([keys] in gateway.toml, mapping a key to a tenant):

curl -s -H "Authorization: Bearer $KNOCKSTAT_KEY" \
  "https://your-knockstat.example.com/v1/stats?from=$(date -d '7 days ago' +%s)&to=$(date +%s)&interval=hour"

Without a session or key every endpoint answers 401 {"error": "not signed in"}.

Common parameters

ParameterMeaning
from, toThe window [from, to): epoch seconds, epoch milliseconds or RFC 3339. Default: the 24 hours before to, which defaults to now.
intervalChart buckets: minute, hour (default) or day. A long window widens the bucket so there are at most about 240. Times are UTC.
hostOne site, by its host name as /v1/sites lists it. Default: every site.
devicedesktop, mobile or tablet.
path, country, browser, osNarrow to one page, country code, browser or OS.
channel, referring_domain, utm_source, utm_medium, utm_campaign, utm_term, utm_contentNarrow to the sessions whose first page view had that value.
limitRows in a list, where an endpoint has one. At most 1000.

Endpoints

EndpointAnswers
GET /v1/statsSummary (requests, humans, bots, bytes, visitors, page views) with changes against the window before, and a time series.
GET /v1/breakdownsTop 50 pages, referrers, countries, devices, browsers, OSes, versions and outbound links, plus status shares.
GET /v1/sessionsSessions, bounces and duration against the window before; entry and exit pages, referrers, channels and UTMs.
GET /v1/liveVisitors and page views in the last five minutes.
GET /v1/hours?tz=Area/CityVisitors and page views by weekday and hour in that IANA time zone (default UTC).
GET /v1/errorsPages people got 4xx and 5xx answers on.
GET /v1/eventsCustom event names, most frequent first.
GET /v1/events/detail?name=One event over time, its property keys, and the top values of a property.
GET /v1/sitesSites seen in the window, busiest first.
POST /v1/reports/{insights,funnels,flows,retention}The product reports, with a JSON body. These are what the dashboard’s report builders send.
GET /v1/usage?from=&to=Stored events per UTC day for your organization, by site, source and event class.

What each figure counts (visitors, page views, sessions, channels) matches the dashboard: bots and automated traffic are left out of people’s figures and counted on their own.

Errors, caching and CSV

  • Errors are JSON with an error message: 400 for a bad parameter or body (reports add the field they refuse), 401 without a session or key, 422 for a report too large to run (with a hint), 502 when the store fails, 501 for a report the store does not have.
  • Answers are cached for 20 seconds per gateway replica, keyed by the exact request, and a window that ends less than 5 seconds ago is not cached. Round to down so repeated calls share an answer.
  • The API answers JSON only. The dashboard’s CSV buttons on its lists and report tables export CSV.

Stuck? Email [email protected].