Categories
Web Development

How to Use Multiple Facebook Pixels on One Site

Want to Support Me?
Get two free stocks valued up to $1,850 when you open a new Webull investment account through my referral link and fund the account with at least $100!

Can you use multiple Facebook pixels on one site? The short answer is: yes!

This was a frustrating problem! Before late 2017, Facebook didn’t provide any real documentation about using more than one pixel on a single site. This is hard to believe, as I imagine many businesses work with multiple digital marketing vendors who provide Facebook ad services. From the looks of the many unanswered questions in Facebook’s help forum about this, it’s obvious many were frustrated along with me.

When I originally worked on this problem in late summer 2017, doing a Google search for “facebook multiple pixels on one site” found results from Facebook’s help forums and StackOverflow, but none had a full discussion of the possible options and what works or doesn’t.

I later discovered a blog post by Facebook called “Accurate Event Tracking with Multiple Pixels.” It goes into detail on how to track pixel events to specific pixel IDs using the fbq actions trackSingle and trackSingleCustom. While those functions do track pixel events to only a single pixel ID, rather than all that have been inited on the site, the problem still remains that any other developer who doesn’t use those functions will track all their pixel events to your pixel, whether you want them or not.

Let’s get into this issue with a deeper discussion…

Discussion

I highly recommend you first install the Facebook Pixel Helper. It helps diagnose problems and allows you to see what’s going on under the hood.

I had two important objectives for this project:

  1. Include multiple Facebook pixels on one website and be able to track activity, engagement, and conversions on that site to them.
  2. Ability to fire my own events to my pixel ID without anyone else being able to track data to my pixel ID.

The first thing you might try is simply adding the two full Facebook Pixel code snippets to the site. Don’t do that! Only include one full snippet, then add additional fbq('init', '[your-pixel-id]') calls for each additional pixel ID you want to install. It would look something like this:

<script>
    !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','//connect.facebook.net/en_US/fbevents.js');
    fbq('init', '[first-pixel-id]');
    fbq('init', '[second-pixel-id]');
    fbq('track', 'PageView'); //tracks a PageView to both initialized pixels
</script>
<noscript>
    <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=[first-pixel-id]&ev=PageView&noscript=1">
    <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=[second-pixel-id]&ev=PageView&noscript=1">
</noscript>

If you init your pixel IDs like this, any future calls using the basic track action, like fbq('track', 'PageView'), will send an event for each initialized pixel ID (see screenshot below).

A view of the Facebook Pixel Helper on a site with multiple pixels

But what we really want to do is only track our pixel events to our own pixel, not everyone else’s too. That’s where Facebook’s new trackSingle and trackSingleCustom actions come in. Instead of calling fbq('track', 'PageView') , simply call fbq('trackSingle', '[your-pixel-id]', 'PageView') instead.

If #1 is your only objective, you could stop here. You’ve found your solution!

But for me, the second objective was not met to my satisfaction. From what I’ve seen, most developers don’t use the trackSingle or trackSingleCustom actions yet, which means every event they track will also flow into my pixel. This isn’t the end of the world, but that data isn’t mine, and I don’t want it getting into my pixel. So if I init my pixel ID on the site, it will receive any events they fire that use the basic track action. In other words, with these new single track actions, I can ensure my pixel events only track to my pixel ID, but I can’t do anything about other developers who track to all initialized pixel IDs on the site. Argh!

Let’s discuss a specific scenario.

The company I work for provides a service for Facebook Dynamic Ads, so we have our own Product IDs in our Product Catalog that need to match the Product IDs on our clients’ websites. But our Product ID may not be the same as other companies’, so we need to be able to specify our own and send those along with only our pixel events.

For instance, let’s say you land on a client’s product page for a t-shirt, and that client works with two different digital marketing companies for Facebook Dynamic Ads. The first company identifies products by their own unique product ID (e.g. “tshirt_1022”). The second company identifies products by the UPC (e.g. “892185001003”). If both their pixels are initiated, the first company may include a call like this:

fbq('track', 'ViewContent', {
    content_type: 'product',
    content_ids: ['tshirt_1022']
});

The obvious problem is that the Facebook pixel will send the track event to both Pixel IDs, but only the first will match a product ID, because my company’s product ID is “892185001003”, not “tshirt_1022”. You will see something like this in the Pixel Helper:

The Facebook Pixel Helper can help you find mismatched product IDs and other similar problems.

If both developers use the trackSingle action, then we’re golden. Objective #2 is met! But you have to trust that all other developers will use the new trackSingle action and not the track action.

Progress

All right, we’ve now identified the problem, so how do we go about solving it?

I originally spent hours searching and testing on the Facebook pixel’s fbq object, but I couldn’t find any way to send a pixel event to only one pixel. Thankfully, since then, they have introduced the new track actions, which work perfectly, if every developer on the site uses them. But since I want to track only my events to my pixel ID and not have anyone else’s events getting into my pixel data, I had to continue working.

The first part of my eventual solution came from the recognition that Facebook’s pixel code includes two methods to initiate your pixel and track events on a site: JavaScript and non-JavaScript. Facebook’s example pixel code looks like this:

