How to set up Meta Conversions API with server-side GTM

Browser tracking leaks. Ad blockers stop the Meta Pixel from loading, Safari shortens the life of cookies set by JavaScript, and a buyer who closes the tab before your thank-you page finishes loading never fires a Purchase event. Meta’s Conversions API (CAPI, which plenty of people search for as “Conversion API”) closes part of that gap by letting your own server send events to Meta directly.

This guide covers the route most GTM users take: a server-side Google Tag Manager container that receives events from your website and forwards them to Meta. You will create the server container, connect your web container to it, add the Conversions API tag, deduplicate against the browser pixel, and test the whole chain before publishing. Set aside an afternoon. The tags take little time to configure, and testing takes longer.

Why run the Conversions API next to the Meta Pixel

Meta recommends what it calls a redundant setup, where the same events travel through both the browser pixel and the Conversions API. The pixel is fast and captures rich browser signals. The server connection delivers events the browser would have dropped. Once both are live, Meta needs a way to see that a pixel Purchase and a server Purchase describe the same order. That is deduplication, and step 7 covers it.

Server-side tagging has a second benefit: your server container sees every payload before it leaves. You can strip fields, hash personal data and honor consent choices in one place instead of across a dozen browser tags.

How the setup works

Data moves through a short chain. Your web container sends GA4 events to your tagging server instead of straight to Google. The server container receives them through its GA4 client, the Conversions API tag converts each event into Meta’s format, and the tag posts it to graph.facebook.com. Meanwhile the browser pixel keeps firing, with the same event ID attached.

Visitor’s browser GTM web container Meta Pixel (fbq) Server container GA4 client Conversions API tag Meta Events Manager GA4 events server event browser event, same event_id on both paths so Meta can deduplicate
Event flow for Meta Conversions API through a server-side GTM container.

The tagging server should run on your own domain, for example metrics.yoursite.com. Google’s custom domain article names same-origin serving (a path on your main domain, like yoursite.com/metrics) as the best practice, and the examples in its Cloud Run guide use that pattern. A plain subdomain works for many sites, but if cookie lifetime in Safari matters to you, read that article before you pick.

What you need before you start

  • A GTM web container already installed on your site, with the Meta Pixel base code loading through it.
  • GA4 events or a data layer you can send to the server. Purchase and lead events need at least a value and currency.
  • Your Meta Pixel (dataset) ID and full control of the business portfolio that owns it.
  • For self-hosting, a Google Cloud account with billing enabled and the Project Creator and Billing Account User roles.
  • Access to your DNS settings.

Step 1: create and provision the server container

In Google Tag Manager, open the account menu and choose Create container. Name it, pick Server as the target platform, and click Create. GTM then offers to provision a tagging server for you. The automatic option creates a Google Cloud project and deploys the container to Cloud Run in a few clicks, which is the path Google recommends over App Engine.

Look at the price before you click through. Google estimates each Cloud Run server at about $45 a month (1 vCPU, 0.5 GB of memory, CPU always allocated) and suggests running at least two so an outage does not lose data. At that minimum you are near $90 a month. Google expects an autoscaling group of 2 to 10 servers to handle roughly 35 to 350 requests per second, depending on how many tags you run.

Logging is the surprise on many bills. Once a tagging server handles more than about a million requests a month, per-request logs can cost real money. Google documents how to exclude request logs with a filter in the Logs Router, and it takes five minutes. If you would rather not run Google Cloud at all, managed hosts such as Stape, Taggrs and owntag deploy the container for you. Their pricing differs, so check each one.

Step 2: map a custom domain and test the server

Map your Cloud Run service to the domain or subdomain you chose, then add the DNS records Cloud Run shows you. Next, in the server container, go to Admin, then Container Settings, click Add URL and paste your server URL. Save.

Two quick checks tell you whether the server is alive. Click Preview in the workspace and confirm the debug page loads. Then open your server URL with /healthy added to the end, for example https://metrics.yoursite.com/healthy. A working server answers with the text “ok”. If you mapped several domains to one server, add each URL in Container Settings and keep the path identical on all of them.

Step 3: send events from your website to the server

Your web container needs to send events to the new URL. The most common method uses the GA4 tag. Open your Google tag in the web container and add the server_container_url parameter under configuration settings, with your tagging server URL as the value. Older setups call this parameter transport_url. Every GA4 event that tag sends will now go to your server first.

Server containers include a GA4 client by default. Open Clients in the server container and make sure it is present with the default GA4 paths selected. The other popular route is Stape’s Data Tag and Data Client pair, which works the same way but carries more fields. Pick one and stay with it.

Before moving on, put both containers in Preview mode and trigger a page view. The web preview should show a request to your tagging server (select the GA4 Measurement ID in the preview filters to see it), and the server preview should list an incoming request with the event name underneath. If nothing arrives, fix it now. Everything after this step depends on it.

