PA
PureAnalytics

How Pure Analytics works

Pure Analytics is a privacy-friendly web analytics service. You add one script tag to your site, and you get real-time traffic, referrer, geography, device, campaign and revenue data in a dashboard and through a REST API.

This page is written to be read by both people and AI agents. Every fact here reflects the running system: the tracking snippet, the API contract, the plan limits and the quota behaviour.

In one paragraph

A small JavaScript file on your pages sends events to https://pure-analytics.com/api/track. A Cloudflare Worker validates each event, filters bots, stores it in PostgreSQL, and counts page views against your monthly quota. You read the aggregated numbers either in the dashboard at https://pure-analytics.com/dashboard or through the public API at https://pure-analytics.com/api/v1/analytics with an API key.

Architecture

Four pieces: the script on your pages, the ingest Worker, the database, and the two readers on top of it.

LayerWhat runs there
Tracking scripthttps://pure-analytics.com/tracking/utilities/script.js, loaded with defer on your site
IngestCloudflare Worker, POST /api/track
StoragePostgreSQL (Supabase), row-level security per account
DashboardReact single-page app on Cloudflare Pages
Public APICloudflare Worker, GET /api/v1/analytics

Aggregation happens in the database. The dashboard and the public API call the same aggregation functions, so a number you read through the API is the number you see in the dashboard.

Install the tracking script

Paste this before the closing </body> tag of every page. Replace the data-ea-website-id value with the tracking ID shown when you add a website in the dashboard.

<!-- PureAnalytics Tracking Code -->
<script
  defer
  data-ea-website-id="YOUR_TRACKING_ID"
  data-ea-domain="example.com"
  src="https://pure-analytics.com/tracking/utilities/script.js">
</script>
<!-- End PureAnalytics Tracking Code -->

Script attributes

AttributeRequiredMeaning
data-ea-website-idyesTracking ID of the website, from the dashboard
data-ea-domainnoYour domain, used to tell internal from external links
data-ea-track-accuracynoCookie mode. most accurate sets a first-party cookie; any other value, or no attribute, keeps the script cookieless. Default: cookieless
data-ea-stripe-keynoStripe publishable key, enables client-side revenue tracking
data-ea-optionsnoJSON object that turns single event types on or off, see below

The script does not run on localhost, on 127.0.0.1, on file:// pages or inside an iframe. That is intentional: your development traffic never reaches your production numbers.

What the script collects

By default the script collects every event type. Turn single types off with data-ea-options, for example data-ea-options='{"clicks": false, "exits": false}'.

OptionDefaultEvents
pageviewstruePage views
externalLinkstrueClicks on links to other domains
formstrueForm submissions
clickstrueClicks on elements with an id or a class
exitstruePage exits
respectDNTtrueHonour the browser's Do Not Track setting

Only page views count toward your monthly quota. Clicks, form submissions, external links, exits, heartbeats and custom events are stored and reported, but they never consume it.

The accuracy level you pick in the dashboard (Basic, Standard, Enhanced, Professional, Most Accurate) goes into the snippet as data-ea-track-accuracy. In the current script it decides only the cookie: Most Accurate is the only level that sets one; every other level, and a snippet without the attribute, is cookieless.

Custom events

Beyond the automatic events, you can record your own through the global object the script installs.

window.pureAnalytics.track("signup_completed", { plan: "pro" });

The first argument is the event name, the second is an optional object of metadata. window.easyAnalytics is a backwards-compatible alias for the same object and points at the same instance.

What is collected and what is not

Collected: page URL and title, referrer, UTM parameters, country derived from the request, browser, operating system, device type, screen size, and the timing of the events you enabled.

Not collected: no IP address is stored, no cross-site identifier, no behavioural profile, no fingerprint sold to a third party. IP anonymisation is on by default, and Do Not Track is respected by default.

Because the default configuration stores no personal data and sets no cookie, most sites can run Pure Analytics without a cookie banner. That is a description of what the software does, not legal advice for your jurisdiction.

Bot filtering

Events are dropped before storage when the request looks automated. Three checks run in order on the ingest Worker: known bot signatures in the user agent, a Chrome user agent arriving without Client Hints, and a source network that belongs to a hosting or datacenter provider. The network check is the one that catches headless browser farms, because the network an event arrives from cannot be spoofed by the page.

Traffic from Apple iCloud Private Relay is deliberately allowed through: those are real visitors on iOS.

Public API

Base URL: https://pure-analytics.com/api/v1/analytics

Authenticate with an API key created in the dashboard, sent in the X-API-Key header. A key is scoped to one or more of your websites; asking for a website the key does not cover returns 403.

Query parameters

