Leaving GA4 is not hard because the new tool is hard. It is hard because GA4 holds years of your history hostage to retention windows, export limits, and a data model nothing else shares. A migration that starts with "install the new tag" and ends with "delete the old one" will lose data in between.
This GA4 migration checklist is the version that doesn't. Six steps, in strict order, because the order is the point: you export before you install, you overlap before you compare, and you compare before you delete. If you want the deeper product comparison first, read ClickStream vs GA4; this page is the operational plan.
Why You Need a GA4 Migration Checklist at All
Two properties of GA4 make an unplanned exit expensive:
- Rolling retention windows. On the free tier, GA4 keeps event-level data for 2 months by default, extendable to 14 months (GA360 reaches up to 50 months). Standard aggregated reports persist longer, but the granular data behind explorations and funnels ages out continuously. Every month you delay, a month of detail quietly expires.
- Precedent. When Universal Analytics was sunset, Google stopped processing data on July 1, 2023, and permanently deleted remaining UA data in 2024. Teams that planned to export "later" lost their history outright. There is no reason to assume a future GA4 transition would be gentler.
The cost of migrating off GA4 is mostly the cost of what you failed to export before you started. Everything else is just work.
Step 1: Export Everything GA4 Will Give You
Do this first — before installing anything new — because some exports only capture data going forward.
Link the BigQuery export immediately
GA4's BigQuery link is the only way to get raw event-level data out, and it only collects from the day you link it — there is no backfill. Even if you are leaving Google entirely, link it now so the months between decision and decommission are captured at full granularity. Daily export caps apply on the free tier, and querying the data requires a Google Cloud project, but a raw archive you own beats aggregates you don't.
Export the reports you actually use
You will not export everything, so export what people reference:
- Monthly aggregates as CSV — sessions, users, conversions by channel, by month, as far back as your retention allows. These become your trend-continuity baseline.
- Acquisition and campaign reports — source/medium and campaign breakdowns for at least the periods your team compares against ("versus last year" needs last year).
- Top landing pages and conversion paths — anything a quarterly deck has ever cited.
- The Data API for scripted pulls — if you have engineering support, scripted pulls of aggregated metrics get around UI export tedium.
Document what can't be exported
Audience definitions, key-event configurations, custom dimensions, and attribution settings don't export as data — capture them as documentation (screenshots and a spreadsheet are fine). You will need them in Step 2, and they disappear with the property.
Step 2: Map Events to a Clean Taxonomy
Most GA4 properties accumulate years of taxonomy debt: purchase, Purchase, and purchase_new_v2 coexisting peacefully. Migration is the one moment you can fix this without breaking historical comparisons, because the comparison line breaks anyway.
Inventory your events in four buckets — auto-collected, enhanced measurement, recommended, and custom — then map each to a deliberate convention. In ClickStream's case, standard interactions (page views, scrolls, clicks, form submits, file downloads, video plays) are auto-captured, so the mapping burden falls almost entirely on custom events:
| GA4 Event | Migration Action |
|---|---|
page_view, scroll, click, form_submit, file_download |
Auto-captured — no mapping needed |
gtag('event', 'purchase', {value: 99}) |
window.clickstream.trackEvent({name: 'purchase_completed', value: 99}) |
gtag('event', 'sign_up', {method: 'email'}) |
window.clickstream.trackEvent({name: 'sign_up', label: 'email'}) |
gtag('config', ..., {user_id: ID}) |
window.clickstream.identify(user.email) |
ClickStream event names are free-form strings, so the naming convention is yours to define — define it once, in writing, before anyone ships a tag. The same discipline applies to campaign parameters: if your UTMs are inconsistent, fix them in the same sprint (see the UTM taxonomy guide for conventions that survive growth).
Step 3: Run Parallel Tracking During Cutover
Never hard-cut an analytics migration. Run both systems side by side for at least two to four weeks — ideally one full business cycle — so you have overlapping data to validate against.
The two tags coexist safely. GA4 sets and reads its own _ga cookies through gtag.js; ClickStream ships a 344-byte loader that pulls a ~56.5 KB gzipped bundle from your own subdomain and relies on its own server-set first-party cookie. Neither touches the other's cookies, globals, or events — they observe the same pages independently:
<!-- Existing GA4 tag (keep this for now) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
<!-- Add ClickStream below -->
<script src="https://t.yourdomain.com/sdk.js" data-key="cs_live_xxxxxxxxxxxxxxxxxxxxx" async></script>
Before the tag goes live, point a CNAME subdomain at ClickStream so cookies are set server-side on your own domain — our implementation guides cover the DNS setup, and the install page covers script-tag, NPM, mobile, and server options. The overlap period costs nothing to start: the Hobby tier is free for 50,000 human pageviews a month, no card required (see pricing).
Step 4: Validate — Your Numbers Will Not Match, and That's Expected
The most common migration failure isn't technical. It's a stakeholder looking at two dashboards, seeing different numbers, and concluding the new tool is broken. Set the expectation up front: no two analytics systems report the same numbers, because they disagree about what to count before they ever count it.
| Why numbers differ | GA4 | ClickStream |
|---|---|---|
| Bot filtering | Silently excludes known bots; you can't see what was removed | Classifies bots across 11 categories (158 named bots, 38 AI agents), shows them, and excludes them from billing — you're billed on human pageviews only |
| Sampling | Explorations sample above ~10M events per query; user counts are approximated | No sampled scoring — every event processed at ingest |
| Identity & cookies | JavaScript-set cookies capped at 7 days by Safari ITP; users modeled via Google Signals / User-ID | Server-set first-party cookie persists ~400 days (the browser maximum), so more returning visitors are recognized |
| Session definition | 30-minute inactivity timeout plus its own engaged-session rules | 30-minute inactivity timeout; definitions differ enough to shift session counts |
| Processing latency | Reports can lag 24–48 hours | Real-time; scores computed at the edge (p95 under 3 ms per event in a CI-enforced benchmark) |
During the overlap, document each delta and its cause. Expect ClickStream to report more unique returning visitors (longer-lived cookies recognize people GA4 forgets) and fewer "new" ones, and expect visible bot traffic GA4 never showed you. Then agree acceptance criteria with stakeholders before cutover: not "the numbers match," but "the deltas are stable week over week and each one is explainable." That's what a trustworthy migration looks like. If bot volume surprises anyone, that conversation matters beyond analytics — it distorts A/B tests and paid-media metrics too.
Step 5: Redirect Dashboards and Reports
Analytics data has consumers, and they don't migrate themselves:
- Inventory every consumer of GA4 data — Looker Studio dashboards, scheduled email reports, spreadsheet pulls, board decks, agency reports. If a number appears anywhere monthly, find its source.
- Rebuild each report in the new platform, mapping to equivalent metrics rather than chasing identical values.
- Annotate the cutover date on every long-running chart. A trend line that silently switches measurement systems mid-series will mislead someone within a quarter.
- Keep GA4 read-only during transition so people can answer "what did we report last March" from the old system while the new one accrues history.
For warehouse users: ClickStream offers self-serve CSV export on Growth+ plans, and raw-event Parquet export (BigQuery/Athena/DuckDB-compatible) on request on Scale+ — so your archived GA4 BigQuery data and your new event stream can eventually sit side by side in the same warehouse.
Step 6: Decommission GA4 Cleanly
Once stakeholders have signed off on the new source of truth:
- Remove the
gtag.jssnippet from your HTML or templates. - If you use Google Tag Manager, remove the GA4 tag from the container — deleting only the HTML snippet leaves GTM re-injecting it.
- Search the codebase for leftover calls:
grep -r "gtag\|googletagmanager" . - Update your consent manager so GA4 no longer appears in the analytics category (and confirm your new tool does — the cookie consent guide covers the analytics-specific wiring).
- Verify in DevTools that no requests go to
google-analytics.comorgoogletagmanager.com, and no_gacookies are set. - Keep the GA4 property itself (and your BigQuery archive) — it costs nothing and preserves read access to whatever history remains.
What Does Not Migrate — Being Honest About It
No platform, ClickStream included, can import your historical GA4 raw events as if they were natively collected. The data models are fundamentally different — GA4's event schema, session logic, and identity spaces don't translate into another system's without producing numbers that are neither GA4's nor the new tool's. Anyone who promises a lossless historical import is promising something the data can't support.
What you keep instead: your BigQuery archive (raw events, queryable forever), your CSV aggregates (trend continuity), and your parallel-tracking window (the bridge between measurement eras). That combination answers virtually every real question — "how did last year compare" comes from the archive; "how are we doing now" comes from the new system; the overlap explains the difference between them.
The new history you accrue is also structurally different. With server-set first-party cookies persisting ~400 days, cross-session identity survives in ways GA4's 7-day JavaScript cookies never allowed — the mechanics are covered in first-party cookie persistence. And if you're still choosing a destination, the honest guide to Google Analytics alternatives compares the field beyond ClickStream.
The Bottom Line
A GA4 migration succeeds in the ordering: export first, map deliberately, overlap before comparing, compare before deleting. Teams that follow the sequence keep their history, their taxonomy improves in transit, and their stakeholders trust the new numbers because every delta was explained rather than discovered. Teams that skip Step 1 find out what the retention window meant.
For the week-by-week implementation version of this checklist — with owners, statuses, and the full event-mapping tables — see the GA4 migration guide in our implementation docs.