a.k.a. fbq-spy or Facebook Queue Spy
This lightweight library allows listening, modifying, and blocking data tracked through Facebook's pixels.
This works with implementations that follow Facebook's reccomendations, using the fbevents.js library and fbq
command queue API. (While not officially supported by Facebook, this library was deemed a viable solution by Facebook engineers as of 2021-9-16 โ specifically to prepare web pixels for CAPI support).
The methodology is little more than a proxy pattern. For a detailed walkthrough, see this walkthrough of the highly similar logic of GA Spy.
The listener is run by calling fbqSpy
, which takes a configuration object as the sole argument.
fbqSpy( {
// Function fired whenever `fbq()` is called.
'callback' : function( data ){},
// The name of the global `fbq` object. Default: "fbq".
'fbqObjName' : 'fbq',
// Set true to activate logging and avoid try/catch protection. Default: false.
'debug' : false,
// String with which to prefix log messages. Default: "fbqSpy".
'debugLogPrefix' : 'fbqSpy',
} );
The callback function gets called whenever fbq
is called.
The callback can be passed in the callback
field of the configuration object,
or in place of the configuration object.
fbqSpy( function( data ){
/**
* @var {Object} data - Provides original and parsed command arguments.
* @var {Array} data.args - Arguments passed to `fbq()`.
* @var {Object} data.the - Object listing all passed fields by name.
* @var {string} data.the.command - the first argument (init, track, etc)
* @var {string|undefined} data.the.eventName - only for track and trackCustom commands
* @var {Object} data.the.parameters - merge of all passed objects
* @var {string|undefined} data.the.parameters.*
*/
} );
Any changes made in data.args
will be passed through to fbq
.
Return false to abort the call and prevent the command from firing.
Uncaught callback errors will be suppressed (unless config.debug
is active).
So if your integration breaks, default FB pixel behavior will be unaffected.
Warning: Calling fbq
in the callback will cause an infinite feedback loop.
fbqSpy( function(data){
console.debug.apply( console, ['fbq():'].concat(data.args) );
})
fbqSpy( function(data){
return ( data.the.parameters.dataSource !== '{{Container ID}}' );
})
fbqSpy( function(data){
var namespace = 'fbq.' + data.the.command,
frame = {event:namespace+(data.the.eventName?':'+data.the.eventName:'')};
frame[namespace] = data.the;
dataLayer.push(frame);
})
This adds event_id
to FB pixels for redundant deduplicated CAPI tracking.
This code is for Google Tag Manager, but would be trivial to modify code for use anywhere.
In GTM web container ...
- Add 'Facebook Pixel CAPI Helper' as a tag; fire on All Pages.
- Add 'Facebook Event ID' as a variable.
- In GA4 config tag, set
event_id
={{Facebook Event ID}}
. - Validate using FB Events Manager. If you see 'Deduplicated' next to the event name, it's working!
GTM Tag: 'Facebook Pixel CAPI Helper' (fire on All Pages)
(function(){
var dL = google_tag_manager[{{Container ID}}].dataLayer,
dLKey_counter = 'fw.fb.counter',
eventId = {{Facebook Event ID}};
fbqSpy( function(data){
if( 'track' !== data.the.command && 'trackCustom' !== data.the.command ) return;
data.args.push({eventID:eventId});
dL.set( dLKey_counter, (dL.get(dLKey_counter)||0)+1 );
});
})()
GTM Variable: 'Facebook Event ID'
function(){
try{
var dL = google_tag_manager[{{Container ID}}].dataLayer,
dLKey_counter = 'fw.fb.counter',
dLKey_pageloadId = 'fw.core.pageload_id',
pageloadId = {{Pageload ID}} || dL.get(dLKey_pageloadId),
counter;
if( ! pageloadId ) return "";
( counter = dL.get(dLKey_counter) ) || dL.set( dLKey_counter, counter=1 );
return pageloadId+'.'+counter;
}catch(ex){ {{Debug Mode}} && console.error("[GTM Variable: Facebook Event ID]",ex);}
}
Open-source under the MIT license.