Typeform Meta (Facebook) Conversions API Tracking
I recently had a client who needed Facebook or Meta Conversions API (CAPI) Conversion Tracking for Typeform. This setup is one of the most difficult ones. Took a long time to figure this one out. Very technical. But Iāll provide a rough summary of the technical solution, which you can use for inspiration.
Hire Me and Get The Jew Typeform Tracking App
š Pssst.
You can also get in touch with me, if you want the most accurate Typeform Meta CAPI setup, done-for-you, quickly, with zero effort.
Why Meta Conversions API tracking is important for Typeform?
The better your conversion tracking is, the more money you will make.
Simple as that.
If you run ads on meta and the action you want your users to take is a typeform submission, meta-convergence API is a non-negotiable.
All ad platforms, whether it's Google Ads, Meta Ads, TikTok or others, rely on conversion tracking. If these platforms do not receive information on who converted and who did not, they are essentially shooting blind. Your ads will simply not work effectively. Especially for Meta, the entire AI advertising targeting algorithm is based on conversion tracking.
Event Match Quality Score and Results
The more information about the conversion you share with Meta, the higher your Meta Event Match Quality Score will be. If you share the email, the phone number, first name, last name, IP address, device, click ID, country, zip code, gender, every parameter will increase your event match quality score and we will track more conversions.
Read this post on Reddit that talks about the results that happen if you can improve your meta event match quality score. Meta Event match quality score (EMQ) is the ranking meta gives you on how good your conversion tracking is. 0 to 10.
The Reddit post is a very good primer on why a more advanced Typeform Facebook / Meta CAPI installation makes you tons of money:
When you improve your EMQ score from 8.6 to 9.3, here's what we consistently see:
Cost per acquisition drops by 18% ($42 ā $35)
Customer match rates increase by 24%
ROAS improves by 22% on average
Ad spend efficiency increases by ~$2,100/month at $1,000 daily spend
Server-Side Implementation The most critical element that 90% of stores get wrong is proper server-side implementation. Here's what you need to know:
Data Completeness: Your server needs to send the right identification parameters in the right format. Focus on high-priority parameters:
Email (High priority)
Click ID/fbc (High priority)
Phone number (Medium priority)
External ID (Medium priority)
Browser ID/fbp (Medium priority)
Processing Speed: Real-time event processing is crucial. Batched events significantly reduce your EMQ score. Your events should reach Meta within seconds, not minutes or hours.
Event Accuracy: Deduplication is essential. Double-counting events destroys your EMQ score and wastes ad spend.
The key idea here was that the improvement in the Reddit post Meta Event Mtch Quality Score was only +0.8. Oftentimes with proper Meta conversions API setup, for an iframe-based conversion like Typeform, the difference may be more like +6.0
Absolutely insane increase. It will literally make or break your ads. You ads will make or break your business. I am not exaggerating because we have literally saved the businesses from going bankrupt just by installing high quality conversion tracking.
How good our tracking is determines how many conversions we will see. See below example where I'm using different conversion tracking methods to track conversions for Acuity Scheduling (which is vety similar iFrame-based system as Typeform)
You may think you have conversion tracking but you are only tracking 30% or 50% of your conversions. With proper Typeform Meta conversions API installation, we're usually able to get that to 90 to 95%. This means the meta-AI targeting algorithm has twice the data to figure out who is most likely to convert to target your ads only to the people who will actually buy. It is an unfair competitive advantage where you will beat the competition.
How to do Typeform Meta / Facebook Conversions API setup?
Short answer: it's difficult.
Typeform is already difficult to track because it is an iframe-based widget. Basically it is not part of your website. It is a website within a website. To track Typeform conversions there are two methods. We can either use a JavaScript iframe message listener and embed Typeform inside your website or we can use a redirect to a custom thank you page. Both have their pros and cons.
Redirect
The redirect to a thank you page has the benefit of having field values available in the URL. We can do /thank-you?email=example@gmail.com&phone=+1555555&firstname=john&lastname=smith
etc. This helps us if we want to do meta Facebook Pixel Advanced Matching and send hashed first-party data via our browser side tracking for a solid foundation. It's also a method to do Google Ads enhanced conversion tracking if you also run Google Ads for your Typeform conversion.
The downside of this is that it is
often difficult to manage. You may need multiple thank you pages.
There will be personal information or PII in the URL. Google Analytics and Metapixel do not really allow this, so you may need a very complex URL cleaning function before you send Google Analytics or Metapixel events.
Google Analytics is easier because we can choose the page view URL we want to send and it doesn't have to be the exact one the user is viewing.
Metapixel is more difficult because it will use whatever URL we are browsing. We cannot remove this sensitive information, but we also kind of have to remove it or Meta will start blocking our parameters and may start blocking other things in our ad account as well.
We will often get bad referrers in Google Analytics. We may see traffic that seems to be originating from typeform.com, so we have to exclude those referrers for clean analytics.
We often get duplicate conversions. In Google Ads this usually is not a big problem because for every Google Ads conversion we can choose if we want to track one or every. However, in Meta we do not have this option. Users often accidentally refresh the thank you page because iOS, Safari for example will reload the last visited page. If user reopens their browser they will send duplicate conversions. Our numbers will not be accurate.
We have to block this page from search engines. It is in theory possible that bots or crawlers or people will visit this thank you page by accident.
Super annoying detail in Typeform (unlike for example Tally forms or Jotform): we do not get the unique submission ID of this specific form submission. We really would prefer to have this, especially for MetaConversions API and Pixel to deduplicate events.
JavaScript iFrame Listener
The other method which I usually prefer is listening to a message type form sends via the iframe. So we add a listener script to Google Tag Manager or our website.
The benefits of this method is that we don't need to create thank you pages and we get the ID of the form and the unique submission ID. The unique submission ID will match our server side event so it is perfect for deduplication, Facebook, Meta Conversions API and Meta Pixel events.
The downside is that we do not get the field values. So for example, meta pixel advanced matching is almost impossible. We could send a webhook to a Cloudflare worker or whatever that would get us this information, but that's probably too much work.
The more practical downside is that we cannot do Google Ads enhanced conversion tracking with the email address and phone number for more accurate conversion tracking. And also we cannot create different conversions based on the field values. So let's say you ask a question like, do you want residential solar panels or business solar panels. And you want a different conversion based on their Typeform field value. This is only possible with the redirect method.
So both the Typeform redirect and the Typeform iframe message conversion tracking method have some pros and cons. For perfection we unfortunately usually need to do the thank you page, although it is more work.
The principles for Typeform Facebook / Meta Conversions API setup
So here's how to actually do the most accurate meta Facebook tracking for Typeform.
Create a Typeform redirect to a custom thank you page. Add the field values as URL parameters.
Set up browser-side conversion tracking in Google Tag Manager by creating a custom HTML function that will look for the Typeform form ID (e.g. ā?typeform_form=PVB2Cā) in the URL. This way we can use one thank you page in our website for multiple type forms and we don't need different pages like /thank you-1 or /thank you-2 etc.
This custom HTML script will need to be, unfortunately, pretty complicated because it needs to look for these parameters in our URL. It needs to store them in a variable or cookie or local storage. And then it needs to clean up our URL from PII, which again Google Analytics for example does not allow.
example.com/thank-you?typeform_form=PVB2C&email=john.smith@gmail.com&phone=+1555555
š
example.com/thank-you?typeform_form=PVB2C
You can use JavaScript, or Google Tag Manager Trim Query template
<!--Clean up url of PII-->
<script>
// Store the original URL
var originalURLBeforeUrlCleanup = window.location.href;
console.log("POTENTIALLY VIOLATING URL FIXER: Original URL stored: ", originalURLBeforeUrlCleanup);
// Construct the new URL// Use the variable provided by another function that has trimmed the query
var newURL = {{Trim Query - Page URL (ct_ PII Cleaned)}};
console.log("POTENTIALLY VIOLATING URL FIXER: New URL after removing specified parameters: ", newURL);
// Update the browser URL without refreshing the page
if (window.history && window.history.replaceState) {
window.history.replaceState({}, document.title, newURL);
console.log("POTENTIALLY VIOLATING URL FIXER: Browser URL updated to: ", newURL);
} // Push a custom event to the data layer after PII cleanup
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'initialization-after-pii-cleanup',
'originalURLBeforeUrlCleanup': originalURLBeforeUrlCleanup,
'newURL': newURL
});
console.log("Data Layer event 'initialization-after-pii-cleanup' pushed"); </script>Ideally, whatever method we use, we will create a new page view trigger. This page view trigger will replace our normal page view triggers in our Google Tag Manager for all pages. The way this custom page view trigger works is that it will either see that we are not on a thank you page and there is no personal information in our URL we should clean up. Or it will detect the personal information and will clean it up using TrimQuery or the default URL document.location without the parameters. Or will clean up the names, emails, etc. by detecting them. The trick is that our Google Analytics, our MetaPixel, and any base tracking tags will be fired using this custom page view thing to not get blocked from Meta by sending private sensitive information in the URL.
Client Side Tracking and Data Layer
Next, we will fire a custom data layer event not by the URL which is now being cleaned up, but instead by the custom JavaScript object cookie or local storage. And we will have a trigger for our Google Ads conversions, Meta Pixel conversions and other client-side Google Tag Manager conversion tags.
Tools for sending Facebook Meta Conversions API events
For Facebook / Meta Conversions API Typeform events, you can use a Cloudflare worker, you can vibe code your own server-side tracking system with Cursor or ChatGPT, or you can use a visual no-code tool as Zapier or n8n.com or make.com.
Here please note that N8N does not have a built-in MetaConversions API module, so that's tricky. You can use HTTP request system or I think purchase a third-party module.
Zapier MetaConversions API feels like a child's toy. It doesn't have all the field values. I think they just want to reduce their support load. So Zapier doesn't have the Meta or Facebook Click ID field. This is a disaster.
Make.com I think has a really solid Meta-conversions API module. It handles hashing the parameters easier than the others.
These events are fairly straightforward to implement. However, now we get to the trickier parts.
User identifiers and other parameters
Remember the event match quality score, how we want to send all the possible parameters? Like Click ID, user agent, IP address, Facebook click and Facebook Pixel cookies? The problem is that we do not have these within Typeform. We can get some results by the first-party user data like email, phone number, first name, last name, but it's not enough if we really care about our ad performance.
So we need to pass this information into Typeform.
Creating hidden fields allows us to have all the parameters available for Facebook Meta-Conversions API. Here is where it gets even more difficult. If you only have one landing page and the visitors cannot browse your website, you may be able to store the Facebook click ID, UTM parameters, etc because Typeform iframe inherits them from the parent page, the landing page URL. But most of the time your users may be able to browse your website and you will lose the original URL parameters. This means you will need to remember the URL parameters in your browser so you will use JavaScript to create a custom tracking app that stores the click IDs and others in the browser e.g. as JavaScript cookies.
You will then need to create a custom version of your Typeform embed that's generated by adding these parameters into the hidden fields of your Typeform.
If our object with hidden field attribution values is called iframePrepopulationParameters, we can create dynamic type form button using an example code like this.
// Your Typeform URL
var TYPEFORM_URL = 'https://form.typeform.com/to/YOUR_FORM_ID';
// Create popup with prefilled hidden fields
var popup = window.typeformEmbed.makePopup(TYPEFORM_URL, {
mode: 'popup',
autoClose: 0,
hideScrollbars: true,
opacity: 100,
hidden: iframePrepopulationParameters
});
// Button trigger
var btn = document.querySelector('.btn.btn--border.theme-btn--primary-inverse.sqs-button-element--primary');
if (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
popup.open();
}, { passive: false });
}
The end result we want is to have a reliable attribution of our Google and Facebook click identifiers in hidden fields in our Typeform results.
However, it gets even more difficult.
Deduplication, and event IDās
Remember when I mentioned type form thank you page URL does not have the submission ID. This means we don't have a reliable way to deduplicate Facebook Pixel and Facebook conversions API events.
So we need to add a few more hidden fields into our type form. We want a random ID, just 10 random digits, which will be available in a thank you page and via the API in a hidden field. Notice that this should not be stored in the browser like the others. This one would be unique to the submission, not the user and their device.
This way we can deduplicate Meta Pixel event from the Meta Conversions API server-side event.
Formatting the Facebook Click ID
Another pretty difficult part is formatting our Facebook Click ID. Whenever we use Google Ads conversion tracking like offline conversion tracking or conversion enhancement, we can just pass the Google Click ID. Done. Easy.
In meta or Facebook we need custom formatting for our click ID including a Unix timestamp. This is why we also wanted to pass that into our Typeform hidden fields. Please note that the timestamp should not be the timestamp when they submitted the type form, but the first visit time when we first saw them enter our website. This is how the cookie would work via Meta Pixel.
Facebook documentation of click ID formatting:
The formatted ClickID value must be of the form version.subdomainIndex.creationTime.<fbclid>, where:
version is always this prefix: fb
subdomainIndex is which domain the cookie is defined on ('com' = 0, 'example.com' = 1, 'www.example.com' = 2)
creationTime is the UNIX time since epoch in milliseconds when the
_fbcwas stored. If you don't save the_fbccookie, use the timestamp when you first observed or received thisfbclidvalue<fbclid>is the value for thefbclidquery parameter in the page URL.
Hereās an example of what the resulting fbc parameter value could look like (note that the <fbclid> portion is invalid):
fb.1.1554763741205.AbCdEfGhIjKlMnOpQrStUvWxYz1234567890Using a visual no-code tool like make.com, and Meta Conversions API module, the click ID formatting is possible, but annoyably complex. Below is an example formatting function, assuming we have the click ID and the first visit UNIX timestamp in our Typeform hidden field.
IP Address and User Agent
If we aim for perfection (and of course we aim for perfection, not having good tracking always equals leaving money on the table and having ads not perform as well), we also want to store the Facebook pixel cookie, the Facebook click cookie created by the Facebook pixel, user agent and IP address.
For this I have a pretty good solution but not a perfect solution yet. The problem with something like Facebook, PixelCookie or an IP address is that it takes a little bit of time to get these. In the browser we do not have the IP address of our visitor and Typeform does not store the visitor IP addresses (like JotForm does).
So the problem is that we would want to pre-populate information into Typeform hidden fields before we have that information. Now we could get the information, like IP addresses via a free API like this:
// Async function for IP lookup
async function getIP() {
try {
const response = await fetch('https://api.ipify.org?format=json'); //You can use it without limit (even if you're doing millions of requests per minute.
const { ip } = await response.json();
return ip;
} catch (error) {
console.log("Typeform iFrame PREPOPULATOR: ā ļø Failed to fetch IP:", error);
return 'unknown';
}
}⦠But we don't really want to delay loading of our Typeform and we probably don't want to do too much of a custom Typeform in bed just to keep things reliable.
At the moment my best solution will pass these fields into the typeform if they exist but I do not take any risks in dynamically generating a custom typeform. Basically this means that if the visitor visits more than one page we get all the information. If they convert directly on the landing page without visiting any other pages we will have very good tracking but not the absolute peak or perfection with IP address.
Another quirk in Facebook and Meta-conversions API is that it doesn't take user agent or IP address separately. It will only take both or neither one. This is pretty annoying because many times we would have the user agent but maybe not IP address if you use landing pages that do not have navigation menus.
If you use a custom-coded app or Cloudflare worker, you can easily Vibecode a solution. If you use a visual no-code tool like make.com, we will need to get a little bit creative. Hereās an example that will only send IP address if we also have user agent, and will only send user Agent if we also have IP address.
Remember, weāre aiming for perfection. Our ads are gonna crush. Competitors are gonna be left waiting with their negative ROAS.
One more parameterā¦
Facebook conversions API also wants to know the URL where the conversion happens. This is super easy or basically automatic with the meta or Facebook pixel, but on the server side we don't necessarily know the URL, especially if our type form exists on multiple pages. Also I recommend sending the conversion page into a hidden field as well.
Facebook Convesion Event Names?
One last thing to take into account is choosing the event. Usually Typeform is used for lead generation, so event like lead or submit application work really well. I recommend standard events (like Lead) because custom events (like Typeform_Contact_form_submit) need to be manually turned into custom conversions, and activated by running a campaign with this custom goal to start seeing the results. So it's a lot of hassle and standard events are easier.
However, if your niche is more on the healthcare side where the data is more sensitive, you will need to use custom event names. For those cases, I would also skip the hidden field prepopulation and use settings to forget the sent data in a tool like make.com, just so you won't be breaking anyone's privacy. But this would be a topic for another blog post because it varies so much by country and
Improving things further?
This is already pretty enterprise-grade conversion tracking setup. That's better than 99.99% of Typeform conversion tracking setups. With something like this, you will have very good time watching the money come in through your Facebook ads with near-perfect tracking and targeting.
For a future version of an even better setup, I'm considering skipping the Typeform hidden field prepopulation and going through a webhook instead, combining all the attribution information we store in the browser. Then sending the info from Typeform iframe embeds JavaScript postmessage, sending the Typeform form ID and Typeform unique submission ID to a server where we will use typeform API to fetch the information of this specific form submission and then put all these pieces together. This has the benefit of always having IP address and any Meta Pixel generated cookies that again on the first load would be slower than the typeform embed so they may not be available. It would also solve problems for potential messy Google Analytics URLs and the redirect thank you annoyances.
For a few more tiny boosts in performance, we may consider a solution that makes our cookies last longer if Apple Intelligent Tracking Prevention tries to remove them. This could be used with a lazier local storage cookie keeper or a fancier server-side fingerprinting option. However, tracking like this tutorial shows will get you insane results and the rest is probably micro-optimization that may not have a noticeable impact in your tracking accuracy or ad performance.
Want a pro to handle Meta tracking for you?
Typeform <> Facebook/ Meta Conversion API conversion tracking is pretty complicated.
If you would like a pro to set it up for you, I'm now building an affordable monthly subscription app that handles all of this for you. Zero effort required. It's just gonna work and make your ads perform like they've never performed before. If that sounds interesting, get in touch.