HTTP ingest API

The tracker's ingest endpoint, its payload contract, and a plain statement of what it is not: a supported server-side tracking API.

Markdown
Updated Sep 2, 2026
On this page
  1. The endpoint
  2. The envelope
  3. What this is not
  4. Response shape

There is one ingest endpoint, and it is built to receive a genuine browser beacon. It is documented here so you can debug what your own pages are sending, not so you can send to it yourself.

The endpoint

POST /api/collect (aliased as /api/data on a managed proxy subdomain). The body is JSON. The tracker sends it as Content-Type: text/plain, which makes the request a CORS simple request and skips the preflight round trip; the server parses the body as JSON whatever the declared type. CORS is open (the endpoint replies with Access-Control-Allow-Origin: *) because the whole point is accepting cross-origin beacons from whatever domain a customer's site runs on.

The envelope

One request carries a batch. The identity and browser-context fields sit once on the envelope, and each event in events carries its own name, props, and path:

FieldTypeRequiredNotes
sitestringYesSite id. Missing or empty returns 400.
eventsarrayNoUp to 25 entries per request; anything beyond that is discarded. Each entry takes event, props, path, url, t.
eventstringNoSingle-event shorthand, used when events is absent or empty. Defaults to "pageview".
propsobjectNoSanitized with the same 8-key / 256-char rules track() uses client-side.
sessionstringNoWithout it, the event still passes the bot gate and updates rollup counters, but skips the sessions upsert and the events-table mirror entirely.
client_idstringNoCookie-mode persistent visitor id. Ignored on a cookieless site.
path, url, referrer, utmstring / objectNoUsed for page/source classification. A per-entry path wins over the envelope's.
tz, lang, screen, uastringNoBrowser context; tz also feeds the geo/timezone bot check.
wd, pr, nl, vp, nbbooleanNoClient-side bot signals (webdriver, prerendering, empty languages, zero viewport, empty UA-data brands). Omitting them just means those specific checks can't fire.

What this is not

Do not try to work around the gate. There is no authentication model, no stability commitment on the payload contract, and no rate-limit tier designed for backend traffic, so anything built that way will break without notice and its numbers will not be trustworthy in the meantime. If your product needs backend-originated events, tell us and we will treat it as the feature request it is.

Response shape

SituationHTTP statusBody
Accepted200{ ok: true, accepted: n, replay: { enabled, sample_rate } }
Classified as a bot200{ ok: true, bot: true, reason }
Every event in the batch was on an excluded path200{ ok: true, excluded: true }
Every event in the batch was a retired autocapture name200{ ok: true, dropped: "autocapture" }
Hard pageview quota exceeded (grandfathered free plan only)200{ ok: true, quota: "exceeded" }
Malformed JSON or missing site400{ ok: false, error }
Unknown site404{ ok: false, error: "unknown site" }

For the client-side API this endpoint exists to serve, see the JavaScript API reference. For what triggers a bot classification, see bots and spam.