Call or WhatsApp: +92 321 209 8738 WhatsApp: +971569803329 info@pixelsproagency.com

Ads Tracking

Meta Pixel and Conversions API for Lead Forms: Setup Guide for Pakistan and UAE Businesses

If you run Facebook or Instagram ads to a lead form, the number of leads in Ads Manager rarely matches the enquiries in your inbox. Part of the gap is the browser: ad blockers, privacy settings and slow connections stop the Meta Pixel from reporting some submissions. The Conversions API fixes much of that by sending the same lead from your server. This guide shows how to set up both for a lead form on a static or PHP website, including how deduplication works, how to hash Pakistani and UAE phone numbers so Meta can match them, how to respect consent, and how to test before trusting the numbers. The code was tested before publishing.

Why the Pixel Alone Misses Leads

The Meta Pixel is a piece of JavaScript that runs in your visitor's browser and tells Meta when something happens, such as a page view or a form submission. Meta uses those events to report results in Ads Manager and to find more people likely to do the same thing. When the Pixel misses events, your cost per lead looks higher than it really is and the ad algorithm learns from incomplete data.

Browser-only tracking misses events for ordinary reasons:

  • Ad blockers and privacy browsers block requests to Meta's domains.
  • Browser privacy features, especially on iPhones, limit cookies and tracking.
  • The page changes before the Pixel finishes. When a form submits and the browser moves to a thank-you page, the event request can be cut off, particularly on a slow mobile connection.
  • Consent. Visitors who decline tracking shouldn't be tracked at all, and that's correct, but it does reduce the numbers.

The Conversions API (CAPI) is Meta's answer. Your server sends the same event directly to Meta, so it doesn't depend on the browser. Meta recommends running both: the Pixel in the browser and CAPI from the server, sending the same event with the same ID so Meta counts it once.

This guide is written for lead-generation sites: clinics, real estate agents, schools, consultants, home services. You can set it up on a static site or a custom PHP form. If you use Shopify or WordPress with WooCommerce, check for Meta's official integration first, since it handles most of this for you.

What You Need Before You Start

Setup requirements
ItemWhere to get it
Meta Business portfolio and ad accountMeta Business Suite
A dataset (Pixel) IDEvents Manager > Data sources. Older accounts call it a Pixel; newer screens call it a dataset. It's the same ID.
A Conversions API access tokenEvents Manager > your dataset > Settings > Conversions API > Generate access token
A verified domainBusiness settings > Brand safety > Domains
A server that can run PHP with cURLAlmost every cPanel host, including GoDaddy, Hostinger and Namecheap shared hosting
A consent mechanismYour cookie banner; see our Consent Mode v2 guide

Treat the access token like a password. It must never appear in your HTML, JavaScript or a public code repository. Store it as an environment variable or in a config file outside the public web folder.

Check Whether Your Industry Is Restricted

Do this first, because it can change your whole plan. Since early 2025 Meta has been applying stricter data rules to businesses it classifies as health and wellness, and some other sensitive categories. For datasets Meta flags this way, lower-funnel events like Lead and Purchase can be restricted, and Meta can limit what you're allowed to send and use for optimisation. Industry reports indicate that sending the same events through the Conversions API doesn't get around these restrictions, since they apply to the data, not the delivery method.

So if you run a clinic, pharmacy, dental practice, fitness business with medical claims or similar, check Events Manager for warnings on your dataset before investing time in setup. You may need to optimise for upper-funnel events, and you should never send health details, such as which treatment someone asked about, to any ad platform. Our guide to clinic website design covers this from a patient-privacy angle.

How Deduplication Works

If the Pixel and your server both report the same lead, Meta needs to know it's one lead, not two. According to Meta's documentation, it matches events using event_name together with an event ID: eventID on the Pixel and event_id in the Conversions API. When both arrive within 48 hours with the same name and ID, Meta keeps one. If they arrive within about five minutes of each other, Meta prefers the browser event.

That means the same ID has to be created once and used in both places. The simplest reliable approach for a form:

  1. When the visitor submits, JavaScript generates a unique ID.
  2. It puts the ID in a hidden form field and fires the Pixel Lead event with that ID.
  3. The form submits normally to your PHP script, which saves the lead first and then sends a Lead event to the Conversions API with the same ID.

Saving the lead first matters. The lead is the business; the tracking is secondary. If Meta's API is slow or down, the visitor's enquiry still has to reach you.

Step 1: The Form and the Pixel Event

This assumes the standard Meta Pixel base code is already in your page's head. Add a hidden event_id field to your form and the short script below it.

<form id="quote-form" action="/contact-submit.php" method="post">
  <input name="name" autocomplete="name" required>
  <input name="phone" type="tel" autocomplete="tel" required>
  <input name="email" type="email" autocomplete="email">
  <input type="hidden" name="event_id" value="">
  <button type="submit">Request a quote</button>
