| utm_source | |
| utm_medium | cpc |
| utm_campaign | {{campaign.name}} |
| utm_id | {{campaign.id}} |
| utm_term | {{ad.name}} |
| utm_content | {{adset.name}} |
A Meta Ads account is one buying surface that fans out into four placement networks: Facebook, Instagram, Messenger, and Audience Network. The same campaign object can deliver into all four, and the same URL Parameters field on a given ad applies regardless of which placement actually rendered the impression.
That’s the conceptual handle. The mechanical reality is messier: Meta’s UTM tokens use double curly braces ({{campaign.id}}), which Google Ads and most other platforms don’t; placement dimensions return long underscore-separated strings; fbclid is the click identifier that’s easy to break by adding it manually; and the iOS 14.5 ATT change pushed the entire post-click measurement story onto a server-side track (Conversions API) most teams still haven’t fully wired up.
This page covers the URL Parameters field, the dynamic-token catalog, the fbclid story, the Pixel/CAPI handshake, and the small list of common ways the setup goes wrong.
The recommended UTM template
| Parameter | Value | Why |
|---|---|---|
utm_source | facebook | GA4 default channel grouping classifies facebook as Paid Social. meta lands in Unassigned. |
utm_medium | cpc | Matches GA4’s paid-channel regex. paid-social also works but is less universal across analytics tools. |
utm_campaign | {{campaign.name}} or {{campaign.id}} | Name is human-readable in reports; ID is rename-safe. Pick one and stick with it. |
utm_id | {{campaign.id}} | Always the numeric ID. Powers GA4’s Campaign ID dimension and any cost-data import. |
utm_term | {{ad.name}} | The most actionable level for paid social — creative-level performance is what you tune. |
utm_content | {{adset.name}} | Audience differentiation. Add {{site_source_name}} here if you want to split FB vs IG. |
This is the template the mini builder above hands users when they pick “Meta Ads.” It’s standards-only — no custom params — and it works across every downstream tool that reads utm_*.
Dynamic tokens
Meta calls them “dynamic URL parameters.” The catalog is small — about a dozen useful ones — but the syntax is what trips people up. Double curly braces, all lowercase, dot-separated path ({{campaign.id}} not {campaignid} not ${CAMPAIGN_ID}). Get the syntax wrong and the token outputs literally as {{campaign.id}} in your URLs.
A few practical points the headline list won’t tell you:
- Names break, IDs don’t.
{{campaign.name}}returns whatever the campaign is currently called. Rename the campaign tomorrow and every UTM-based join in your reporting silently desyncs against historical rows. The numeric ID variants ({{campaign.id}},{{adset.id}},{{ad.id}}) are stable forever — they outlive renames, restructures, and even archival. {{placement}}is wordy. Returns strings likeFacebook_Feed,Instagram_Stories,Instagram_Reels,Audience_Network_Native. Useful as autm_contentcompanion, but plan for the length — it can add 30+ characters to your URL.{{site_source_name}}is the cheap split. Returnsfb/ig/msg/an. If you want to break Facebook traffic from Instagram traffic in default GA4 reports without splitting your campaigns, drop this intoutm_content.- No conditional macros. Meta has no equivalent of Google Ads’
{ifsearch:}/{ifmobile:}. If you need different UTMs per placement, you split the ad set rather than templating around it.
Where to put the URL parameters
Meta’s URL Parameters field lives at the ad level, in the ad’s tracking section (Ads Manager → edit ad → bottom of the page → URL parameters). There’s no campaign-level or ad-set-level template that cascades down. Every ad has its own field.
In practice this means: when you create a new ad, paste the template. When you duplicate an ad, the template duplicates with it. When you bulk-edit, you edit the URL Parameters field across many ads in one operation.
utm_source=facebook&utm_medium=cpc&utm_campaign={{campaign.name}}&utm_id={{campaign.id}}&utm_term={{ad.name}}&utm_content={{adset.name}}
Note: paste it without the leading ?. Meta appends the parameters after whatever query string the destination URL already has, joining with ? or & as needed.
fbclid and the _fbc cookie
When a user clicks a Meta ad, Meta appends fbclid=… to the destination URL. Your site’s Pixel reads the URL on landing, persists the value into the _fbc cookie (with a fb.<subdomain_index>.<creation_time>.<fbclid> format), and includes the cookie value in every subsequent Pixel event so Meta can join site-side conversions back to the click.
Three failure modes worth knowing:
- Stripped fbclid. Link shorteners, marketing-automation wrappers, AMP cache, and aggressive consent-mode scripts can strip
fbclidbefore it reaches your domain. Without it, the_fbccookie isn’t created and Meta has no click identifier to join against. Result: Pixel events without click attribution, and your campaign data degrades. - Manual
&fbclid=…in URL Parameters. Adding it manually creates duplicate fbclid params, the second of which is what gets persisted into the cookie — Meta then can’t match the cookie value back to the click that generated it. Don’t add fbclid to your UTM template. - First-party cookie blocked. Safari ITP and most ad-blockers drop the
_fbccookie or shorten its lifetime. The Conversions API (below) is the answer for this — it reads click metadata server-side where ITP doesn’t apply.
Pixel + Conversions API
Meta’s measurement story has two halves. The Pixel is the JavaScript tag on your site — fires events client-side, reads cookies, sends to Meta. Easy to install, blocked by everything from ad-blockers to Safari ITP to the user choosing No Tracking on iOS.
The Conversions API (CAPI) is the server-side complement. Same events, sent from your server (or your CDP, or a CAPI partner) to Meta’s Conversions API endpoint, with the _fbc value, hashed customer data, and an event_id. The Pixel and CAPI fire the same event — Meta deduplicates them by matching event_id + event_name.
For most advertisers in 2026 the practical setup is: Pixel for the 70% of clicks where it works, CAPI for the iOS-and-blocker tail it can’t reach, deduplication via event_id so you’re not double-counting. CAPI alone (without the Pixel) is technically possible but rare — the Pixel is what scrapes click metadata into the _fbc cookie that CAPI then ships to Meta.
For the receiver-side picture — how GA4 classifies utm_source=facebook, what happens when fbclid arrives without UTMs — see Google Analytics .
Limitations and gotchas
- Double-curly syntax is Meta-specific. Don’t muscle-memory
{campaign.id}from Google Ads — it outputs literally. Always{{campaign.id}}on Meta. - Case sensitivity matters. GA4 preserves source/medium case in dimensions.
Facebook,facebook, andFACEBOOKcreate three rows. Lowercase everything. - No campaign-level URL template. Every ad has its own URL Parameters field. Use bulk edit when you need to update many at once; budget time for it.
- fbclid won’t survive certain redirects. Test your redirect chain in incognito and confirm
fbclidlands on the final destination domain. {{site_source_name}}is not a substitute for splitting placements. If your conversion-rate gap between Facebook and Instagram is large, separate the ad sets — don’t just tag and hope the optimizer figures it out.
Verification
Three steps, in order:
- Preview the URL in Ads Manager — at the bottom of the ad-edit screen, Meta shows the resolved URL with tokens substituted against a sample value. If it doesn’t look right here, it never will live.
- Click your own ad in incognito. Run the targeting against yourself, click the ad, inspect the URL on your landing page. Confirm UTMs landed AND that
fbclidwas appended automatically. - Verify in Events Manager.
Events Manager → your Pixel → Test Events— fire a test event from your landing page and confirm it shows up with the click metadata Meta needs for attribution.
Common problems
- My UTMs come out as literal
{{campaign.id}}in URLs. Tokens were pasted into the destination URL field instead of the URL Parameters field. Move them. - My Meta campaign shows as
(direct)/(none)in GA4. fbclid was stripped between click and landing. Common culprits: link shorteners, AMP cache, marketing-automation wrappers, hash-routing apps. Add the manual UTMs to the URL Parameters field as a fallback — they survive even when fbclid doesn’t. - My Pixel and CAPI events are double-counting in Events Manager.
event_idmismatch between Pixel and CAPI. Both sides must send the sameevent_idfor the same event. Check your CAPI implementation generates a stable ID and your Pixel reads the same value (via dataLayer or server-set cookie). - My iOS conversions look way lower than Android. Aggregated Event Measurement and shorter ATT-era attribution windows. Check your AEM priority ranking — Purchase should be rank 1 for ecommerce, not buried below upper-funnel events.
- My UTM template uses campaign names and they keep breaking. You hardcoded
utm_campaign=summer_sale_2026instead ofutm_campaign={{campaign.id}}. Names change when you rename; numeric IDs don’t.
| Token | Description | Substituted at |
|---|---|---|
| {{campaign.id}} | Numeric campaign ID. Stable across renames — recommended as utm_id. | Click time |
| {{campaign.name}} | Human-readable campaign name. Renaming a campaign breaks downstream joins. Prefer the ID for utm_campaign too. | Click time |
| {{adset.id}} | Numeric ad set ID. | Click time |
| {{adset.name}} | Ad set name. | Click time |
| {{ad.id}} | Numeric ad ID. | Click time |
| {{ad.name}} | Ad name. | Click time |
| {{placement}} | Where the ad rendered — e.g. Facebook_Feed, Instagram_Stories, Audience_Network_Native. Returned as a long string with surface and slot separated by underscore. | Click time |
| {{site_source_name}} | Two-letter source code — fb (Facebook), ig (Instagram), msg (Messenger), an (Audience Network). Useful for splitting Facebook vs Instagram traffic when utm_source is shared. | Click time |
| fbclid | Facebook Click Identifier — appended automatically by Meta when a user clicks an ad. Don't add manually. Powers the _fbc cookie that links pixel events to the original click. | Click time |
