Your pixel fires on page load. So Meta learns to find people who load pages, and it gets very good at it. Play counts are a vanity metric for the same reason: they count arrivals, not attention. This is the how-to that fixes it. Which moments of a VSL to send to Meta, how to send them twice without counting them twice, and which one to put in charge of your ad sets.
Don't send Meta percentages. Send two or three named moments from your VSL: reached the mechanism, reached the pitch, clicked to order. Fire each one from the browser pixel and the Conversions API with the same event name and event_id, so Meta counts it once.
Optimize for Purchase once an ad set gets about 50 in a week. Until then, optimize for the reached-the-pitch event, and let your cart send the real Purchase back from the server.
Why Meta needs watch time at all
Meta learns from the events you send it. A PageView from someone who left after two seconds counts exactly the same as one from someone who watched for thirty minutes. Feed it only page loads and it will find you more people who load pages.
The learning phase makes this sharper. Meta says an ad set leaves learning "after about 50 results in the week after the ad set's last significant edit". When it can't get there, the status turns to Learning limited. A cold VSL rarely makes 50 sales a week in one ad set. It can easily put 50 people in front of the pitch. That gap is the whole reason a watch event is worth sending.
Pick moments, not percentages
Every video tool and every AI answer you will find says the same thing: fire at 25, 50 and 75 percent. Some players fire every 5% or every 10%. That is fine for building broad audiences. As a signal to optimize on, it is noise.
Why 25/50/75 breaks on long videos
On a 40-minute VSL, 25% is minute 10, which is just somewhere in the story. 75% is minute 30, often after the pitch has already happened. A percentage lands wherever the arithmetic puts it. It has no idea what the viewer just heard.
The three moments that predict a buyer
A VSL is built in sections, and three of them mark a change in the viewer:
- Mechanism reached. The viewer has heard why this works. Belief is forming.
- Pitch reached. The viewer has heard the price and stayed for it.
- Order click. The viewer went to the checkout.

How to find the pitch second
Take the seconds from your script and check them against your retention curve. In TrackPlay, Pitch Time marks the pitch on the retention graph. If you haven't set one, the pitch read from the transcript stands in, labelled as detected. Write the three seconds down. Everything below uses them.
Standard event, custom event, or custom conversion?
Meta has 17 standard events and none of them is about video. ViewContent is the nearest. That leaves three tools, and they do different jobs.
- Custom event: any name you choose, up to 50 characters. You fire it like any other event.
- Custom conversion: a rule on top of a standard event, a custom event or a URL. You can pick it as the performance goal of an ad set. The limit is 100 per ad account.
- Standard event: Meta already knows what it means, which helps its models, but you can't give it your own meaning.
Send the pitch moment as a custom event such as VSL_Pitch, then wrap it in a custom conversion so you can optimize and report on it. Keep ViewContent for general interest. Never send a watch moment as Lead or Purchase. It pollutes the one signal you most need clean.
Name events neutrally. Meta's rules say event names, custom conversions and custom data must not "reflect, imply, or be based on" prohibited information, and health conditions are on that list. Most VSL offers sell health, wealth or relationships. Use VSL_Pitch. Never a name that says what the product treats.
The event plan
Here is the full set for a typical VSL page. The seconds are illustrations. Use your own.
| Event | Fires when | Type | Used for |
|---|---|---|---|
| PageView | Page load | Standard | Reporting only |
VSL_Play or ViewContent | A real play | Custom or standard | Audiences, excluding bouncers |
VSL_Mechanism | The mechanism is revealed (example: 06:30) | Custom, then custom conversion | Audiences, reporting |
VSL_Pitch | The pitch starts (example: 22:10) | Custom, then custom conversion | Optimization under 50 purchases a week, audiences, lookalike seed |
| ViewContent | Order button click | Standard | Clicked but didn't buy |
| Lead | In-video form submitted | Standard | Lead-gen funnels, better matching |
| Purchase | The cart posts the sale back | Standard | Optimization at 50 or more a week, value |
Browser pixel plus Conversions API, counted once
Meta recommends running both: the pixel in the browser and the Conversions API from a server. The browser copy gets blocked by ad blockers and privacy settings. The server copy doesn't. Send both and Meta has to be told they are the same event.
The rule has two halves, and most guides quote only one:
- The pixel's
eventIDmust equal the server'sevent_id. - The pixel's event name must equal the server's
event_name.
Meta matches the pair within 48 hours and generally keeps the one that arrived first. There is a fallback that matches on fbp or external_id plus the event name, but it only works when the browser event arrives before the server event. Don't build on it.

In practice: create one ID in the browser when the moment fires, pass it to the pixel as eventID, and send the same value to whatever posts the server copy. There are three ways to run the server side: a direct integration with a system-user token, Meta's Conversions API Gateway, or a partner integration. Signals Gateway is Meta's newer, broader self-hosted option.
TrackPlay sends its own server events to Meta: ViewContent on CTA clicks and watch milestones, Lead on a captured lead, Purchase from the cart, and a custom HotLead event for viewers who look ready but haven't bought. If you also fire browser events for the same moments, check how the pairs land in Events Manager before you trust the counts.
Whatever the route, test before you trust it. The Test Events tab in Events Manager shows each event as it arrives, from the browser and from the server, so you can see both halves of a pair land.
Event match quality: why anonymous viewers barely count
A server event only helps if Meta can tie it to a person. Event match quality scores that from 0 to 10, on the last 48 hours of Conversions API web events. Meta ranks the keys that raise it:
- High: email and click ID.
- Medium: phone, external ID and browser ID.
- Low: name, city and zip.