ParameterDefaultMeaning
website_idall websites on the keyWebsite UUID or tracking ID
start30 days before endISO 8601 start of the period
endnowISO 8601 end of the period
metricsvisitors,pageviews,bounce_rateComma-separated list, see below
groupBynoneAdds a breakdown by one dimension
limit100Rows returned in the breakdown, at most 10000
granularitydayBucket size of the timeseries: hour, day, week, month
filtersnoneOne or more of page, entry_page, exit_page, referrer, channel, ai_assistant, utm_source, utm_medium, utm_campaign, browser, country, device, os, button

Metrics

visitors, sessions, pageviews, unique_pageviews, bounce_rate, engagement_rate, avg_session_seconds, views_per_session, plus two that return their own array: timeseries and revenue.

avg_session_duration and avg_session_time are accepted as aliases of avg_session_seconds.

Breakdown dimensions

Valid values for groupBy: page, entry_page, exit_page, country, browser, device, os, referrer, channel, ai_assistant, source, medium, campaign.

Every breakdown row reports visitors, sessions and pageviews separately, so there is never an ambiguous single count.

Filters

Any filter parameter restricts the whole response — summary, timeseries and breakdown alike. Filters combine with groupBy to cross two dimensions: groupBy=entry_page&referrer=google.com returns the pages Google search sends people to, groupBy=referrer&page=/pricing returns who sends people to a given page.

Attribution is per session and first touch: referrer, channel and the utm_* filters describe where the session started, so the page they pair with is entry_page. Grouping by page under a referrer filter answers a different question — every page seen by sessions from that source, not the page it landed on.

The referrer filter takes the same label the referrer breakdown returns: a hostname, or Direct.

ai_assistant exists because AI products strip the Referer on roughly two thirds of their traffic: neither referrer nor utm_source alone sees all of it. Filter or group by ai_assistant (ChatGPT, Perplexity, Copilot, …) to get the whole picture, and pair it with groupBy=entry_page for the pages each assistant sends people to.

Example request

curl -s "https://pure-analytics.com/api/v1/analytics?metrics=visitors,pageviews,bounce_rate&groupBy=country&limit=5" \
  -H "X-API-Key: YOUR_API_KEY"

Landing pages of the traffic coming from Google:

curl -s "https://pure-analytics.com/api/v1/analytics?metrics=visitors,sessions&groupBy=entry_page&referrer=google.com&limit=10" \
  -H "X-API-Key: YOUR_API_KEY"

Example response

{
  "website_ids": ["8f3ca1d2-....-...."],
  "period": {
    "start": "2026-08-02T00:00:00.000Z",
    "end": "2026-09-01T00:00:00.000Z",
    "timezone": "Europe/Rome"
  },
  "visitors": 4821,
  "pageviews": 11204,
  "bounce_rate": 42.7,
  "breakdown": [
    { "country": "IT", "visitors": 1902, "sessions": 2210, "pageviews": 5104 }
  ]
}

The reporting timezone is the one configured on the website. When a single call spans several websites the response is in UTC, because mixing timezones in one bucket would produce a series that matches none of the sites.

Revenue

Ask for metrics=revenue and the response carries one row per currency with gross, refunded, net, transactions and refunds. Amounts in different currencies are never summed together.

Rate limits