<script>
    !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','//connect.facebook.net/en_US/fbevents.js');
    fbq('init', 'FB_PIXEL_ID');
    fbq('track', 'PageView');
</script>
<noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=FB_PIXEL_ID&ev=PageView&noscript=1"></noscript>

Note the <img> tag in the <noscript> section. If the user doesn’t have JavaScript enabled, the browser will send the event data via an image request to Facebook. Using this method, you can pass a single pixel ID, and the event is tracked exclusively to my pixel. Hazah! So what we need to do is dynamically create an <img> tag like we see in that example code.

Solution

In my Googling, I came across an answer on StackOverflow that employed this idea of dynamically creating an <img> tag to send the event data, so I used it as a foundation. Credit goes to the user Flavio Wuensche for the basis of my function below!

I’ll include my code, then we can discuss what it does:

(function (window, document) {
    if (window.myfbq) return;
    window.myfbq = (function () {
        if (arguments.length > 0) {
            var pixelId, trackType, contentObj;

            if (typeof arguments[0] == 'string') pixelId = arguments[0];
            if (typeof arguments[1] == 'string') trackType = arguments[1];
            if (typeof arguments[2] == 'object') contentObj = arguments[2];

            var params = [];
            if (typeof pixelId === 'string' && pixelId.replace(/\s+/gi, '') != '' &&
            typeof trackType === 'string' && trackType.replace(/\s+/gi, '')) {
                params.push('id=' + encodeURIComponent(pixelId));
                switch (trackType) {
                    case 'PageView':
                    case 'ViewContent':
                    case 'Search':
                    case 'AddToCart':
                    case 'InitiateCheckout':
                    case 'AddPaymentInfo':
                    case 'Lead':
                    case 'CompleteRegistration':
                    case 'Purchase':
                    case 'AddToWishlist':
                        params.push('ev=' + encodeURIComponent(trackType));
                        break;
                    default:
                        return;
                }

                params.push('dl=' + encodeURIComponent(document.location.href));
                if (document.referrer) params.push('rl=' + encodeURIComponent(document.referrer));
                params.push('if=false');
                params.push('ts=' + new Date().getTime());

                if (typeof contentObj == 'object') {
                    for (var u in contentObj) {
                        if (typeof contentObj[u] == 'object' && contentObj[u] instanceof Array) {
                            if (contentObj[u].length > 0) {
                                for (var y = 0; y < contentObj[u].length; y++) { contentObj[u][y] = (contentObj[u][y] + '').replace(/^\s+|\s+$/gi, '').replace(/\s+/gi, ' ').replace(/,/gi, '§'); }
                                params.push('cd[' + u + ']=' + encodeURIComponent(contentObj[u].join(',').replace(/^/gi, '[\'').replace(/$/gi, '\']').replace(/,/gi, '\',\'').replace(/§/gi, '\,')));
                            }
                        }
                        else if (typeof contentObj[u] === 'string' || typeof contentObj[u] === 'number') {
                            params.push('cd[' + u + ']=' + encodeURIComponent(contentObj[u]));
                        }
                    }
                }

                params.push('v=' + encodeURIComponent('2.9.33'));

                var imgId = new Date().getTime();
                var img = document.createElement('img');
                img.id = 'fb_' + imgId, img.src = 'https://www.facebook.com/tr/?' + params.join('&'), img.width = 1, img.height = 1, img.style = 'display:none;';
                document.body.appendChild(img);
                window.setTimeout(function () { var t = document.getElementById('fb_' + imgId); t.parentElement.removeChild(t); }, 1000);
            }
        }
    });
})(window, document);

This attaches a new function called myfbq to the global window object, so you can use it from anywhere in your JavaScript code. Note that this function has no association with and makes no calls to Facebook’s fbq object.

Near the top of the function, there are some checks to make sure there is a pixel ID and event type specified. If those exist, and the event type is valid, it begins building an array of parameters that need to be appended to the image request URL. Flavio included code to build the custom parameters, if you need to define those.

Lastly, the function creates an image element, defines its attributes, then appends it to the body. After one second, the image element will be removed from the page (that’s not necessary, but just a matter of housekeeping).

Usage

The function accepts 2-3 arguments, the first of which is your pixel ID:

myfbq("[your-pixel-id]", "PageView");
myfbq("[your-pixel-id]", "ViewContent");
myfbq("[your-pixel-id]", "ViewContent", { content_type: "product", content_ids: ['892185001003'] });

Wrapping It Up

That’s it! When you browse to your site, open up the Facebook Pixel Helper and confirm that the pixel event is being sent to Facebook with the correct data.

One final note is that if you use this function, you won’t need to include your Facebook’s pixel code snippet on your site. If you call fbq('init', '[your-pixel-id]') on the site, remember that all events tracked with the basic track action will also be sent for your pixel ID, and that’s what we want to avoid. Always use trackSingle or trackSingleCustom for your own events using the fbq function. And if you want your events to only track to your pixel ID, just include the myfbq function and make calls to it with your pixel ID.

Your Turn!

Have you run into this problem before? What solution did you come to? I’d love to hear your questions or experiences, so let’s discuss in the comments below!

Want to Support Me?
Get two free stocks valued up to $1,850 when you open a new Webull investment account through my referral link and fund the account with at least $100!