Founder pricing — only 4 of 100 seats left at $49/yrthen $149Claim yours

February 10, 2026

Migrating off Google Analytics 4: a practical guide

Google Analytics 4 is free in the way a puppy is free. The license costs nothing; the cost shows up later, in sampled reports you can't trust, cardinality limits that quietly fold your data into "(other)", a consent banner you maintain forever, and the nagging question of whether shipping your visitors' behavior to Google is something your privacy policy can actually defend. If you've decided you're done with that, this is the practical guide to getting off GA4 and onto a cookieless analytics tool — TrackWhy in our examples — without losing your history or your mind.

We'll go step by step. None of this requires a data team, and most of it you can do in an afternoon.

Why teams leave GA4

Before the how, a quick honest accounting of the why, because it shapes what you migrate and what you happily leave behind:

  • Sampling. Once a report crosses GA4's processing thresholds, you're looking at an estimate, not your data. The numbers move when you reload. That's fine for a billion-dollar ad business and miserable for someone trying to make a decision.
  • Cardinality limits. High-cardinality dimensions (think URLs with query strings, or granular event params) get bucketed into (other). Your long tail vanishes exactly when you go looking for it.
  • The event model's learning curve. Universal Analytics was sessions and pageviews. GA4 is events, parameters, and a data model you have to learn before you can answer "how many people visited the pricing page." Powerful, but the ramp is steep.
  • Bounce rate that isn't. GA4 redefined bounce as the inverse of "engaged sessions" (10+ seconds, a conversion, or 2+ pageviews). It's a different metric wearing the old name, and it confuses everyone who learned analytics before 2023.
  • Consent-banner overhead. GA4 sets cookies and processes personal data, so in the EU you need consent. That means a banner, a CMP, Consent Mode v2 wiring, and a measurable chunk of your traffic clicking "reject" and disappearing from your numbers.
  • Data ownership and PII under GDPR. The data lives in Google's systems. IP addresses and identifiers are personal data. Schrems II made cross-border transfers a genuine legal question. A cookieless tool that doesn't collect personal data sidesteps most of this by design.

Step 1: Export your GA4 history before you lose it

This is the step people skip and regret. GA4 only retains event-level data for 2 to 14 months depending on your retention setting — and the default is on the short end. Your old data is on a clock. Export it before you turn anything off.

Three ways to get it out, roughly in order of completeness:

  1. BigQuery export (best). Admin → Product links → BigQuery links. This streams raw, unsampled, event-level data to BigQuery daily. It's the only export that gives you the real grain, and it backfills nothing — so link it today even if you're not migrating until next month.
  2. The GA4 UI. Any report has a share/download action for CSV, PDF, or Google Sheets. Good for the handful of dashboards you actually look at; not a real archive.
  3. The Data API (runReport). Programmatic, scriptable, but aggregated and subject to the same sampling as the UI. Useful for pulling specific metric histories into a warehouse.

Important framing: you are not moving this data into the new tool. Your historical GA4 data stays in Google (and your BigQuery archive). TrackWhy starts collecting clean, unsampled data the moment you install it. You keep the past where it is and start the future fresh — no messy import, no fake backfill.

Step 2: Swap the tag

Find every place the GA4 snippet lives — usually a gtag.js loader in your <head>, or a Google Tag Manager container, or a Next.js <Script> component. Remove it:

<!-- delete this -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX');
</script>

Drop in the replacement:

<script defer data-site="YOUR_SITE_ID" src="https://cdn.trackwhy.com/tw.js"></script>

That's the whole install. It's 1.7 KB, it's cookieless, and because it stores nothing on the device and collects no personal data, you don't need a consent banner to load it. One line in, several Google products out.

Step 3: Map your events

GA4 events come in four flavors, and they map cleanly to a single custom-event model:

  • Automatically collectedpage_view, session_start, first_visit. Pageviews are tracked by the script automatically; the rest are GA4 plumbing you don't need to recreate.
  • Enhanced measurementscroll, click (outbound), file_download, video_start, form_submit. These become explicit custom events you fire when you care about them.
  • Recommendedpurchase, sign_up, add_to_cart, etc. Map the ones that matter to your funnel.
  • Custom — whatever you defined yourself. Carry these over as-is.

Sending a custom event is one call:

trackwhy('event', 'signup', { plan: 'pro', source: 'pricing' })

The pattern: anything that was a meaningful GA4 event becomes a named custom event with a small, low-cardinality set of properties. Resist the urge to pour every parameter you ever logged into the new tool — clean inputs are the whole point.

Step 4: Re-create goals and conversions

GA4 "conversions" (now "key events") become funnels and key events in the new model. Define the steps you care about — /pricingsignuppurchase — and let the funnel report show drop-off at each stage.

For anything revenue-shaped, skip the proxy entirely. Instead of marking a purchase event and trusting GA4's attribution, connect Stripe revenue attribution directly. Real money, joined to the session and source that produced it, beats an event you hope fired with the right value attached.

Step 5: Validate — and expect the numbers to differ

Run both tools side by side for a week or two before you trust the new one alone. Then brace yourself: the numbers will not match, and that's correct.

Why they diverge:

  • GA4 samples large reports; TrackWhy counts every event.
  • The two tools dedupe and sessionize differently.
  • Bounce rate will look dramatically different. TrackWhy computes it the classic, honest way — a bounce is a session with a single pageview. GA4's bounce is the inverse of an engagement heuristic. These are different questions; expect different answers.

Don't chase a 1:1 reconciliation — you'll never get one, and the attempt teaches you nothing. Instead, check that trends agree (do both show Tuesday's spike?) and that top pages and sources rank the same way. Directional agreement is the real validation.

Step 6: Cut over

Once you trust the data:

  1. Remove GA4 completely — the snippet, the GTM tags, the Consent Mode wiring, and any leftover dataLayer pushes.
  2. Update your privacy policy. No more "we use Google Analytics cookies." And because the new tool is cookieless, you can very likely drop the cookie consent banner for analytics altogether. Confirm with whoever owns your compliance, but this is usually the moment that banner dies.
  3. Confirm DNS/CDN. Make sure cdn.trackwhy.com loads from your pages, isn't blocked by your CSP (script-src), and resolves in production. A staging-only install is a classic way to "lose all your traffic" on launch day.

Common gotchas

  • UTM handling. Your existing utm_* links keep working — campaign attribution is parsed from the URL, no gtag config needed. Audit your tagged links so the new source/medium breakdown is clean from day one.
  • SPA route changes. Single-page apps don't trigger a full page load on navigation. If history.pushState doesn't fire a pageview automatically, send one on route change: trackwhy('pageview') inside your router's after-navigation hook.
  • Bot filtering. Make sure known bots and crawlers are filtered, or your "traffic" will include a lot of robots. GA4 did this silently; verify your new tool does too.
  • Referrer spam. Junk referrers (buttons-for-website.com and friends) inflate referral numbers. A maintained referrer-spam blocklist keeps the report honest.

Wrap-up

Migrating off GA4 isn't a data-engineering project — it's deleting one script tag, adding a smaller one, mapping a handful of events, and updating a privacy policy you were probably overdue to touch anyway. The hard part is psychological: trusting numbers that finally don't move when you reload, and explaining to a stakeholder why bounce rate "changed" when really it just started being honest.

Keep your GA4 history in Google for as long as you need it. Start collecting clean, unsampled, cookieless data today. And enjoy not maintaining a consent banner for a tool you no longer run. Analytics with teeth — and no cookies in them.