A cold viewer watching your VSL has a browser ID and nothing else, unless you kept the click. Capture fbclid when they land and build fbc in Meta's format: fb.subdomainIndex.creationTime.fbclid, with the creation time in milliseconds. Keep it for 90 days and never change its case. TrackPlay captures fbclid on the session and builds fbc from the real click time.
Hash email, phone and name with SHA-256 after trimming and lowercasing. Never hash the IP address, user agent, fbc or fbp. Send contact data only with consent, which for EU and UK traffic means a consent banner, and never put an email in a URL.
The biggest lift is an email. A lead form inside the video, before or at the pitch, turns a viewer Meta can barely match into one with the two High keys. It also gives you a Lead event worth sending.
Optimize for the pitch, retarget by depth
Sending events is half the job. The other half is telling each ad set which one to chase.
Which event to optimize for
Purchase, if the ad set clears about 50 purchases a week. Otherwise the VSL_Pitch custom conversion. Check weekly and move up as soon as the volume allows. This is a judgement built on Meta's 50-results guidance, not a Meta rule for VSLs. The logic is simple: the pitch event is the deepest signal with enough volume to get out of learning.
Audiences by watch depth
Meta's own video-view audiences cover videos played on Facebook and Instagram. A VSL on your own page needs events. With the plan above you can build:
VSL_MechanismminusVSL_Pitch: watched the story, missed the offer.VSL_Pitchminus Purchase: heard the price, didn't buy.- A lookalike seeded from the pitch audience.

If your data source is restricted
Since early 2025, Meta has restricted datasets it classes as health and wellness, and some other categories. There are three levels: Core Setup, a restriction on mid and lower funnel standard events, and a full restriction. On a restricted dataset, custom events are blocked until you review them. Meta's own suggestion is to use "more upper-funnel events", which is exactly what a watch moment is.
Check yours in Events Manager: open the dataset, go to Settings, and look at the data source category. Keep condition words out of event names, URLs and parameters. That is the guidance, and it is where we stop.
Close the loop: the purchase comes from the cart
The sale happens on the cart's domain, after the video page is gone. A thank-you page pixel misses every buyer whose browser blocks it. The cleaner path is the cart posting the sale back, and a server sending Purchase to Meta with value, currency, fbc, fbp and a hashed email.

That is what cart postbacks do in TrackPlay. ClickBank, BuyGoods, Digistore24, WarriorPlus, JVZoo, Kiwify and ElasticFunnels post the order back, it joins the session that played the video, and the Meta Conversions API integration sends the Purchase. Only purchases and rebills fire, never refunds or test orders.
If you also fire a Purchase pixel on the thank-you page, check in Events Manager that each order is counted once before you trust the numbers. The simplest setup is one Purchase source: the server.
Step-by-step setup
- In Events Manager, copy the dataset (pixel) ID and create a Conversions API system-user token.
- Check the data source category under Settings. If it's restricted, review your custom events.
- Mark the three seconds: mechanism, pitch, order box. Use your retention curve or Pitch Time.
- Fire a named custom event at each second from the player. In TrackPlay, add a Custom pixel at a chosen second and put your own call in it, such as
fbq('trackCustom', 'VSL_Pitch', {}, {eventID: id}). - Send the same moments server-side with the same
event_nameandevent_id, plus fbc, fbp, IP and user agent. - Add a lead form in the video before or at the pitch, so later events can carry a hashed email.
- Connect the cart postback, so Purchase goes server-side with value and currency.
- Watch the Test Events tab, confirm deduplication in Events Manager, then check match quality after 48 hours.
- Create custom conversions:
VSL_Pitchfor optimization,VSL_Mechanismfor audiences. - Pick the optimization event with the 50-a-week rule, and leave the ad set alone while it learns. A significant edit resets learning.
You may have read that iOS tracking needs eight events ranked by priority. Meta removed that limit in 2025 and now handles eligible web events automatically. Skip that step.
The same idea on Google Ads and GA4
GA4's enhanced measurement sends video_start, video_progress at 10, 25, 50 and 75 percent, and video_complete, but only for embedded YouTube videos with the JS API enabled. A VSL on any other player needs its own events. TrackPlay sends video_start, video_progress, select_content, generate_lead and purchase to GA4 over the Measurement Protocol, with a transaction ID on purchases.
GA4 now calls conversions key events, and you import a key event into Google Ads as a conversion. Keep watch events as secondary conversion actions unless you mean to bid on them. For Google Ads directly, the player can fire your Google tag at the pitch second. Google's version of match quality is Enhanced Conversions, which uses a hashed email. TrackPlay doesn't send Enhanced Conversions or offline conversion uploads, so set those up on the Google side if you need them.
The one-paragraph version
Find the mechanism and pitch seconds in your own VSL. Fire them as neutral custom events from the browser and the server, same name, same ID. Capture the click and ask for an email so Meta can match the viewer. Optimize for the pitch until an ad set makes about 50 sales a week, then for Purchase, and let the cart send the Purchase from the server. To fire those events on watch time instead of page load, start with watch-time pixels. The free plan gives you 3,000 plays a month, free forever, with no card.
