Docs
Explorar la documentación

What the tracker sends (request payload)

The exact POST request the tracker sends to /api/event, every field in its JSON payload, and what is never sent: no cookies, identifiers or fingerprints.

Ver como Markdown

Every hit is one POST to {origin}/api/event:

  • Content-Type: text/plain, so browsers don't send a CORS preflight,
  • credentials: 'omit': no cookies are sent or accepted,
  • keepalive: true, so hits survive page navigation. navigator.sendBeacon is the fallback when fetch throws.

The server responds with 202 Accepted with an empty body, whether or not the hit is later kept, so the response never tells a script anything about your settings.

Retries#

If the server responds with an error (any 4xx or 5xx status, including 429 when a rate limit is hit), the tracker sends the same hit again:

  • up to 5 more times, after 1 second, 10 seconds, then 60 seconds for each of the last three,
  • oldest hit first, one at a time,
  • from a queue of at most 20 hits that lives in memory only. Nothing is written to the device, so hits still waiting are lost when the page is closed or reloaded.

A request that never gets a response (the visitor is offline, or a content blocker stops it) isn't retried.

A retried hit is recorded with the time it arrives, which can be a few minutes after it happened. In rare cases the server has already recorded a hit before the error reaches the browser, and the retry counts it a second time.

Body#

The three examples show every field that can appear in each kind of hit the core script sends, in the order the script writes them. A real hit carries far fewer: r, h, d, g, hp, ch, a, tz and xp are only there in the cases the table lists.

A pageview:

json
{
  "s": "pa_7Q2K9XH3AB",
  "t": "pageview",
  "u": "https://example.com/app?utm_source=newsletter#/pricing",
  "l": "en-US",
  "w": 1440,
  "i": 0,
  "r": "https://news.example.org/",
  "h": 1,
  "d": 1,
  "g": 1,
  "hp": 1,
  "a": "webdriver",
  "tz": "America/Denver",
  "xp": "var_4M8T2KQ9ZD",
  "p": { "plan": "pro" }
}

An engagement hit:

json
{
  "s": "pa_7Q2K9XH3AB",
  "t": "engagement",
  "u": "https://example.com/app?utm_source=newsletter#/pricing",
  "l": "en-US",
  "w": 1440,
  "i": 1,
  "r": "https://news.example.org/",
  "h": 1,
  "d": 1,
  "g": 1,
  "hp": 1,
  "a": "webdriver",
  "tz": "America/Denver",
  "e": 48210,
  "sd": 80
}

A custom event:

json
{
  "s": "pa_7Q2K9XH3AB",
  "t": "event",
  "u": "https://example.com/app?utm_source=newsletter#/pricing",
  "l": "en-US",
  "w": 1440,
  "i": 1,
  "r": "https://news.example.org/",
  "h": 1,
  "d": 1,
  "g": 1,
  "hp": 1,
  "a": "webdriver",
  "tz": "America/Denver",
  "n": "signup",
  "p": { "plan": "pro" }
}
Key Long name Sent with Meaning What happens to it
s site all Site id Used to find the site. Stored as the site the event belongs to
t type all pageview, event, engagement, vital or click Stored as the kind of event
u url all Page URL after exclusions, masks and query parameter stripping Hostname and path are stored after your path masks and the PII scrubber, cut at 2,048 characters. Campaign parameters are stored, ad click ids become a yes/no flag, and the rest of the query is dropped
l language all navigator.language The first 16 characters are stored
w screen_width all screen.width in CSS pixels Stored only as a size bucket: xs (under 414), sm (under 768), md (under 1024), lg (under 1440) or xl. Also read in memory for the bot score. The exact width is dropped
i interacted all 1 once a real input was seen on the page, 0 before. See Interaction flag Stored as yes, no or unknown on the visit, never on the event
r referrer when present document.referrer, or a URL on your own site for SPA navigations Host and path are stored after the PII scrubber. The query string is dropped and your own domains are blanked
h hash_mode when on 1 if this URL's fragment is part of the path Used in memory to keep the fragment as part of the path. Not stored
d dnt when on 1 if Do Not Track is on If the site honors Do Not Track (off by default), the hit is dropped and nothing is stored. Otherwise ignored. The flag is never stored
g gpc when on 1 if Global Privacy Control is on If the site honors Global Privacy Control (on by default), the hit is dropped and nothing is stored. Otherwise ignored. The flag is never stored
hp honeypot pages with data-hp 1 on a honeypot page (data-hp="true"), 0 on a page marked data-hp="false" A hit with 1 never becomes a visit or an event and doesn't use your monthly events. It is counted as a bot and, with Advanced Bot Detection, kept as a honeypot hit. A hit with 0 is counted as usual
ch challenge pages with data-hp="ch" 1 on a "confirm you're human" page that a site shows a random few visitors, people and bots alike A hit with 1 never becomes a visit or an event and doesn't use your monthly events. It is not counted as a bot. With Advanced Bot Detection it is kept next to the honeypot hits, marked as a challenge
a automation honeypot and challenge pages, when an automated browser is detected webdriver, phantom, nightmare or cypress. On every other page the script sends nothing at all from an automated browser Kept with the honeypot hit. Any other value is read as none, and it is ignored on every other hit
tz timezone all, when the browser reports one The name of the browser's time zone, for example America/Denver. See Time zone Compared in memory with the country of the request for the bot score, then dropped. Never stored with a visit or an event and never used as a location. Kept only with a honeypot hit
xp preview pageview, when set The value of pa_preview in the address, from an experiment preview link Used in memory to show the version it names. The pageview is not counted for the experiment. Not stored
p props pageview, event (every hit when data-props is set) Properties. See track(). A pageview with none sends {} Up to 30 are stored, strings cut at 500 characters, after the PII scrubber. revenue and currency are stored as their own fields
e engaged_ms engagement Visible time on the page in milliseconds Stored. At most 4 hours is counted per hit
sd scroll_depth engagement Maximum scroll depth, 0 to 100. Left out of the one engagement hit sent at the first input when the engage module is off Stored in 10% bands
n name event, vital Event name, or the Web Vital (LCP, INP, CLS, FCP, TTFB) Stored, event names cut at 120 characters
v value vital The Web Vital value Stored, for the share of hits your site's sampling rate keeps
c selector click Short CSS selector of the clicked element, or [data-privatus-click="name"] when the site named it Stored, cut at 512 characters

