Every analytics product is an identity product with charts on top. Google Analytics 4's identity is a first-party cookie, optionally enriched with Google signals. SiteLine's identity is a hash that dies at UTC midnight. If you skip that sentence, the rest of the comparison is marketing.
This page is only the identity model: what each system can honestly say about a "visitor", what it cannot, and why cookieless tracking changes the banner conversation. For the broader vendor map see GA4 alternatives. For the legal-adjacent banner question see GA4 cookie banner. How a day of uniques is computed without a cookie is unique visitors without cookies. The binding product description is /docs/privacy. The public notice is /privacy.
SiteLine is cookieless on-site analytics, a durable Google Search Console import, and two read-only MCP servers. It is not Mixpanel, not session replay, and not a GA4 replacement with the logo filed off. Chris Richards builds it at Aglet Technologies. Questions about the operator go to [email protected], and the public description of who runs the service is /trust.
How GA4 knows it is "the same person"
A typical GA4 web stream sets _ga (and related _ga_* cookies) in the first-party context of your site. That client ID persists across days until the cookie is cleared, blocked, or never set because the user refused consent. If you enable Google signals, Google may stitch logged-in Google users across devices. That is powerful. It is also why the CMP exists, why modelled conversions exist, and why small-number reports get thresholded.
Google's introduction to GA4 describes the product as event-based rather than session-based, with privacy controls such as cookieless measurement and behavioral and key event modeling, plus direct integrations to media platforms. Read that list as a feature menu, not as a description of the tag you actually ship. "Cookieless measurement" and "key event modeling" on that page are fills for observations Google does not have. They are not the same mechanism as a tracker that never writes _ga. When the cookie is present and consented, GA4 still uses it. When it is absent, modeling estimates. An estimate is a statistical story. It is not a count you can reconcile to a row in your application database.
GA4 can answer "how many users returned this month?" in a way a daily hash cannot. Pretending cookieless tools do the same thing with a different label is how you get a board slide that lies.
Sampling is a second identity problem, because the person you think you counted may be a scaled subset. Google's GA4 sampling documentation says event-level queries sample when they exceed a quota: 10 million events for a standard property, and up to 1 billion events for a Google Analytics 360 property. A 360 property starts at a default of 100 million events per query so the result returns faster; Explore can request more detailed results from the data-quality icon. The same page says the icon shows the percentage of data used, and that unsampled distinct counts for users and sessions are estimated with HyperLogLog++. Google says discrepancy rates are under 1% in most cases and can be higher when you combine more than one of those metrics. SiteLine's on-site events are not sampled. Plan caps change what a chart is allowed to show. They do not scale a sample up and call it the population.
How SiteLine derives a visitor
The tracker does not set a first-party analytics cookie. The browser fetches POST /api/analytics/event with credentials: 'omit', so even a SiteLine session cookie from the app origin is not attached to a cross-origin beacon.
On ingest, SiteLine truncates the client IP (IPv4 /24, IPv6 /48), combines it with the user agent and a rotating daily salt customers cannot see, and stores the SHA-256 of that tuple. The raw IP is not stored on the event. The salt rotates. The hash resets at UTC midnight. A person who visits Tuesday and Wednesday is two visitors. A household behind one /24 and one browser looks like one visitor that day. Known bots are dropped before quota. Events from hostnames outside your allowlist are rejected.
That is a daily, coarse, non-persistent identifier. It is enough to compute unique visitors for a day, and to compute goal conversion as unique visitors who matched a path or event name in the selected range. It is not enough to build a 30-day retention cohort of people.
Walk one UTC day with three browsers, as a mechanism, not as a customer.
| Visitor | What the request looks like | Stored id |
|---|---|---|
A laptop on a home /24, Chrome |
Truncated IP + that user agent + today's salt | Hash H1 for this UTC day only |
A second laptop on the same /24, same Chrome major |
Same truncated IP, same user agent class, same salt | H1 again, if the user agent string matches |
| The first laptop after 00:00 UTC | Same network, new salt | Hash H2, unrelated to H1 |
There is no join key between H1 and H2. Changing networks, changing browsers, or crossing midnight all mint a new id. Two colleagues on a corporate NAT with the same browser can share an id for that day. Treat the unique-visitor number as an order-of-magnitude instrument. The longer explanation of what you may still call a unique is unique visitors without cookies.
What you can still count
Page URL, path, hostname, referrer, event name, up to 10 properties (keys at most 64 characters, string values at most 256), UTMs on the page URL, coarse device, browser, and OS from the user agent, and country when the request includes it. Custom events are billable 1:1 with pageviews. Goals and funnels are derived in the UI. They do not emit extra events. Packages: @siteline.tools/tracker and @siteline.tools/react. Script: /js/script.js.
SPA navigations: the script patches pushState and replaceState and listens for popstate. Some routers (Next.js App Router in edge cases) still need a manual trackPageview().
What you should put on a weekly dashboard, given this identity:
| Question | Honest SiteLine answer | Why the cookie product answers differently |
|---|---|---|
| How many visitors today? | Distinct daily hashes | GA4 users are a consented, cookied population, optionally signals-stitched |
Did /thanks fire? |
Unique hashes that hit that path or event name in the range | A returning user can be one user across many days |
| Which page did organic search land on? | Pageviews, plus Search Console clicks on that URL | A GA4 session channel is a grouping, not a query |
| Who came back this month? | Not a person-level metric | The cookie was built for this question |
Side-by-side
| Question | GA4 (typical web stream) | SiteLine |
|---|---|---|
| Persistent ID across days? | Yes, via cookie (if consented and not blocked) | No. The hash resets at UTC midnight |
| Cross-device stitch? | Possible with signals or user-id | No |
| Cookie written by the tracker? | Yes (_ga and related) |
No |
| Collect credentials | Cookies sent in first-party context | credentials: 'omit' |
| Unique visitors for one UTC day | Users, subject to consent and signals | Daily hash of truncated IP + user agent + salt |
| Returning user in 28 days | Reportable | Not a person-level metric |
| IP stored on the hit | Depends on configuration and redaction | Not stored on the event |
| Sampling of this identity | Explorations can sample; see the sampling quotas | On-site events are not sampled; visibility follows plan caps |
SiteLine also does not offer session replay, heatmaps, Ads attribution, or BigQuery export. Those features need stronger identity or a warehouse story. If you need them, keep the tool that has them.
Campaigns without cookies: UTM tracking without cookies.
Search Console is a different identity entirely
Queries, pages, countries, and devices in Search Console are Google's aggregates for searches, not your on-site visitor hash. A click is not a session and not a daily hash. The units are explained in Search Console clicks vs analytics sessions. SiteLine imports Search Console read-only (webmasters.readonly): a 16-month backfill of whatever Google still holds in its rolling Performance window, kept while you are subscribed, refreshed every 4 hours for the last 7 days including fresh rows, and capped at 25,000 rows per day per report. Google's API can expose up to 50,000 rows per day per search type. SiteLine stores up to 25,000. Recent days include fresh data and can still change before Google finalizes them. CTR is computed, not stored. You can put both series on one account without pretending a Search Console click is a GA4 session.
That combination — daily cookieless visitors plus durable query history — is the reason to look at SiteLine instead of another cookieless counter. Compare vendors on that axis in privacy-first analytics comparison. Agents that need both ledgers use the two read-only MCP servers described in SEO analytics for AI agents.
Worked example: 1,000 query rows in the UI, 25,000 in the import
This is a labeled hypothetical, not a customer. Call the day Tuesday. Call the property a documentation site you operate. The search type is web. The dimension is queries. Nobody in this paragraph is a case study, and none of the row counts below are a measured click total. They are the caps.
On Tuesday the Search Console interface is willing to show 1,000 query rows. Export the table and you still have 1,000 representative rows. Google's export help says the download is truncated to 1,000 rows, that smaller sites may already have most or all of their data inside that cut, and that the totals shown in the report are the actual totals, not the sum of the truncated rows. Google's Performance data deep dive says the same UI ceiling, and says the Search Analytics API's upper limit is 50,000 rows per day per site per search type. The API defaults to 1,000 rows. rowLimit can raise a page to 25,000, and startRow can request the next page.
SiteLine's import for that Tuesday query report holds 25,000 rows. The UI export holds 1,000. The difference is 24,000 query rows that never appear in the UI export. Those missing rows are the tail: low-click queries, new URLs, the phrases you would retitle if you could see them. They are not a second copy of your visitor hashes. They do not identify a person, and they must not be summed and called the site total. Anonymized queries are omitted from the table and can still sit in the chart total, which is why a sum of rows and a chart disagree even before the row cap. The full mechanics of the 1,000-row ceiling are Search Console's 1,000-row limit.
If Tuesday's property is small, it may already fit in 1,000 rows. Google says the daily row limit will not affect the majority of properties. In that case the import and the UI list the same queries, and the reason to keep SiteLine is the other half of the product: the day stays after it ages out of Google's rolling 16-month Performance window. That window is described in Search Console's 16-month limit.
Do not average CTR or position across those rows. CTR in this system is a fraction of 1. 0.0412 means 4.12%. Averaging the fractions weights every query the same, which is not how a click-through rate for the property is defined.
Practical consequences
Do not build a "returning users" KPI on SiteLine. Do build "unique visitors today", "signups today" (an event or a thank-you path), and "which pages Google is actually sending". Do not expect the visitor count to match GA4 users. GA4 users are a consented, cookied population, optionally signals-stitched. Do expect SiteLine uniques to move with real traffic, including people who rejected the CMP, unless they also block the script. A first-party proxy for /js/script.js and POST /api/analytics/event is your reverse proxy, not a SiteLine-managed CNAME.
Plans: Starter $4.99 for 1 million events, Growth $19.99 for 10 million, Scale $99.99 and then $2.99 per extra million. MCP is included on every tier. The trial is 7 days, no card to start, with visibility of about 50,000 events. Soft cap on Starter and Growth: ingest continues, and charts hide the overflow until you upgrade. Scale meters. Unlimited domains. Analytics has a 30-day grace period after access ends. Details: /docs/billing. Try it at /register.
FAQ
Is a daily hash still personal data?
Often yes. Privacy law is not "no cookie means not personal data". Truncated IP plus user agent can still be personal data in some regimes. SiteLine's model is designed so this tracker does not need an analytics cookie. Your notice, DPA, and legal basis are still yours. /privacy describes what SiteLine stores. /docs/privacy is the product constraint.
Can two people share one visitor id?
Yes. A /24 plus a common browser on a shared network can collide for that UTC day. The reverse is also true: changing IP or user agent creates a new id. Treat uniques as an order-of-magnitude instrument, not a headcount.
Why reset at midnight UTC rather than persist 24 hours from first seen?
A rolling 24-hour window would still be a short-lived identifier. A calendar day is simpler to explain and to join to daily Search Console rows. It is a product choice, not a cryptographic requirement. Either way, SiteLine does not recognise the same person across days.
Do Search Console rows use the visitor hash?
No. Query, page, country, and device rows are Google's search aggregates, imported with webmasters.readonly. They are not SHA-256 of a truncated IP. A click in that table is not a SiteLine visitor and not a GA4 session. Keep the units apart: clicks vs sessions.