60 requests per 60 seconds per API key, or per client IP for requests without a key. The limit covers /api/v1/*, /api/health and the MCP server; it never applies to the tracking ingest.

Every response carries the IETF rate limit headers:

RateLimit-Policy: "api";q=60;w=60
RateLimit: "api";r=57;t=42

r is the number of requests left in the current window and t the seconds until it resets, as seen by the edge node that answered. Past the limit the API answers 429 with a Retry-After header in seconds: wait that long, then retry.

Errors

Errors are application/problem+json (RFC 9457). Every error body has the same fields, so an agent can branch on code and show resolution to a person.

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json; charset=utf-8

{
  "type": "https://pure-analytics.com/docs#errors",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Missing X-API-Key header",
  "code": "missing_api_key",
  "resolution": "Send a valid API key in the X-API-Key header. Create one in the dashboard under Settings > API keys.",
  "instance": "/api/v1/analytics"
}
StatuscodeCause
400invalid_group_by, invalid_granularity, no_websitesUnsupported parameter value, or no website on the key
401missing_api_key, invalid_api_key, inactive_api_key, expired_api_keyThe API key is missing, unknown, deactivated or expired
403website_forbiddenThe requested website is not associated with the key
404not_foundNo endpoint at that path
429rate_limitedMore than 60 requests in 60 seconds
500query_failed, internal_errorThe query failed; retry later

The body also repeats detail in an error field, for clients written against the first version of the API.

OpenAPI

The full contract of the API, with every parameter, schema, error and header, is published as OpenAPI 3.1 at https://pure-analytics.com/openapi.json. The API catalog at https://pure-analytics.com/.well-known/api-catalog (RFC 9727) links to it.

MCP server

AI agents can read the same data through a Model Context Protocol server, without writing HTTP calls.

Endpointhttps://pure-analytics.com/mcp
TransportStreamable HTTP, JSON responses, no session
AuthenticationX-API-Key header on the connection, or Authorization: Bearer <key>
Server cardhttps://pure-analytics.com/.well-known/mcp/server-card.json

Tools

Both tools are read-only.

ToolNeeds a keyWhat it returns
get_analyticsyesThe same report as GET /api/v1/analytics: summary metrics, an optional breakdown (group_by), timeseries and revenue, with the same filters
get_api_referencenoAccepted metrics, dimensions, filters, limits, rate limit and plan prices

Without a key, initialize, tools/list, get_api_reference and the resources still work, so an agent can discover the server before the user hands over a key.

Resources

URIType
https://pure-analytics.com/docs.mdThis documentation, text/markdown
https://pure-analytics.com/openapi.jsonThe OpenAPI specification, application/json
pure-analytics://referenceThe API reference as JSON

Connect a client

Any MCP client that supports remote servers over HTTP can connect. For example, in Claude Code:

claude mcp add --transport http pure-analytics https://pure-analytics.com/mcp --header "X-API-Key: YOUR_API_KEY"

Command line

There is no separate CLI to install: the API is one GET request, so curl and jq are the command line client.

export PA_KEY=YOUR_API_KEY

# Visitors and bounce rate for the last 30 days
curl -s "https://pure-analytics.com/api/v1/analytics?metrics=visitors,bounce_rate" -H "X-API-Key: $PA_KEY" | jq

# Top 10 landing pages of the traffic sent by ChatGPT
curl -s "https://pure-analytics.com/api/v1/analytics?groupBy=entry_page&ai_assistant=ChatGPT&limit=10" -H "X-API-Key: $PA_KEY" | jq '.breakdown'

# Daily visitors as CSV
curl -s "https://pure-analytics.com/api/v1/analytics?metrics=timeseries" -H "X-API-Key: $PA_KEY" | jq -r '.timeseries[] | [.date, .visitors] | @csv'

To explore the MCP server from a terminal, the official inspector works without installation:

npx -y @modelcontextprotocol/inspector

Plans and limits

Limits are page views per month, counted across all your websites. Clicks, form submissions, exits, heartbeats and custom events are stored but do not count.

PlanPageviews per monthPrice per month
Free1,000€0
Starter5,000€19
Pro25,000€49
Enterprise75,000€89
Scale150,000€149
Unlimited300,000€249

What happens when you go over

Going over your plan limit does not drop your data. You are notified once per month inside the app, and collection continues. Losing a customer's data to enforce a price tier is not a trade we make.

A separate ceiling, ten times the plan limit, exists to protect the infrastructure against abuse. Past that point the ingest returns 429. If the quota counter itself fails, tracking proceeds anyway: a quota problem must never break collection.

An account whose payment is late keeps its plan limit during the grace period. An account with no active subscription falls back to the Free limit of 1,000 pageviews.

Machine-readable resources

These URLs exist so an agent can read the product without rendering a page.

ResourceURL
Short summary for language modelshttps://pure-analytics.com/llms.txt
This page, full texthttps://pure-analytics.com/llms-full.txt
This page, Markdownhttps://pure-analytics.com/docs.md
Sitemaphttps://pure-analytics.com/sitemap.xml
OpenAPI 3.1 specificationhttps://pure-analytics.com/openapi.json
API catalog (RFC 9727)https://pure-analytics.com/.well-known/api-catalog
MCP serverhttps://pure-analytics.com/mcp
MCP server cardhttps://pure-analytics.com/.well-known/mcp/server-card.json
MCP registry entryhttps://pure-analytics.com/server.json
Tracking scripthttps://pure-analytics.com/tracking/utilities/script.js

Every public page of this site is served as complete server-rendered HTML, so a crawler that does not execute JavaScript still reads the content. Ask for any public page with Accept: text/markdown and you get its Markdown version instead of the HTML, from the same URL.

Where to go next

The rest of the site, for a human reader.

  • Comparisons: /compare/google-analytics, /compare/plausible, /compare/fathom, /compare/simple-analytics
  • About the product and who builds it: /about
  • Contact: /contact
  • Blog: /blog
  • Privacy policy: /privacy
  • Sign up or sign in: /login

Try Pure Analytics for free

Privacy-friendly analytics with a free plan. Set up in 2 minutes.

© 2026 PureAnalytics. All rights reserved.