The server also accepts rv/revenue and cu/currency at the top level, but the tracker puts them in p.

A body larger than 32 KB, a body that isn't a JSON object and a hit with an unknown t are ignored (still given a 202).

What each module sends#

The core script (pa.js) sends pageview, engagement and event hits. The modules add these:

Module What it sends
auto Custom events (t: "event") through track(), with the common fields above, n and p: Outbound Link (domain, url), File Download (file, host), Email Link (to_domain), Phone Link, Form Submit (form) and 404 (path)
vitals One t: "vital" hit per metric when the page is hidden, with the common fields above, n (the metric) and v (its value)
clicks One t: "click" hit per click, with the common fields above and c
Click map on your page Only when a member opens Show on the page, never for visitors. Its own request, not /api/event: POST /api/overlay with t (the member's one-hour pass), u (the same page URL a pageview sends), period, device and h (1 when the page keeps its # part, as a hit says). No hit is sent while it is open. See click maps
ask Its own two requests, not /api/event. GET /api/surveys asks which survey to show, with s (site id), u (the same page URL a pageview sends) and dnt=1 or gpc=1 when the browser sends that signal. POST /api/survey reports what happened, with s, u, d and g as above, id (the survey), k (shown, dismiss or answer), a (the answers) and done (1 when the survey was finished). See surveys

Experiments#

A pageview is normally given a 202 and no body. When the page has a running experiment and this visitor is given another version, the response is 200 with a small JSON body:

json
{ "x": "/pricing-b", "p": 0 }

x is a path of the same site and p is 1 for a preview. The tracker sends the visitor there and keeps the query string. That pageview is not stored. The redirect adds pa_ref to the address with the page the visitor really came from, so the visit keeps its source. The tracker reads it once and removes it from the address bar. Nothing is stored in the browser.

Interaction flag#

Some bots run a real browser, load a page and leave without touching it. To tell them from people, every hit says whether the tracker has seen a real input on the page:

  • i is 0 until the first pointer press or move, key press, touch or wheel turn, and 1 from then on.
  • At that first input the tracker sends one engagement hit straight away, so the visit is marked even if the hit sent when the page closes is lost.
  • Scrolling alone doesn't count, and neither does input a script generates. Both are things a bot can do without a person.

It is a yes or no. Nothing about the input is sent: not what it was, not where the pointer was, not which key, not when. Visits are then reported as input seen, none or unknown, see Visits with no input seen.

Time zone#

Every hit carries the name of the browser's time zone (tz), as Intl.DateTimeFormat().resolvedOptions().timeZone reports it.

A browser running on a server is rarely set to a time zone of the country its address is in. So the server compares the zone with the country of the request and feeds the result (it fits, it doesn't, it is UTC) into the bot score.

The zone is used in memory, for that comparison only. It is not stored with the visit or the event, not logged, and never used as a location: the country in reports comes only from the country header our network provider (Cloudflare) adds to the request, and a hit without that header has no country. The one place a time zone is kept is a honeypot hit.

What isn't sent#

No cookies, no stored identifiers, no user id, no screen height or color depth, no installed fonts or plugins, no canvas or audio fingerprints, no mouse movement, no form contents.

The server receives the IP address and User-Agent because every HTTP request carries them. They're used in memory and dropped. See How it works.