</form>
<script>
document.getElementById('quote-form').addEventListener('submit', function (e) {
  var form = e.target;
  // One ID for this lead, shared by the browser event and the server event.
  var eventId = 'lead-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10);
  form.elements.event_id.value = eventId;

  if (typeof window.fbq === 'function') {
    fbq('track', 'Lead', {}, { eventID: eventId });
  }
  // No preventDefault: the form submits normally, so the lead reaches you
  // even if the Pixel is blocked or the visitor declined cookies.
});
</script>

We tested this in jsdom with a stubbed fbq function. On submit, the hidden field gets an ID beginning with lead-, the Pixel receives a Lead event with exactly that ID as its eventID, and the form still submits normally because the script doesn't cancel it. If the Pixel is blocked, the typeof fbq check means the form still works.

We deliberately don't fire the Lead event on the thank-you page instead. Thank-you pages get refreshed, bookmarked and visited directly, which creates fake leads in your reports.

Step 2: Hashing Customer Details Correctly

To match a server event to a Facebook or Instagram account, Meta uses customer information such as email and phone. These must be hashed with SHA-256 before sending, and they must be normalised first, otherwise the hash won't match and the data is useless. Meta's rules for the two most common fields:

  • Email: remove spaces at the start and end and convert to lowercase, then hash.
  • Phone: keep digits only, include the country code, and remove leading zeros, then hash.

Phone numbers are where Pakistani and UAE sites usually go wrong, because people type them in many formats:

Phone formats and the normalised value Meta expects
What the visitor typedNormalised before hashing
0321 1234567 (Pakistan mobile, local format)923211234567
+92 321 1234567923211234567
0092-321-1234567923211234567
050 123 4567 (UAE mobile, local format)971501234567
+971 50 123 4567971501234567

A local number starting with 0 needs the country code added, and the code has to know which country to assume. The PHP below takes a default country code so a UAE site can pass '971'. Don't hash the browser cookies _fbp and _fbc, the visitor's IP address or the user agent; Meta expects those unhashed.

Step 3: Sending the Event from PHP

Save this as a separate file, for example meta-capi.php, outside your public folder if your host allows it, and include it from your form handler. It reads your dataset ID and token from environment variables. On cPanel you can set those through your host's environment settings, or load them from a config file outside public_html.

<?php
// Add to your form handler (e.g. contact-submit.php) and call
// send_meta_lead($_POST) after the lead email has been sent.
// Use send_meta_lead($_POST, '971') on a UAE site.

const META_API_VERSION = 'v26.0'; // latest when written (July 2026); check Meta's changelog

function meta_phone(string $raw, string $defaultCountry = '92'): string {
    $digits = preg_replace('/\D+/', '', $raw);
    if (strpos($digits, '00') === 0) {
        return substr($digits, 2);                       // 0092..., 00971...
    }
    if (strpos($digits, '0') === 0) {
        return $defaultCountry . ltrim($digits, '0');    // 0321... -> 92321...
    }
    return $digits;                                      // already +92 / +971
}

function build_meta_lead(array $post, array $server, array $cookies, string $country = '92'): ?array {
    $eventId = substr(preg_replace('/[^A-Za-z0-9-]/', '', $post['event_id'] ?? ''), 0, 64);
    if ($eventId === '') {
        return null;
    }
    $user = [
        'client_ip_address' => $server['REMOTE_ADDR'] ?? '',
        'client_user_agent' => $server['HTTP_USER_AGENT'] ?? '',
    ];
    if (!empty($post['email'])) {
        $user['em'] = [hash('sha256', strtolower(trim($post['email'])))];
    }
    if (!empty($post['phone'])) {
        $user['ph'] = [hash('sha256', meta_phone($post['phone'], $country))];
    }
    foreach (['_fbp' => 'fbp', '_fbc' => 'fbc'] as $cookie => $key) {
        if (!empty($cookies[$cookie])) {
            $user[$key] = $cookies[$cookie];             // sent unhashed
        }
    }
    return ['data' => [[
        'event_name'       => 'Lead',
        'event_time'       => time(),
        'event_id'         => $eventId,
        'action_source'    => 'website',
        'event_source_url' => $server['HTTP_REFERER'] ?? 'https://www.example.com/contact.html',
        'user_data'        => $user,
    ]]];
}

function send_meta_lead(array $post, string $country = '92'): void {
    $pixelId = getenv('META_PIXEL_ID');
    $token   = getenv('META_CAPI_TOKEN');
    $payload = build_meta_lead($post, $_SERVER, $_COOKIE, $country);
    if (!$pixelId || !$token || $payload === null) {
        return;
    }
    $url = 'https://graph.facebook.com/' . META_API_VERSION . '/'
         . rawurlencode($pixelId) . '/events?access_token=' . rawurlencode($token);
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 5,                     // never hold up the visitor
    ]);
    $response = curl_exec($ch);
    if ($response === false || curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
        error_log('Meta CAPI error: ' . ($response ?: curl_error($ch)));
    }
    curl_close($ch);
}

Then, in your form handler, after the lead has been saved or emailed:

require __DIR__ . '/../private/meta-capi.php';

// ...after the lead has been saved or emailed:
$consent = ($_COOKIE['site_marketing_consent'] ?? '') === 'granted';
if ($consent) {
    send_meta_lead($_POST, '92'); // use '971' for a UAE site
}

