Appearance
Migrating from Google Analytics 4: a worked example
This guide walks through a real migration: the corporate website of a B2B software company (lead forms, resources, webinars and a blog), which ran on a GA4 property with a BigQuery export. The company and its figures are left out; the steps are the same for any GA4 property. It covers what to look at in GA4 before you move, how to import the full history, and how each GA4 setting translates in Eureka.
1. Audit the GA4 property first
Before importing anything, list what the property actually contains. On this site the audit found:
| Finding | Why it matters for the migration |
|---|---|
| Tracking had nearly stopped for a month (a warning banner about the tag) | Imported history is only as good as what was collected. Note the gap so nobody reads it as a real drop. |
A created event (middle_of_funnel) used a regex that started with | | It matched every page, so its history counts every page view. Re-create it correctly in Eureka; keep the imported rows for comparison only. |
| 19 key events, only 4 with data | Import them all as key events and goals, then remove the dead ones in Eureka. |
| 37 custom dimensions, 8 of them user-scoped | Map each one with its scope so it is still usable as a dimension after the move. |
| Event data retention of 2 months | GA4 explorations cannot go further back. The BigQuery export is not affected (see below). |
| Data-centre and 1280×720 traffic counted as human | Typical of scripted browsers and agents. Eureka classifies these separately. |
(not set) as the second landing page | Sessions starting with an event before the page view. Eureka's Setup health flags the same pattern. |
Eureka runs most of these checks for you once the tag is live: see Setup health.
2. Import the full history from BigQuery
GA4's data retention setting (2 or 14 months) applies to explorations only. The daily BigQuery export tables (events_YYYYMMDD) keep everything since the export was linked. On this property that is every day since December 2020, while GA4 itself could only explore the last 2 months.
The import job, in short:
yaml
import_id: corporate-site-ga4-history
property_id: env:AH_PROPERTY_ID
mapping: ga4
mapping_profile: corporate-site # custom events, custom dimensions, key events -> goals
source:
type: ga4-bq
project: <gcp-project>
dataset: analytics_<property-id>
location: europe-west1 # the dataset's region
include_intraday: false
maximum_bytes_billed: 20000000000 # cost guard, per day
time_zone: America/New_York # must match the Eureka property
currency: USD # must match the Eureka property
date_range: {start: 2020-12-08, end: 2026-10-30}
privacy:
user_id_salt: env:AH_IMPORT_SALT- Time zone and currency: GA4 cuts its daily tables on the property's time zone and stores revenue in the property currency. Create the Eureka property with the same values (here New York and USD), or daily totals will not line up.
- Cost: the importer reads one daily table per query, never the
events_*wildcard, with a byte limit per query. - Re-runs: event IDs are derived from the source content, so re-importing a day replaces it without duplicates.
A mapping profile is a per-property section of the GA4 mapping file (importers/mappings/ga4.yaml, under profiles:); the repository ships the profile and job of this example. Run eureka-import plan first. Its coverage report lists every field found in the export and where it lands. Then run the import over the whole period. See Importing history for the full procedure.
3. Translate the GA4 configuration
eureka-import definitions --config job.yaml prints what the mapping profile creates in Eureka. Send it to the Admin API, or create the same objects in Admin.
| GA4 | Eureka | On this property |
|---|---|---|
| Key events | Key events + event goals | 19 key events (lead, event_registration, demo_request, pricing_request, newsletter…) |
| Key events used as leads | Lead events with a stage | lead, contact, newsletter… = new; demo_request, pricing_request, free_trial = qualified; purchase = converted |
| Custom dimensions, event scope | Custom definitions, scope event; the parameter is kept in the event's properties | page_category, pageTopics, ebook_name, webinar_name, A/B test campaign ids… |
| Custom dimensions, user scope | Custom definitions, scope user; the user property is kept on every event of the visitor | company enrichment fields, visitor ids, login state |
| Created events | Created events, evaluated when reports run (they also apply to past data) | top_of_the_funnel, bottom_of_funnel, the corrected middle_of_funnel |
| Calculated metrics | Calculated metrics | none on this property |
| Data filters (Testing / Active / Inactive) | Data filters, same three states | an internal-traffic filter |
| Data retention | Data retention, in months, up to your plan's limit | 2 months in GA4; set at least 14 in Eureka |
| Redact data | Redact data (query parameters and e-mail addresses) | |
| Report library, collections | Library: published collections appear in the menu | "Life cycle" and "Business objectives" collections |
| Audiences | Segments, listed in the Audiences report | 31 audiences, most of them remarketing lists |
Created events in GA4 are written to the BigQuery export as their own events, so their history comes over with the import. Their definitions do not: create them again in Eureka, and fix any broken regex while you are at it.
4. Run both tools in parallel
Keep the GA4 tag for 2 to 4 weeks after the Eureka tag goes live: Eureka reuses the tags already on the site, so nothing has to be retagged. Over the overlap, compare sessions, users and key events day by day. Expect Eureka to show fewer human sessions: agents, scripted browsers and data-centre traffic are counted in their own actor families instead of being mixed with people.
5. What you get that GA4 did not show
- Agents and bots as their own audience: action agents, indexing bots, AI readers, and mixed sessions where a person hands over to an agent.
- Pages read by AI assistants and server-side crawler visits.
- Setup health, which catches a broken tag, a catch-all regex or a silent key event before they distort your reports.
- Full event-level history for as long as your plan's retention allows, from the import onwards.