In today’s web development landscape, there’s an ever-growing reliance on libraries and package managers, often bundled with many dependencies. While these tools speed up development and help solve complex problems, there’s a number of downsides.
When integrating the Facebook Conversion API, you might consider using a prebuilt library like the facebook-php-business-sdk, which requires Composer. Not only does this add extra setup steps, but an SDK like this is packed with features – most of which are unnecessary for the task at hand – unless that task is to build a user-friendly interface for the entire Facebook ecosystem.
In our case, we often want to leverage the Facebook Conversions API to send events server-side, rather than relying on the Facebook Pixel, which has become less dependable in the age of cookie banners and privacy regulations.
By building a solution from scratch, we ensured it was lightweight, easily transferrable to other projects, and adaptable for our future needs.
We start by defining a simple PHP class FacebookConversionsAPI to handle our API integration. This class will include methods for sending event data, making HTTP requests, and hashing user data.
In a larger applications, you might wish to split off your cURL code into other classes to avoid repetition.
<?php
class FacebookConversionsAPI {
private $pixel_id;
private $access_token;
private $api_url;
// constructor to set up initial values
public function __construct(){
$this->pixel_id = 'YOUR_PIXEL_ID_HERE'; // replace with your actual Pixel ID
$this->access_token = 'YOUR_ACCESS_TOKEN_HERE'; // replace with your actual access token
$this->api_url = "https://graph.facebook.com/v18.0/{$this->pixel_id}/events";
}
// method to send conversion data to Facebook
public function sendEvent($event_name, $user_data = [], $custom_data = []) {
// determine if the request is over HTTPS or HTTP
$protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
// build the full URL of the current page
$event_source_url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// collect Facebook cookies that are important for tracking
$user_data['fbc'] = $_COOKIE["_fbc"];
$user_data['fbp'] = $_COOKIE["_fbp"];
// collect the client's IP address and user agent
$user_data['client_ip_address'] = $_SERVER['REMOTE_ADDR'];
$user_data['client_user_agent'] = $_SERVER['HTTP_USER_AGENT'];
// prepare event data to be sent to Facebook
$event_data = [
'event_name' => $event_name, // name of the event (e.g., 'Lead')
'event_time' => time(), // current timestamp
'event_source_url' => $event_source_url, // URL where the event occurred
'user_data' => $user_data, // hashed and non-hashed user data
'custom_data' => $custom_data, // any additional custom data for the event
'action_source' => 'website' // specifies that the event came from the website
];
// prepare the full payload including the access token
$payload = [
'data' => [$event_data],
'access_token' => $this->access_token
];
// send the payload to Facebook using the makeRequest method
return $this->makeRequest($payload);
}
// private method to handle the CURL request to Facebook
private function makeRequest($payload) {
$payload_encode = json_encode($payload); // encode the payload as JSON
// initialize CURL with the API URL
$ch = curl_init($this->api_url);
curl_setopt($ch, CURLOPT_POST, true); // set CURL to use POST method
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload_encode); // attach the payload
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return the response as a string
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); // set the content type to JSON
// execute the CURL request and get the response
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); // get the HTTP status code
curl_close($ch); // close the CURL session
// check if the response was successful
if ($http_code !== 200 || json_decode($response)->error) {
return 'Error : ' . json_encode($response); // return error if the response is not OK
} else {
return true; // return true if the event was tracked successfully
}
}
// method to hash user data as per Facebook's requirements
public function hashUserData($data) {
$hashed_data = [];
// hash each piece of user data using SHA-256
foreach ($data as $key => $value) {
$hashed_data[$key] = hash('sha256', strtolower(trim($value))); // trim, lowercase, and hash the data
}
return $hashed_data; // return the hashed data
}
}
?>
With our class set up, we can now track conversions by invoking the sendEvent method. Here’s how you can use it in your project.
<?php
// include the Facebook Conversions API class
require realpath($_SERVER['DOCUMENT_ROOT']) . '/FOLDER_LOCATION/fb_conversions_api.php';
// create an instance of the FacebookConversionsAPI class
$fb_api = new FacebookConversionsAPI();
// prepare the data to be hashed, obtain data from form data as required
$data_to_hash = [
'em' => $email, // user's email
'fn' => $first_name, // first name
'ln' => $last_name, // last name
'pn' => $telephone, // telephone number
'zp' => $postcide // post/zip code
];
// any custom data you want to send with the event
$custom_data = [
'lead_event_source' => $form_name // example custom data
];
// hash the user data using the hashUserData method
$user_data = $fb_api->hashUserData($data_to_hash);
// send the 'Lead' event to Facebook with the hashed user data and custom data
$fb_api->sendEvent('Lead', $user_data, $custom_data);
?>
Like what you’ve read, then why not tell others about it... they might enjoy it too
If you think Bronco has the skills to take your business forward then what are you waiting for?
Get in Touch Today!
Thanks for writing this super clean implementation! Really appreciate how you’ve stripped it down to the essentials without the bloat of the official SDK.
Quick heads up though – if anyone’s using this alongside the Meta Pixel (which is pretty common), you’ll probably want to add event deduplication by sending event_id.
public function sendEvent($event_name, $user_data = [], $custom_data = [], $event_id = null) {
// … existing code …
// Generate event_id if not provided (you might want to use your own ID generation)
if (!$event_id) {
$event_id = uniqid(‘ev_’, true);
}
// prepare event data to be sent to Facebook
$event_data = [
‘event_name’ => $event_name,
‘event_id’ => $event_id, // Add the event_id here
‘event_time’ => time(),
‘event_source_url’ => $event_source_url,
‘user_data’ => $user_data,
‘custom_data’ => $custom_data,
‘action_source’ => ‘website’
];
// … rest of the existing code …
}
This way, if you’re firing the same event from both the Pixel and CAPI, Meta will know they’re the same conversion and won’t double-count them in your reports. Just make sure to use the same event_id when firing the event from your Meta Pixel!
Good spot. Though the code above isn’t specific on generating an event ID that can also be sent to the Meta Pixel event code. I believe both would need to include the ID to ensure that the event is deduplicated (more than what Facebook already attempts through data matching). https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events/
So the code may look something like this, with the event id created and passed to both the CAPI and Meta Pixel functions…
require realpath($_SERVER[‘DOCUMENT_ROOT’]) . ‘/path_to_class/fb_conversions_api.php’;
$fb_api = new FacebookConversionsAPI();
// Generate a unique event ID
$event_id = uniqid(‘ev_’, true);
// Hash user data
$data_to_hash = [
’em’ => $email,
‘fn’ => $first_name,
‘ln’ => $last_name,
‘pn’ => $telephone,
‘zp’ => $postcode
];
$user_data = $fb_api->hashUserData($data_to_hash);
// Define custom data
$custom_data = [
‘lead_event_source’ => $form_name
];
// Send event via CAPI
$fb_api->sendEvent(‘Lead’, $user_data, $custom_data, $event_id);
// Send event via Meta Pixel (front-end JS)
echo ““;
?>