Meta Ads and GA4: A UTM Reconciliation Workflow
Why do Meta and GA4 report different numbers?
They measure different things. Meta's default window credits a purchase up to 7 days after a click or 1 day after a view, dated to the ad impression and matched across devices. GA4 credits sessions by the UTM or referrer data they arrive with, and UTM values are case-sensitive. Compare both with store orders.
Meta and GA4 will not agree, and forcing them to is the expensive mistake. Each answers a different question under different rules, so the job is to make the gap explainable: one UTM taxonomy both sides can join on, three revenue ledgers compared side by side, and a fixed order for diagnosing whatever is left.
If your store, not GA4, is the number that disagrees, read Why does Meta show more purchases than Shopify? and Why do Meta and my store report different purchase counts?
Why don’t Meta and GA4 match?
Meta lists the reasons its numbers differ from third-party tools such as GA4 (conversion count differences):
- People, not cookies. Meta measures across devices for people with Meta accounts; cookie-based tools measure cross-device journeys less well.
- Views count. Meta can credit a purchase to an ad someone saw but did not click. Tools that rely on clicks and referrers struggle to.
- Ad blockers. A blocked pixel means Meta reports fewer conversions than your own data.
- Lost referrers. A purchase in a new tab, or after the referrer is lost, leaves the analytics tool unable to credit the ad.
- Different definitions and aggregated reporting. The same metric name can be calculated differently, and limited data sharing leaves Meta with less information.
Two Ads Manager rules add to that. New ad sets default to a 7-day click or 1-day view attribution setting, and Ads Manager gives the conversion to the last ad clicked, or the last ad viewed if there was no click, dating it to the impression rather than the purchase (how conversions are attributed).
What UTM taxonomy should Meta ads use?
Google says to always set utm_source, utm_medium and utm_campaign, strongly recommends utm_id and utm_source_platform too, and warns that values are case-sensitive: utm_source=google and utm_source=Google are different values, so use lowercase as standard (GA4 campaign URLs).
| Parameter | Contract | Example value rule | Rejection condition |
|---|---|---|---|
utm_source |
Referring platform | One fixed lowercase value per platform, such as facebook and instagram (or Meta’s {{site_source_name}}) |
Missing or mixed case |
utm_medium |
Paid-media class | paid_social, which GA4’s Paid Social rule matches |
Organic and paid traffic share a value |
utm_id |
Stable campaign join key | Meta’s {{campaign.id}} dynamic parameter |
The key changes during the reporting window |
utm_campaign |
Readable campaign label | {{campaign.name}}, fixed at first publish |
Used as the join key instead of utm_id |
utm_content |
Creative join key | Meta’s {{ad.id}} |
Ad copy used as the key |
utm_term |
Optional audience cell key | Test-cell ID, never personal data | Free text or undeclared audience labels |
Why these rules hold:
- Paid Social. GA4 puts a session in Paid Social when the source matches its list of social sites and the medium matches
^(.*cp.*|ppc|retargeting|paid.*)$. Channel rules are not case-sensitive and cannot be edited (default channel group), but your report rows are, soFacebookandfacebookstill split. - IDs over names. Meta’s dynamic parameters include
{{campaign.id}},{{adset.id}},{{ad.id}}and name versions. Name-based values freeze at the name used when the ad was first published, so a renamed campaign keeps reporting its old name (dynamic URL parameters). - Change them early. Meta warns that editing URL parameters after publishing may send the ad set back into the learning phase.
- Check for added parameters. Since 1 January 2024 Meta may add source, medium and ID parameters to ad traffic on its own, and Shopify merchants may get them too (add URL parameters). Parameters you set in the URL parameters field override duplicates in the website URL.
For a copy-ready template, the full list of Meta’s dynamic parameters and where to paste them in Ads Manager, see What UTM parameters should I use for Facebook ads?.
How do the three revenue figures differ?
| Figure | Definition | Source and time basis | What it can answer |
|---|---|---|---|
| Meta-attributed revenue | Revenue Meta credits under the recorded attribution setting | Frozen Meta export; attribution setting and extraction time recorded | What Meta credited under that method |
| Analytics-attributed revenue | Recognized order revenue joined to sessions and assigned by the declared analytics method | GA4 export; session and order join and reporting identity recorded | What the analytics rule assigned to paid-social sessions |
| Commerce-ledger revenue | Authorized orders kept after the declared cancellation and refund cutoff | Order and refund ledger by order time | How much revenue the business recognizes; it does not identify a cause |
UTM join coverage = (eligible paid-social sessions carrying every required valid UTM key) ÷ (eligible paid-social sessions). Our working definition. Source and window: GA4 session export for the declared acquisition window. Limitation: consent and cross-device gaps remain.
Recognized-revenue reconciliation gap = (commerce-ledger revenue − analytics-attributed recognized revenue) ÷ (commerce-ledger revenue). Our working definition. Source and window: joined commerce and analytics records after the same maturity cutoff. Limitation: the gap is diagnostic, not incremental lift.
In what order should I diagnose the gap?
- Freeze time zones and date fields: Ads Manager dates a conversion to the ad impression, while your store dates the order when it is placed.
- Compare spend scope: the same campaigns and the same dates in the Ads Manager date picker and in GA4.
- Validate UTM keys: open the ad, go to Tracking > URL parameters, and confirm the values survive every redirect to the landing page.
- Join transaction IDs between GA4 purchase events and store orders.
- Separate consented sessions from sessions analytics could not record.
- Classify cross-device and unmatched orders rather than deleting them.
- Remove duplicates: GA4 deduplicates purchases that share a transaction ID, on web streams only.
- Apply cancellation and refund state to every order in the window.
- Label the residual as a method difference instead of forcing it to zero.
On step 7, Google’s rules are specific: transaction IDs must be unique, must not contain anything that identifies a customer, and must never be an empty string, because GA4 deduplicates every purchase sent with transaction_id="" together (transaction IDs).
What does reconciling a duplicate look like?
Synthetic example. These values are chosen to demonstrate the method; they are not client results, industry benchmarks or a forecast.
Assume the store has 80 eligible paid orders while collected purchase events contain 92 rows. Matching stable order identifiers reveals 12 duplicate rows. After removing those duplicate occurrences, both sets contain 80 unique orders. You have resolved one collection discrepancy, not proved that platform attribution will match store orders or that every campaign deserves credit.
Decision and limits. Keep the reconciliation output as counts and opaque identifiers, with no personal details in exported URLs or shared screenshots. If unique orders still differ, inspect date boundaries, refunds, consent and collection gaps. Never create replacement purchase events simply to force two reports to match.
Change note, September 8, 2026: added this worked example and its decision boundary. The example does not imply review by a named external expert.
For a human review of your own acquisition, store conversion and contribution data, see the growth consultation scope and sample action plan.
Can software help?
Bach.ai connects to your Meta ad account, audits it, and ranks the revenue leaks it finds by estimated dollar impact, each with a proposed fix. It applies a fix on Meta only after you approve it. The reconciliation above still needs your GA4 and order exports.
For the account-wide checks around tracking, see the Meta ads audit checklist.
FAQ
How do you reconcile Meta Ads, GA4 and store revenue?
Freeze one lowercase UTM taxonomy with Meta’s campaign ID in utm_id, then join GA4 purchases to store orders by transaction ID before you compare anything to Meta. Only after time zones, duplicates and refunds are handled should you label what remains as a difference in attribution method.
Should utm_campaign carry the campaign name or the campaign ID?
Use both, in different fields. Put Meta’s {{campaign.id}} in utm_id as the join key, because IDs never change, and the readable name in utm_campaign. Name-based dynamic parameters freeze at first publish, so a renamed campaign keeps reporting its old name in GA4.
Why does GA4 show my Facebook traffic as two sources?
Because UTM values are case-sensitive. Google’s own example is that utm_source=google and utm_source=Google are different values, so facebook and Facebook become two rows. GA4’s channel rules ignore case, so both may still land in Paid Social, but your source reports stay split.
When should an unexplained revenue gap stay labelled rather than fixed?
When time zones, refund state, consent gaps, duplicates and unmatched orders have all been checked and some revenue still does not reconcile. That residue is the method difference between Meta’s attribution and GA4’s. Label it with its size and leave it; inventing events to close it corrupts both reports.
What can a three-ledger reconciliation establish?
It shows which revenue each system credits and why the figures diverge, so budget decisions rest on explained numbers. It cannot replace missing source records, and it cannot turn platform credit or observed association into incremental impact; that needs a controlled test with a holdout.
Sources: Meta Business Help Center, Conversion count differences with third-party tools, How conversions are attributed, Dynamic URL parameters, Add URL parameters; Google Analytics Help, Collect campaign data with custom URLs, Default channel group, Minimize duplicate key events with transaction IDs (all checked 1 Oct 2026).