An antique engraved plate of clock movements, gears, and pendulums

Guide

Own your analytics stack, end to end

The full path from first-party event collection to API reporting to agent-ready CLI output — every piece of a GA4 + Search Console stack you control, in one guide.

Published August 28, 2026

Owning your analytics stack means owning both ends of it: collection, where one first-party script and one API route replace every vendor tag, and read-back, where a service account pulls GA4 and Search Console data into caches and archives you control. Google still does the processing in the middle — everything else runs on your origin, your database, your repo.

What does owning the stack actually change?

The default analytics setup rents both ends. Collection is vendor scripts with unrestricted access to your pages; read-back is a browser tab on someone else's dashboard, with history that expires on their schedule. The owned version keeps GA4 as the processing engine — it's good at that, and free — but moves every boundary you can move:

The whole stack on one screen
Browser
  └─ first-party collector script        (your origin, ~250 lines)
       └─ POST /api/collect              (allowlist · PII drop · rate limit)
            └─ GA4 Measurement Protocol  (the only third party)

Read-back (one service account, zero IAM roles)
  ├─ GA4 Data API        → dashboard snapshot, cached in Postgres
  └─ Search Console API  → committed archive → CLI reports for agents
Rented versus owned at each layer of the analytics stack
LayerDefault (rented)Owned
Collectiongtag.js / GTM from vendor CDNsFirst-party script, same-origin route
Data in flightWhatever the page leaksAllowlisted events, PII stripped server-side
ReportsDashboard tabs, per-view API habitsCached snapshots, refreshed deliberately
History16 months of Search Console, then goneRaw windows committed to git, forever
ConsumersHumans onlyHumans and AI agents, each bounded

Layer 1: how do you collect without third-party scripts?

Server-side tagging is the architecture; the smallest implementation of it is a collector script served from your own origin posting to one API route, which forwards to GA4's Measurement Protocol. No tagging server needed when GA4 is the only destination. The route is where ownership pays: an event allowlist, a parameter allowlist, a PII pattern drop, per-IP rate limits — and the conversion event barred from the public route entirely, so only the server-side form handler can emit it. The collector build note has the full anatomy: storage keys instead of cookies, 90-day first-touch attribution, sendBeacon delivery, and the debug endpoint that makes the Measurement Protocol observable.

Layer 2: how does read-back authenticate?

One service account, used by everything that reads. The counterintuitive part: it needs zero IAM roles — GA4 and Search Console each grant access in their own settings screens (Viewer and Restricted, respectively), and the Cloud project only holds the API enablement and the key. The two recurring traps are the multi-line private key corrupting in env vars, and access errors that read identically for four different causes — the service account guide walks the console screens and ships the check script that names the missing grant instead of printing 403.

Layer 3: how do reports come back out?

Two APIs, one habit. The GA4 Data API answers behavior questions — five small runReport calls cover realtime, monthly totals, sources, landing pages, and events. The Search Console API answers demand questions — queries, clicks, impressions — with its own sharp edges: 25,000-row pages, privacy-withheld rows that punish high-dimension pulls, a three-day settling delay, and a 16-month cliff that makes archiving non-optional. The shared habit: never query Google on a page view. The dashboard renders a Postgres-cached snapshot and touches the APIs only when a human presses Refresh; the archive pulls frozen 28-day windows and reads local files thereafter. For demand you don't capture yet — volumes and difficulty on queries you've never ranked for — the same pull-deliberately discipline extends to pay-per-call keyword data.

Layer 4: how do AI agents consume it?

Through a CLI whose every command is sized for a context window: summaries and ranked opportunity queues on stdout, hard caps with deliberately no dump flag, drill-downs bounded per session, and the quota-spending verbs reserved for humans. The CLI build note covers the design rules, most of which transfer to any tool an agent will touch.

The whole build, step by step

  1. Create a GA4 property and web data stream; note the numeric property id and the stream's measurement id.
  2. Create a Measurement Protocol API secret on the stream (Admin → Data streams → Measurement Protocol API secrets). Server-side env var only — never NEXT_PUBLIC_-anything.
  3. Write the collect route: verify shape, allowlist events and params, strip PII-looking keys, rate-limit per IP, forward to /mp/collect with non_personalized_ads: true.
  4. Write the collector script: client id in localStorage, session rollover in sessionStorage, UTM/click-id capture, page_view on history changes, sendBeacon delivery.
  5. Validate against /debug/mp/collect and GA4 DebugView before trusting anything.
  6. Emit the conversion event from your form handler server-to-server; refuse it at the public route.
  7. In Google Cloud: enable the Analytics Data API and Search Console API, create a role-less service account, download its key once.
  8. Grant the robot Viewer on the GA4 property and Restricted on the Search Console property.
  9. Store the private key base64-encoded with a decode-time PEM guard; delete the key file.
  10. Ship a check command that probes both APIs and prints the missing grant by name.
  11. Build the dashboard read path: fetch all reports in parallel, store one snapshot with a timestamp, render only the snapshot.
  12. Start the Search Console archive now — frozen 28-day windows, low-dimension shapes, committed raw. The 16-month clock is already running.
  13. When agents enter, give them a bounded CLI over the archive — never the raw files.

Questions

Do you need all four layers to benefit?

No — they're independent upgrades. A first-party collector alone removes third-party scripts from your pages. A service account alone unlocks both reporting APIs. Cached API reports alone stop dashboard-driven quota burn. Adopt in the order that fixes your current pain.

What does a stack like this cost to run?

Nearly nothing beyond what you already host. The collector and its route live inside the existing site. GA4 and the two APIs are free within quota. The only new bill is wherever the dashboard runs — here, a small app on Railway that was needed for CRM anyway.

Where does keyword and ranking data fit in?

Outside Google's APIs. Search Console reports demand you already receive — clicks and impressions on queries you rank for. Search volume and competitor rankings for queries you don't rank for come from third-party SEO data APIs, and belong in the same discipline: pulled deliberately, cached locally, never queried on page view.

Written by

Karol

Senior engineer and systems architect behind Tall Karol. Everything published here is grounded in real client work — no roundups, no tools that haven't run in production.

Why Tall KarolWork with Tall Karol

Related notes

More on systems integration

Related service: Systems Integration

Want this kind of engineering on your project?

Tall Karol takes on fractional and project-based engagements for startups and agencies.

Book a working session