Module · The Tracking Layer
Wire up your tracking layer. One container, GA4 and Meta together.
By the end of this module you'll have a Google Analytics 4 property and a Google Tag Manager container wired into your site, and that one container fires both your Google and Meta tracking from a single place. You'll confirm with your own eyes that a real conversion lands in both, all of it done before you spend a dollar on ads, so campaigns become measurable instead of a guess.
About 45 minutes. Mostly careful clicking, not thinking. Why before every step, so you're never following a recipe you don't understand.
Reads two ways → As Module 2 of the Growth track in The Local Newsletter Machine, and as a standalone "GA4 + GTM Conversion Tracking" playbook for any website. Newsletter-specific bits are flagged as skippable asides.
Where this fits
In the course
Plugs straight into Module 1. There you made a Meta pixel and copied its ID, here it gets a home. Once this is live and verified, the very next module is ad campaigns. Campaigns without tracking are just burning money and hoping.
Standalone
Works for any site needing conversion tracking, a service business, a store, a course, a client. GTM is the universal switchboard. Swap "subscribe" for your conversion (a lead, a purchase, a booking) and it's identical.
What you'll walk away with
- A GA4 property with a Measurement ID (
G-XXXXXXX) in hand
- A GTM container with a Container ID (
GTM-XXXXXXX) in hand
- One container firing four tags: GA4 page views, GA4 sign-up conversions, Meta page views, Meta subscribe conversions
- The container wired into your website through a single field
- Proof it works: a real test conversion showing up in both Google and Meta
Before you start · pre-flight
- Your Meta Dataset / Pixel ID from Module 1 (the 15–16 digit number), pasted somewhere handy
- Your website's public URL
- A Google account you're logged into (which one matters, see Step 0)
- Access to your site's settings where you can paste a tracking ID (newsletter aside: Beehiiv → Website → Settings → Pixels)
- 45 uninterrupted minutes
Newsletter asideThis whole module is taught through the real build of my Orange County newsletter. Wherever you see a placeholder like G-XXXXXXX or [YOUR DATASET ID], that's your value, I've kept my actual IDs out on purpose. The steps are identical for any newsletter in any market.
The mental model · read this first
GTM is a switchboard. You install it once.
Most people bolt tracking on the messy way: a Google snippet here, a Meta pixel there, a TikTok tag over there, each pasted into the site's code separately. Six months later nobody knows what's installed, half of it's broken, and adding a tool means editing code again.
Google Tag Manager fixes this. Picture a switchboard:
Your website ──fires events──► dataLayer (a belt of "things that happened")
│
▼
Google Tag Manager ← ONE snippet on your site
the switchboard that listens
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
GA4 tags Meta pixel tags (future: TikTok,
(Google Analytics) (Facebook / Instagram) Google Ads…)
You install one thing on your site: the GTM container. From then on, every tracking tool you'll ever want lives inside GTM, turned on with clicks instead of code. Your site pushes "events" onto a dataLayer (a conveyor belt announcing "someone loaded a page," "someone subscribed"). GTM listens and fires the right tags to the right platforms.
Three words and you'll follow everything:
- GA4, Google Analytics 4. Google's free analytics. Its address is a Measurement ID,
G-XXXXXXX.
- GTM, Google Tag Manager. The switchboard. Its address is a Container ID,
GTM-XXXXXXX.
- The Meta pixel/dataset, the thing you made in Module 1. It rides inside GTM.
Why route Meta through GTM at allOne source of truth. One snippet, one place to manage everything, one place to add the next tool. It also lets you fire a clean, structured "subscribe" conversion with the subscriber's email attached (hashed, more later), which is exactly what Meta needs to optimize your ads well.
Order of build: GA4 first (get its ID), then GTM (the container that holds everything), then wire GTM into your site, then verify. Let's go.
0
Step zeroDecide which Google account owns this
GA4 and GTM both live under a Google account. Moving them later is a genuine pain.
Why this matters more than it looksIf you ever hand this newsletter to a partner, sell it, or bring on a team, you want its analytics in a dedicated brand Google account, not tangled in your personal Gmail with your photos and your taxes. Clean separation now saves an ugly migration later.
- Decide: a dedicated brand Google account (recommended) or your personal one.
- Make sure you're logged into that account before you start. Juggling several Google logins? Google adds
?authuser=N to the URL, confirm by clicking your avatar (top-right) and reading the email.
Gotcha · the silent wrong-account trapGoogle will happily let you build your entire setup under whatever account is "active," and the avatar is just a colored circle with a letter, easy to glance past. Click it and read the actual email before you create a single thing. Building under the wrong account and finding out later means recreating everything. (For the OC Buzz build I used a dedicated brand Google account, fully separate from my personal one.)
1
Step oneCreate the GA4 property (grab the Measurement ID)
The bucket that collects your site's analytics, and generates the ID GTM needs.
Why nowGTM needs this ID to send data to Google. Make GA4 first, pocket the ID, move on.
- Go to analytics.google.com. New to it on this account? You'll get a "Start measuring" welcome, click it.
- Account setup: name the account (your brand), leave data-sharing at defaults. Next.
- Property setup: name it ("[Your Brand] Website"), set time zone and currency to your local ones. Next.
- Business details: pick an industry and size. (For a newsletter I use "News" and the smallest size.) Next.
- Business objectives: "Generate leads" and "Understand web traffic" are the right two for a newsletter. Create.
- Accept the Terms of Service, a real legal agreement for your business. I also tick the GDPR Data Processing Terms box (more privacy-protective even if you're US-only). I Accept.
- Set up a data stream: choose Web, enter your URL and a stream name, leave Enhanced Measurement ON. Create stream.
[ Screenshot ] the web data stream setup with the URL and Enhanced Measurement toggle on
Grab the Measurement ID. After the stream is created, GA4 shows a "Google tag" install panel. Inside it your ID appears in a line like:
gtag('config', 'G-XXXXXXX');
That G-XXXXXXX is your Measurement ID. Copy it next to your Meta dataset ID. Do NOT install the snippet it's showing you, GTM fires GA4 for us. Close the panel.
[ Screenshot ] the gtag snippet panel with the G-XXXXXXX Measurement ID highlighted
Gotcha · Measurement ID vs Stream IDThe same screen also shows a long all-numbers "Stream ID." That is not the one. Your tracking ID always starts with G-. Grab that one.
Why you don't paste the snippetInstall GA4's snippet directly and fire GA4 through GTM (which you're about to) and you double-count every page view. One source of truth. GTM only.
2
Step twoCreate the GTM container (grab the Container ID)
The switchboard itself, the one thing that actually goes on your site.
- Go to tagmanager.google.com (confirm you're still in the right Google account).
- Click Create Account.
- Account name: your brand. Country: yours. Leave "share data anonymously" unchecked.
- Container name: your domain. Target platform: Web.
- Create, then accept the GTM Terms of Service (a separate agreement from GA4's, I tick the GDPR box and click Yes).
- An "Install Google Tag Manager" box appears with two snippets. You don't need to copy these, your platform has a dedicated field for the ID (Step 5). Just note the Container ID at the top,
GTM-XXXXXXX. Close the box.
[ Screenshot ] the GTM workspace with the GTM-XXXXXXX container ID visible top-center
You now have both IDs: your GA4 G-XXXXXXX and your GTM GTM-XXXXXXX, plus your Meta dataset ID from Module 1. Everything else happens inside this container.
3
Step three · the heart of itBuild the container
Variables → triggers → template → tags. Ingredients first, then assemble.
We build, in order: 3 variables (reusable values), 2 triggers (the "when should a tag fire?" rules), 1 template (the official Meta building block), 4 tags (the things that actually fire).
Why this orderTags depend on triggers and variables, so we make the ingredients first, then assemble. Build it out of order and you'll be clicking back and forth.
3a · Variables
Left nav → Variables → New (under "User-Defined Variables"). Create three:
ID-GA4, type Constant. Value: your G-XXXXXXX. Save.
ID-META, type Constant. Value: your Meta dataset ID from Module 1. Save.
DLV-EMAIL, type Data Layer Variable. Name: eventProps.email (version 2). Save.
Why constants for the IDsYou reference each ID in multiple tags. Store it once in a named constant, and if it ever changes you update one place instead of hunting through every tag. DLV-EMAIL is how GTM reads the subscriber's email off the dataLayer belt to attach it to the Meta conversion.
[ Screenshot ] the three user-defined variables listed
3b · Triggers
Open Triggers → New. Create two:
Subscriber Sign-Up, type Custom Event. Event name: signedUp. Save.
Page View (History Change), type History Change. Save.
Why a custom event for the subscribeYour platform announces a successful subscribe by pushing an event named signedUp onto the dataLayer. This trigger listens for exactly that word, then tells your conversion tags to fire. The History Change trigger catches page views on modern sites that swap pages without a full reload.
Gotcha · the event name is platform-specificsignedUp is what Beehiiv fires. On a different platform, or for a different conversion (a purchase, a form submit), the name differs. You'll confirm the real name in verification, if your test subscribe doesn't fire, the event name is the first thing to check.
[ Screenshot ] the two triggers listed
3c · Add the Meta Pixel template
GTM has a gallery of pre-built, vetted tag templates. Meta's official one is the safe way to fire the pixel, you never want to hand-write pixel code.
- Left nav → Templates → Search Gallery (under "Tag Templates").
- Search
Meta Pixel and pick the one by facebook (verified checkmark).
- Add to workspace, then accept its permissions (it needs to inject the Meta script and read the dataLayer, expected for any pixel).
[ Screenshot ] the "Meta Pixel by facebook" template in the gallery
Gotcha · search "Meta Pixel," not "Facebook Pixel"Meta retired the old "Facebook Pixel" listing. Search Facebook Pixel and you'll get a pile of third-party knockoffs and miss the real one. The official template is titled "Meta Pixel" by facebook, that exact name, with the verified check.
3d · The four tags
Open Tags → New for each. Tip: to reference a variable in any field, type {{ and pick it.
Tag 1 , GA4 Configuration
- Type: Google Tag. Tag ID:
{{ID-GA4}}
- Trigger: Initialization – All Pages (GTM offers this automatically, accept it)
Boots up GA4 on every page and sends the page view.
Tag 2 , GA4 Event – Subscribe
- Type: Google Analytics: GA4 Event. Measurement ID:
{{ID-GA4}}
- Event Name:
sign_up. Add Event Parameter: method = email
- Trigger:
Subscriber Sign-Up
Records a conversion in GA4 every time someone subscribes.
Tag 3 , Meta Event – Page View
- Type: Meta Pixel (your gallery template). Pixel ID:
{{ID-META}}
- Event: Standard → PageView
- Triggers: All Pages and
Page View (History Change) (add both)
Fires the Meta page view so Meta can build audiences and see traffic.
Tag 4 , Meta Event – Subscribe
- Type: Meta Pixel. Pixel ID:
{{ID-META}}
- Event: Standard → Subscribe
- Turn ON Enable Advanced Matching → add Email =
{{DLV-EMAIL}}
- Trigger:
Subscriber Sign-Up
Fires the Meta "Subscribe" conversion with the subscriber's email attached, dramatically improving Meta's ability to match the conversion and optimize your ads.
[ Screenshot ] the four tags listed with their triggers
⚠ Gotcha · turn OFF the Conversions API opt-in on both Meta tagsThe Meta Pixel template has a checkbox, on by default, reading "Opt in to a Meta-enabled Conversions API integration." Leave it on and you're authorizing Meta to set up server-side tracking under an extra set of terms, a more advanced setup you don't need yet. Uncheck it on both Meta tags. You can add the Conversions API deliberately later if you ever want it.
On privacy · the email is hashed, not sent rawAdvanced Matching doesn't send the plain email to Meta. The pixel SHA-256 hashes it in the browser before it ever leaves the page, so Meta gets a scrambled fingerprint it can match against, never the readable address. The standard, privacy-respecting way to improve match quality.
3e · Publish
Top-right, click Submit → Publish. Name the version ("Initial tracking setup") and publish.
Why publish mattersUntil you publish, your container is a draft. The snippet on your site would load an empty container and fire nothing. Publishing pushes your tags live.
4
Step fourWire GTM into your website
Put the one thing on your site: the container ID, in a single field.
- Go to your site's tracking settings. (newsletter aside: Beehiiv → Website → Settings → Pixels.)
- Find the Google Tag Manager field. Paste your
GTM-XXXXXXX.
- Leave the standalone Google Analytics and Meta Pixel fields BLANK.
- Save (and republish the site if your platform asks).
[ Screenshot ] the platform's pixel settings with only the GTM field filled in
⚠ Gotcha · GTM field ONLY, the one that quietly doubles your dataYour platform probably has separate fields for a GA4 ID and a Meta Pixel ID too. Do not fill those in. You're already firing GA4 and Meta through GTM. Fill the standalone fields as well and every page view and conversion counts twice, wrecking your numbers and your ad optimization. GTM field only. The others stay empty.
Newsletter aside · Beehiiv applies this instantlyOn Beehiiv the pixel setting goes live the moment you save; no full site republish needed. Other platforms may require a republish for the change to reach the live site.
5
Step five · do not skipVerify it actually works
You built a lot of invisible plumbing. Prove it fires before you trust it with ad money.
Two checks: page views, then a conversion. Open your browser's developer tools → Network tab for both.
Check A, page views are firing
- Open your live site in a normal tab. In the Network tab, filter for
collect (GA4) and facebook.com/tr (Meta).
- Reload. You should see a GA4
collect request carrying your G-XXXXXXX, and a facebook.com/tr request carrying your dataset ID with ev=PageView.
⚠ Gotcha · the "I just published and nothing fires" panicRight after publishing, your own browser often still has the old, empty container cached, so your tags look dead even though everything's correct. This trips up almost everyone. Fix: hard-refresh (or clear cache), or wait a few minutes for the cache to expire, then reload. Fresh visitors get the live container immediately, it's only your already-visited browser holding the stale copy. Don't start tearing apart your tags over this.
Check B, a real conversion fires
- Do a real test subscribe on your site (an email you control).
- Watch for a GA4
collect with en=sign_up, and a facebook.com/tr with ev=Subscribe and a hashed email attached.
- Cross-confirm: GA4 → Reports → Realtime shows a
sign_up; Meta Events Manager shows a Subscribe within minutes to a few hours.
[ Screenshot ] the Network tab showing the facebook.com/tr Subscribe request and the GA4 sign_up collect request
Gotcha · "no data yet" is normal for up to 24hGA4 and Meta both warn "no data received" for a while after setup even when everything's firing. The live/realtime views update fast; the main dashboards lag. If your Network tab shows the requests going out with the right IDs, you're done, the dashboards catch up.
Gotcha · an occasional 503 on a GA4 hit is Google's server, not youGA4's collector sometimes returns a transient error on an individual hit and quietly retries. As long as the request went out with the right G-XXXXXXX and event name, your setup is correct.
Both checks pass? Your tracking layer is live and verified. You can now see every subscriber a campaign produces, which means you can finally spend on ads like an adult.
How you know it worked
Verification checklist.
- GA4 property created; Measurement ID
G-XXXXXXX copied down
- GTM container created; Container ID
GTM-XXXXXXX copied down
- Container has 3 variables, 2 triggers, the Meta Pixel template, 4 tags, and is Published
- Conversions API opt-in is unchecked on both Meta tags
- GTM field on your site is filled; standalone GA4 and Meta fields are BLANK
- On a page load: GA4
collect and Meta PageView both fire
- On a test subscribe: GA4
sign_up and Meta Subscribe (hashed email) both fire
- The
sign_up shows in GA4 Realtime and the Subscribe in Meta Events Manager (within 24h)
What's next
Ad campaigns, where the tracking pays for itself.
You now have a foundation that sees. Because every subscribe fires a clean, attributed conversion to Meta, you can run a cold campaign optimized directly toward subscribers, not clicks, not "reach," actual people joining your list, watch your true cost-per-subscriber in real time, and scale what works. Tracking first, then spend. That's the whole reason we did this before touching a campaign.
Gotchas cheat-sheet
Every landmine, one place.
| # | Landmine | The move |
| 1 | Building under the wrong Google account | Click your avatar, read the email, confirm before creating anything |
| 2 | Grabbing the Stream ID instead of the Measurement ID | The tracking ID starts with G-; that's the one |
| 3 | Pasting the GA4 snippet on your site | Don't, GTM fires GA4; the snippet double-counts |
| 4 | Searching "Facebook Pixel" in the gallery | Search "Meta Pixel" by facebook (verified), the real one |
| 5 | Conversions API opt-in left ON | Uncheck it on both Meta tags (defaults to on) |
| 6 | Forgetting to Publish the container | Draft = empty on your site; Submit → Publish |
| 7 | Filling the standalone GA4/Meta fields too | GTM field ONLY, the others blank, or you double-track everything |
| 8 | "Nothing fires after publish!" | Your browser cached the old empty container; hard-refresh or wait, then reload |
| 9 | Panicking at "no data received" | Normal for up to 24h; trust the Network tab / Realtime view |
| 10 | Test subscribe doesn't fire the conversion | Check the custom-event name matches what your platform actually pushes |
The Local Newsletter Machine
Build it. Grow it. Sell the ads.
Build your local newsletter, grow it with AI, and turn it into a real local media business, taught through the live OC Buzz build.
Get on the waitlist →