Adjust the consent check to however your banner stores the visitor's choice. The consent example in our Consent Mode guide saves it in localStorage, which your server can't read, so also set a cookie when the visitor accepts, or copy the choice into a hidden form field. The point is that the server should respect the same choice as the browser. If someone declined marketing cookies, don't send their details to Meta from the server either.

We tested the event-building and hashing functions with PHP 8.3, including against the example hashes in Meta's own documentation for email and phone, and checked the Pakistani and UAE formats in the table above. The live call to Meta's API wasn't part of our test, so use Test Events (below) on your own account before relying on it. The API version is set to v26.0, the latest when we wrote this in 2026; Meta releases new versions regularly and retires old ones, so check the Graph API changelog once or twice a year.

The five-second timeout is intentional. If Meta's servers are slow, your visitor shouldn't stare at a loading thank-you page. Failures are written to your PHP error log so you can see them.

Step 4: Test Before You Trust It

  1. In Events Manager, open your dataset and go to Test events. Copy the test event code it shows.
  2. Temporarily add 'test_event_code' => 'TEST12345' (your code) to the payload in send_meta_lead(), next to data.
  3. Submit your form with a real email and phone. Within a minute you should see two Lead events, one from the browser and one from the server, marked as deduplicated.
  4. Remove the test code. Leaving it in means your real events go only to the test view.
  5. After a few days, check Event match quality for the Lead event in Events Manager. Higher scores mean Meta can match more events to accounts. Sending email, phone, fbp and fbc together usually gives the best result.

Keep in mind that your server event needs the event_source_url, which the code takes from the browser's referring page (the form page), with a fallback you should change to your own contact page URL, and that Meta rejects website events with an event_time more than seven days old. Neither will be an issue when you send at submission time.

Common Problems and Fixes

Troubleshooting the Pixel and Conversions API
SymptomLikely causeFix
Leads counted twice in Ads ManagerThe event IDs don't match, or the event name differs (for example "Lead" and "lead")Use the same ID from the hidden field and the exact same event name in both
Server events don't appearWrong token, wrong dataset ID or cURL blocked by the hostCheck the PHP error log; ask your host whether outbound HTTPS is allowed
Low event match qualityOnly email sent, or phone without country codeSend phone in international format, plus fbp and fbc
Lead event restricted or greyed outDataset classified under a sensitive category such as healthReview Meta's notice in Events Manager; optimise for permitted events
Events in Test Events onlyThe test event code was left inRemove test_event_code

What Not to Send

Only send what you need for matching and measurement. Never send the visitor's message text, health conditions, financial details, ID card or Emirates ID numbers, or anything else sensitive. Don't put personal details in URLs, because URLs end up in analytics reports and server logs. Mention Meta in your privacy policy, and make sure your cookie banner text tells visitors that marketing tools are used.

If you also measure leads in GA4, you can use the same submit event. Our guide to tracking WhatsApp and call clicks in GA4 covers the Google side, which matters because many visitors in Pakistan and the UAE prefer to tap WhatsApp instead of filling in a form.

Setup Checklist

  1. Check Events Manager for any industry restrictions on your dataset.
  2. Verify your domain in Business settings.
  3. Generate a Conversions API token and store it outside the public folder.
  4. Add the hidden event_id field and the Pixel Lead script to every lead form.
  5. Save the lead first, then call the Conversions API from the form handler.
  6. Normalise phones with the right default country code before hashing.
  7. Respect the visitor's consent choice on both browser and server.
  8. Confirm deduplication in Test Events, then remove the test code.
  9. Review event match quality after a week.
  10. Check the Graph API version every six months.

Frequently Asked Questions

Do I need the Conversions API if I already have the Meta Pixel?

It isn't mandatory, but Meta recommends using both. The Pixel misses events when browsers or ad blockers block it, or when a page changes before the request finishes. The Conversions API sends the event from your server, and deduplication stops it being counted twice.

How does Meta stop the same lead being counted twice?

Meta matches the event name and event ID from the Pixel and the Conversions API. If both arrive within 48 hours with the same values, it keeps one. Generate the ID once in the browser and pass it to your server through a hidden form field.

How should I format Pakistani and UAE phone numbers for Meta?

Use digits only, with the country code and without leading zeros or a plus sign, before hashing. 0321 1234567 becomes 923211234567 and 050 123 4567 becomes 971501234567.

Can the Conversions API bypass Meta's health and wellness restrictions?

No. The restrictions apply to the data itself, whether it comes from the Pixel or from your server. Check Events Manager for notices on your dataset and never send health information.

Can I set up the Conversions API on shared cPanel hosting?

Yes, if your host runs PHP with cURL and allows outbound HTTPS requests, which most shared hosts do. Store the access token outside the public web folder and check the PHP error log if events don't appear.

Want Leads Counted Properly?

Tell us how your forms are built and which ads you run. We can set up the Pixel and Conversions API on your forms, test deduplication and leave you with numbers you can trust.

Get in Touch