PureStats documentation
JavaScript API
Use the PureStats JavaScript API to track pageviews and custom events, manage consent, identify users safely and record experiment exposures in your project.
The tracker exposes a small JavaScript API on window.purestats.
Track a pageview
window.purestats?.('pageview');
Track an event
window.purestats?.('Signup Completed');
With properties:
window.purestats?.('Signup Completed', {
source: 'header'
});
Identify a signed-in user
Use a stable, opaque internal ID to connect visits from the same signed-in user. PureStats transforms the value with a site-specific HMAC before storage.
purestats('identify', 'internal-user-42')
// or, after the tracker has loaded
purestats.identify('internal-user-42')
Visitor-scoped custom properties can be supplied with the identity call after defining them in Site settings → Custom dimensions:
purestats.identify('internal-user-42', {
account_type: 'customer',
trial_active: true
})
Call purestats('resetIdentity') or purestats.resetIdentity() when the user logs out or switches accounts. Never send names, email addresses, phone numbers, or other directly identifying values.
Set consent
window.purestats?.consent.grant();
Disable tracking after consent is revoked:
window.purestats?.consent.revoke();
Assign and expose an experiment
Create and start the experiment in Analytics → More → Experiments. PureStats then loads the optional experiment module from the server-side tracker configuration. Wait for the ready event before assigning a variant:
window.addEventListener('purestats:experiments-ready', () => {
const variant = purestats.assignExperiment('homepage_headline');
document.documentElement.dataset.headlineVariant = variant;
purestats.trackExposure('homepage_headline', variant);
});
assignExperiment() is deterministic for the browser and configured weights. It does not count an exposure until trackExposure() is called, so visitors who never see the tested UI do not enter the result. PureStats stores only a site-scoped hash and never stores the local assignment identifier.
Load timing
Because pf.min.js is usually loaded with defer, the API may not exist immediately in inline scripts placed before it. Run custom tracking after the page is ready or after your application initializes.
Data safety
Do not send personal data in event names, paths or properties.
Clean paths, titles, queries and referrers before sending
Tracker 1.8.0 accepts a synchronous beforeSend(data, eventName) configuration hook. Return the cleaned { url, title, referrer, props }, or return null to discard that payload. An invalid URL, a cross-origin URL or a thrown exception also discards it. The hook cannot change the actual browser URL. Configure it before the deferred tracker loads so the first pageview is cleaned as well:
<script>
window.purestats = window.purestats || function () {
(window.purestats.q = window.purestats.q || []).push(arguments);
};
purestats('config', {
beforeSend(data, eventName) {
const url = new URL(data.url);
const path = url.pathname.replace(/^\/(en|de)(?=\/|$)/, '') || '/';
let area = 'error';
if (path === '/' || /^\/(blog|features|product|products|pricing|about|contact|compare|solutions)(\/|$)/.test(path)) area = 'marketing';
else if (/^\/(docs|documentation)(\/|$)/.test(path)) area = 'documentation';
else if (/^\/(login|register|auth|forgot-password|reset-password)(\/|$)/.test(path)) area = 'auth';
else if (/^\/app(\/|$)/.test(path)) area = 'app';
else if (/^\/status(\/|$)/.test(path)) area = 'status';
else if (!/^\/(404|error|errors)(\/|$)/.test(path)) return null;
url.pathname = url.pathname.replace(/(\/app\/devices\/)[^/]+/, '$1:id');
url.search = '';
url.hash = '';
data.url = url.href;
data.title = area === 'app' ? 'Application' : url.pathname;
try {
data.referrer = data.referrer ? new URL(data.referrer).origin + '/' : '';
} catch {
data.referrer = '';
}
data.props = { ...data.props, site_area: eventName === '404' ? 'error' : area };
return data;
}
});
</script>
<script defer data-domain="metribeacon.com"
data-exclude-paths="/app/admin"
src="https://purestats.io/pf.min.js"></script>
This example is an integration contract, not a claim that MetriBeacon has deployed it. Match it to the real route map; unknown routes in this conservative example are discarded. Query removal also removes campaign parameters: retain only explicitly reviewed, non-sensitive UTM values if campaign attribution is required. Never put account, device or email identifiers in them.
The hook runs for automatic pageviews, custom events, scroll/exit events and Web Vitals. Exclusions are evaluated on original paths, including SPA navigation, before sanitizing. Web Vitals retain the original document/page-visit context across SPA changes. Do not send another manual pageview after a tracked history change. For an app that scrolls one content panel rather than the document, configure scrollContainer: '#your-content-panel'; sidebar scrolls otherwise do not count as article reading.
Scroll milestones fire at most once per tracked page visit. Page Exit reports the maximum observed scroll depth and visible active time, not the final scroll position or hidden-tab time. A non-scrollable page does not count as active 100% reading. A BFCache return starts a new page visit; old records without page-visit metadata cannot always be correlated unambiguously.
Success events and asynchronous activation
Use the exact configured names marketing_landing_viewed, signup_completed, first_monitor_created and first_measurement_received. Emit success only after the corresponding operation has actually succeeded. A click, a submitted form, a 404 or a queued first measurement is not activation. Gate first-only business events in the application; retries must retain their original event ID when using the Measurement Protocol.
Ingest deduplicates a supplied event ID per site. Browser calls generate new IDs, so calling the same event twice is not an idempotent retry. Goals currently suppress the same goal within the stored session for 30 minutes; this is not a globally unique account conversion. Ordered funnels are evaluated within stored session journeys: a 10,080-minute configured window is an upper bound, not guaranteed stitching across seven days, different devices or privacy resets. First-touch refers to the stored journey's attribution. A delayed server measurement needs an explicitly permitted stable identity/journey integration; PureStats does not invent that link from an email or an IP address.
Without an associated stored visit, separate server events can create separate sessions and conversions. An event name alone does not establish account-level uniqueness. Server integrations must reuse the same event ID for retries and deliberately supply the permitted visitor context for related, distinct events.
Verify integrations with Site settings → Tracking test. Diagnostic test sessions do not create production visits, conversions or rollups. Missing historical traffic and events cannot be repaired by generating synthetic hits.