Step 4: generate a Meta access token

Open Meta Events Manager, select your pixel under Data sources, and go to the Settings tab. Scroll to the Conversions API section and click Generate access token under the manual setup option. Copy the token right away. Meta displays it once, though you can generate a new one later, and older tokens stay valid.

Treat the token like a password. A tidy habit is to create two Constant variables in the server container, one for the pixel ID and one for the token, and reference them from the tag. Then you rotate the token in one place instead of hunting through tags.

Step 5: install the Conversions API tag template

Two templates are in common use. Meta maintains one called Conversions API Tag, published under facebookincubator. Stape publishes another called Facebook Conversions API. Both send standard events to Meta. This walkthrough uses Stape’s because it exposes extra options, such as cookie generation and an event enhancement feature. Field names in Meta’s version differ a little, and the logic is the same.

In the server container, open Templates, click Search Gallery in the Tag Templates box, find the template and choose Add to workspace. If you prefer a manual route, download the template file from the Stape GitHub repository, then use Templates, New, Import.

Step 6: build the tag and its trigger

Go to Tags, click New, and choose Facebook Conversions API as the tag type. These are the fields to fill in.

  • Event Name Setup Method: Inherit from client tries to match each GA4 event to a Meta standard event and falls back to a custom event when it cannot. Override lets you choose the Meta event yourself, and Stape calls it the preferred way because you control the whole payload. Start with Inherit to prove the plumbing works, then switch purchase and lead events to Override.
  • Action Source: Website, for events that come from browser activity on your site.
  • Facebook Pixel ID and API Access Token: use the two constant variables from step 4.
  • Test ID: paste the test event code from the Test Events tab in Events Manager while you are testing. You will remove it later.
  • Generate _fbp cookie if it does not exist: leave this on. It only creates the cookie when one is missing.
  • Use Optimistic Scenario: leave it off while testing. When on, the tag reports success without waiting for Meta’s answer, which hides errors you need to see.

The Event Enhancement option stores user data in an HTTP-only cookie (gtmeec) and adds it to later events that lack it, which can lift match quality. It also means more personal data lives in a cookie, so only enable it if your consent setup covers that.

For the trigger, create a Custom trigger set to fire on Some Events, with two conditions: Client Name equals GA4, and Event Name matches RegEx for the events you want Meta to receive. A starting pattern is ^(page_view|view_item|add_to_cart|begin_checkout|purchase|generate_lead)$. Resist the urge to fire on all events. Every stray GA4 event becomes a custom event in your Meta data, and cleaning that up later is tedious.

If your web container passes consent state to the server, use the tag’s consent setting so it only sends when marketing consent exists. Leave it off if consent is not being passed, or the tag will quietly stop sending anything.

Step 7: set up deduplication with a shared event ID

This step decides whether your numbers are right. Meta treats two events as the same when the pixel’s eventID matches the server’s event_id and the pixel’s event name matches the server’s event_name. If it sees the same pair on the same pixel within 48 hours, it discards the later one. Without a shared ID, every purchase counts twice.

The simplest reliable approach is to generate the ID once, in the data layer, and let both tags read it. For a purchase, the order ID works well, since it is already unique:

window.dataLayer = window.dataLayer || [];
dataLayer.push({
  event: 'purchase',
  event_id: 'order_10482', // or crypto.randomUUID() for events without an order ID
  ecommerce: {
    transaction_id: 'order_10482',
    value: 59.90,
    currency: 'USD'
  }
});

Create a Data Layer Variable named something like DLV – event_id. Then wire it into both paths.

  • GA4 event tag in the web container: add an event parameter called event_id with the value {{DLV – event_id}}. The server tag picks the ID up from event data automatically, and you can override it in the tag’s advanced settings.
  • Meta Pixel event tag in the web container: pass the ID as the fourth argument of the fbq call, as Meta’s documentation shows.
<script>
fbq('track', 'Purchase',
  {value: {{DLV - ecommerce.value}}, currency: {{DLV - ecommerce.currency}}},
  {eventID: {{DLV - event_id}}}
);
</script>

Watch the event names. GA4 sends purchase, the server tag converts it to Purchase, and your pixel sends Purchase. If you rename events on one side only, the pair no longer matches and deduplication fails even with identical IDs.

Meta also offers a fallback: matching on event name plus fbp or external_id. It is weaker. It generally only works when the browser event arrives first, and a server event is not discarded if no browser event reached Meta in the previous 48 hours. Use event_id.

Step 8: send better customer data to raise match quality

Meta uses customer information parameters to tie a server event to a real person, and the quality of that match feeds back into ad delivery. Meta’s GTM guide ranks the parameters by how much they help. In the GA4 event model, most fields come through as user_data values.

