How it works
Four moving parts.

01 · Collection

One script, served from your own subdomain.
You add an A record pointing a subdomain of your site at the tracker host, and one deferred script tag. Because the script comes from your own domain rather than a known analytics host, blockers that work by blocking third-party analytics domains never see it.
Sessions are counted with a rotating server-side hash of connection data. The salt rotates daily, so the same visitor tomorrow is a new session with no way to connect it to today.
- Nothing is written to the device. No cookie, no
localStorage, nosessionStorage. - The IP address is used in memory to compute the hash and is never stored.
- No identifier survives the day. There is no key that connects a session today to a session tomorrow.
- One DNS record
metrics.pearmetrics.com A 203.0.113.10- One script tag
<script defer src="https://metrics.pearmetrics.com/p.js" data-site="b6c9e2a1"></script>
02 · Attribution
Last non-direct click, session-scoped.

Traffic is attributed on the UTM triple (source, medium, campaign), read from the landing URL at the first event of a session. Later events in the same session are same-site navigations, and their UTMs are ignored so they can’t overwrite the real acquisition source with noise.
Multi-touch is not offered.
A visitor who clicks an ad today and converts next week from a search is credited to the search. Multi-touch attribution needs cross-session identity, and this build has chosen not to have any. That is the honest ceiling without tracking people, and it is where spend decisions actually get made.
Click IDs are never stored.
gclid,fbclidand the rest have their values replaced before anything is written, while the parameter name is kept so a paid click is still classified as a paid click.
03 · Spend
Daily, at campaign level, from both platforms.
Every morning the sync pulls campaign spend, impressions and clicks from the Google Ads and Meta APIs. Money and clicks only. Nothing user-level is ever requested from an ad platform, which is what keeps the privacy posture whole rather than merely front-facing.
Each run re-pulls a trailing seven-day window and overwrites, because both platforms restate recent days after the fact. A same-day-only pull would lock in whatever figure happened to be visible the moment the job ran. It also means a missed run heals itself: the next one restates the days the missed one would have written.
Figures shown are illustrative.
Two things that quietly corrupt the numbers.
Timezone
Sessions in UTC, spend in the account’s day.
Joined naively, up to eight hours of every day’s sessions sit against the wrong day’s spend, and the totals still look plausible. You set one reporting timezone per site, each account’s timezone is read from the platform rather than typed, and a mismatch raises a warning instead of being silently reconciled.
Currency
One currency per site, or the sync refuses.
All ad accounts on a site must share one currency. If they don’t, the sync refuses to run rather than converting at a rate nobody checked. A wrong rate is worse than a refused sync, because it produces a plausible number nobody questions.
04 · The join

Campaign spend meets sessions on the UTM triple.
A mapping table ties one platform campaign to one UTM triple. Matching campaign names against utm_campaign at query time would need less setup and would break the first time somebody renames a campaign in the ad platform, which happens constantly and silently. A mapping row survives the rename; a string comparison does not.
Because click IDs are dropped, manual tagging carries the attribution. If a campaign ships untagged, its clicks land as direct and its spend sits unmatched. So the product tells you, rather than hiding it in an average. Without that panel the tool reports confidently wrong CPAs, which is worse than no tool at all.
Money
Unmatched spend.
Money with no session behind it. Tagging is broken, or a campaign is new.
Traffic
Unmatched paid sessions.
Paid-looking traffic with no spend behind it. A mapping gap, or a typo in a tagging template.
Naming
Unclassified traffic.
Source and medium combinations matching no channel rule. Almost always a typo, and invisible everywhere else.
Click IDs are stripped at ingest and never stored, so tagging is load-bearing: an untagged campaign lands as direct traffic with unmatched spend.
Some spend is missing its pair.
Usually a tagging gap. $412.80 (2.6% of total spend) has no mapped UTM triple. Here is where.
- Performance Max - Q3 Test (Google Ads · 284-991-0043)$268.40
- Advantage+ Catalogue (Meta · act_1099238471)$144.40
Unmatched paid sessions
414 paid-looking sessions have no spend mapped to them. Usually a mapping gap, or a typo in a tagging template.
- facebook / paid_social / summer-promo318
- google / cpc / (not set)96
Unclassified traffic
Source and medium combinations matching no channel rule. Almost always a typo, and invisible everywhere else.
- newsletter / e-mail611
- facebook / Paid_Social204
Figures shown are illustrative.
Custom events
One call.
Mark any event you send as a conversion and it becomes the denominator of every CPA in the table. Shopify purchase tracking runs through a checkout-safe custom pixel, documented in full.
pear.track('signup', { plan: 'pro' })Four parts.
About ten minutes.