ParameterGA4 field namePriority
Emailuser_data.email_addressHigh
Click ID (fbc)x-fb-ck-fbcHigh
Browser ID (fbp)x-fb-ck-fbpMedium
External IDx-fb-ud-external_idMedium
Phone numberuser_data.phone_numberMedium
Countryuser_data.address.countryMedium
First and last nameuser_data.address.first_name, user_data.address.last_nameLow
City and postal codeuser_data.address.city, user_data.address.postal_codeLow

Meta’s own template reads the fbp and fbc values from those x-fb-ck parameters. Stape’s template reads the _fbp and _fbc cookies directly, which is one reason many people prefer it. Either way, an email address captured at checkout or on a lead form is the single most useful addition. The tag hashes personal fields before sending, and you can confirm that in the outgoing request during testing.

To pass an email from the data layer, create a Data Layer Variable that maps to eventModel.user_data.email_address and add it to the GA4 event as a user_data field. Only send what you have legitimate consent to collect.

Step 9: test the whole chain

Put the web and server containers in Preview mode, open your site and perform the actions you want to track. In the server preview, click the incoming request. You should see an outgoing request from the Conversions API tag to graph.facebook.com. Open it and check four things: the access token appears in the URL, the body contains the parameters you expect, user data fields look hashed, and the response status is 200.

Then switch to Events Manager and open the Test Events tab. Events from your session should appear within seconds. The Received From column should read Server for the Conversions API events, and clicking one shows the stored parameters. Your browser pixel events appear here too, and a matching pair shows as deduplicated.

When everything looks right, delete the test event code from the tag. Meta states plainly that the code is for testing only and has to be removed from production payloads. Leaving it in is the classic mistake: events keep flowing into the test tool and never reach your real reporting.

Step 10: publish and watch the first week

Submit and publish the server container first, then the web container changes. After that, return to Events Manager and look at the Overview tab for your pixel. Each event should show as received from both browser and server. Check Diagnostics for warnings, and look at the event match quality score for key events like Purchase. Give it a day or two before judging the numbers, since reporting lags.

Compare Meta’s purchase count against your store’s orders for the same period. A healthy, deduplicated setup should land close to your real order count. If Meta reports roughly double, deduplication is broken. If it reports fewer than your pixel alone did, something is blocking the server path.

Troubleshooting common problems

The tag never fires. Check the trigger first. If it filters on Client Name equals GA4, and the event arrives through a different client, nothing matches. The preview shows which client claimed each request.

Meta returns an error status. Open the outgoing request in the server preview and read the response body. Mismatched pixel IDs and tokens, a token generated for a different dataset, and malformed parameters are the usual causes.

Purchases count twice. The event_id is missing on one side, differs between the two, or the event names do not match. Compare the eventID in the pixel request with the event_id in the server request for one real order.

Match quality stays low. You are probably not sending an email or phone number. Add them at the points where the visitor gives them to you, and make sure the fbc value survives from the ad click to the conversion.

Events vanish after you add consent logic. If the tag’s consent setting is on but the web container never passes consent state, the server treats every visitor as not consenting. Either pass the state or switch the setting off until you do.

Other ways to connect to Meta

Server-side GTM is not the only route. Stape, among others, offers a Conversions API Gateway and a Signals Gateway that need less configuration, and the Gateway is a reasonable choice if you have no GTM server container and no appetite to build one. The trade-off is control: with your own container you decide exactly which fields go out, and you can reuse the same server for GA4, Google Ads and other platforms.

Frequently asked questions

Do I still need the Meta Pixel if I use the Conversions API?

Meta recommends running both with deduplication. Stape’s guidance agrees: either remove the web tracking or set up deduplication, and the second is preferred. A CAPI-only setup works, but you give up browser signals the pixel collects.

What happens if I skip the event ID?

Meta falls back to matching event name with fbp or external_id, but that only works reliably when the browser event comes first. Plan on event_id for anything that affects reporting.

How much does server-side GTM cost?

On Cloud Run, Google’s estimate is about $45 a month per server, with two servers recommended. Managed hosts set their own prices. Request logging adds cost at high volume, so filter it out.

Can I use something other than GA4 to feed the server?

Yes. Stape lists GA4 and its Data Tag with Data Client as the two most popular options. The Conversions API tag works with any client that produces the standard event model.

Checklist before you go live

  • The /healthy URL answers “ok” and Preview loads.
  • The GA4 tag sends to your server URL and the server preview shows each event.
  • The Conversions API tag returns status 200 and shows Server in the Test Events tab.
  • The pixel and the server send identical event names and event IDs.
  • The test event code is gone from the tag.
  • Request logging is filtered, and the token lives in a constant variable.

Sources and further reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top