Lock in $67
Method

Facebook pixel quality through Claude: what gets left out

The dataset tools answer honestly about what they found. They never report what is missing — and on pixel health the missing line is usually the whole diagnosis.

Espen Opdahl Published 6 min read
The short answer

The pixel tools report what fired. They do not report what didn't. An event that never happened comes back as an empty list, not a zero. A match key you are not sending is missing from the response entirely, rather than named at zero coverage. A pixel whose last browser event arrived in February 2024 still reports is_active: true. Every one of those is a silence the model reads as nothing-to-flag, and on signal quality the missing line is usually the diagnosis.

You ask Claude to review your pixel health. It comes back with a tidy paragraph: PageView is arriving, event match quality is such-and-such, first-party cookies are on. Nothing alarming.

Everything in that paragraph is true and it is not an audit. The tools answered about the events they found and the identifiers they were handed. They were never asked, and cannot say, what should have been there and wasn't — and a summary written from their output inherits that blind spot without ever announcing it.

Four tools, and what each one is for

Meta's official ads connector — the MCP server the ads assistant runs on — splits pixel diagnostics across four calls, and the split is stricter than it looks. Listing your pixels gives you names and timestamps and no health data at all. The details call adds configuration and ownership. Quality is where match rates live. Stats is where event counts live, and each tool's own description tells the model to go elsewhere for the other thing.

Every pixel came back twice

The first surprise was in the listing, before any health data. On 28 August 2026, across two live business portfolios, every dataset was returned twice — same ID, same name, two rows. The total_count field counts the rows, so a portfolio with two real pixels reports four.

The duplicate row is not a copy. It carries a different creation timestamp, and its last_fired_time reads 1969-12-31, with a time that resolves to Unix time zero in US Pacific. It is the machine's way of writing never, formatted as a date. Ask a model to summarise a list containing that value and you may well be told your pixel last fired in 1969, or that you have twice as many pixels as you do.

The same epoch stamp turns up in server_last_fired_time on pixels that have genuinely never received a server-side event. In that position the value is correct and useful: it means the Conversions API has never sent anything to this dataset. It just does not look like a diagnosis. It looks like a bug.

Active does not mean receiving

One pixel in the list reports is_active: true with a last_fired_time in February 2024. It has not seen a browser event in over two years and it is still active, because active means the dataset exists and has not been deleted. It is a lifecycle flag, not a heartbeat.

Check it first, and read one line to do it: put is_active next to last_fired_time and ignore the flag. The timestamp is the health signal.

worth knowing Meta's own API documentation shows a match key reported at zero coverage. The connector's reply didn't contain one.

Match quality on the pixel that returned a full score named four identifiers: IP address, user agent, the fbp browser cookie, and an external ID. Email and phone — the two keys that move event match quality most — were not in the list at any value. Meta's Dataset Quality API documentation, which is the underlying surface, publishes an example response containing a deduplication key with its coverage at zero percent. So the API can name a key you are not sending. Whether the connector filters those rows out or this dataset never had them, one response cannot tell you, and I have not verified which.

The three shapes a quality response comes back in

Reading the same call across several pixels, the reply arrives in three different shapes, and they are easy to mistake for one another:

{"web":[]}

{"web":[{"event_name":"PageView"}]}

{"web":[{"event_name":"PageView","event_match_quality":{
   "composite_score": 4,
   "match_key_feedback":[{"identifier":"ip_address", …},
                         {"identifier":"user_agent", …},
                         {"identifier":"fbp", …},
                         {"identifier":"external_id", …}]}}]}

The first is a dormant pixel: an empty array under a channel key, which is this connector's house style for nothing found — the same shape the account errors tool uses for a clean sweep. The third is the full answer, with a composite score Meta documents as being out of ten. Coverage values are trimmed above; the identifier list is the finding, not the percentages.

The middle one is the trap. Events are arriving — the event name proves it — and there is no event_match_quality key at all. Not a low score. No score. A summary built from that response will happily tell you PageView is being received and say nothing about quality, and the reader hears two pieces of good news where the tool supplied one piece of information and a gap.

Only the web channel ever came back, on every pixel I called, though the tool description promises grouping by web, offline, CRM and custom attribution. Empty channels are omitted, so a channel you never set up looks identical to one that is set up and silent.

Most of this job is knowing which silence means what. See how the weekly routine reads an account →

What came back What it actually says {"web":[]} Nothing has fired here {"event_name":"PageView"} Events yes, quality unknown — no score field at all {… "composite_score" …} A real answer, keys listed The middle row is the one a summary turns into good news.

Event counts have the same hole

The stats call defaults to a seven-day window, will not look back further than 28 days, and returns hourly buckets rather than a total. Ask it for an event that has never fired and you get "stats": [] — the same empty array, which is not the same statement as a count of zero, though it will be summarised as one.

There is a totals mode, and it behaved oddly enough to be worth knowing about before you rely on it. Asking for total counts on a single-event pixel returned several rows carrying the same event name and different counts, with no field naming the dimension that splits them, and their sum did not match the sum of the hourly buckets over the identical window. I would not quote either figure to anyone without checking it in Events Manager first, which is the same standing advice as reading a campaign with no purchases.

  • Counting pixels from the listing. Duplicated rows inflate total_count. De-duplicate on the dataset ID before you report a number to anyone.
  • Treating is_active as health. It survives years of silence. The last-fired timestamp is the flag that means anything.
  • Asking "is my match quality good?" and stopping. The response can lack a score entirely, and a missing score summarises as an absence of problems.
  • Reading a missing match key as a good sign. The keys you never send are the ones not in the list. Absence there is the finding, not the all-clear.

An empty array is a sentence about the tool, not about your business.

Try this tonight Ask for your datasets, then ask for quality on each one, and read only two things: whether the dataset has fired this week, and which identifiers are named in the match-key list. If email or phone is not in that list you are not sending them, and that is a Conversions API job for whoever owns your checkout — put it in writing to them tomorrow with the identifier names attached. If a dataset has not fired in months, decide tonight whether to delete it, because the ones you leave lying around are what make next month's audit take an hour instead of ten minutes.

The dataset tools are a small corner of a long manifest — the rest is in the full tools list. If you have not connected an account yet, start with the setup.

Frequently asked questions

Can Claude check my Facebook pixel's event match quality?

Yes, through Meta's official connector, and the score comes straight from Meta rather than being estimated. Read the identifier list underneath it rather than the score on its own — the keys that are missing from that list are the ones you are not sending, and they are what a low score is made of.

Why does my pixel show a last-fired date in 1969?

That is Unix time zero formatted as a date, which is how this response writes "never". On the duplicated listing rows it appears to be an artefact; on server_last_fired_time it is a genuine finding and means no server-side event has ever reached the dataset.

Does an empty result mean my pixel is broken?

Not by itself. It means nothing matched the question you asked, which could be a dead pixel, an event name that has never fired, or a window too short to contain anything. Widen the window and ask for all events before you conclude anything, then confirm in Events Manager.

Should I trust the event counts it reports?

Treat them as a pointer, not a figure to quote. The hourly buckets and the totals mode disagreed on the same window for me on 28 August 2026, and neither is the number your finance side should see. Use it to find what to look at, and read the real count in Events Manager.

Reading an account without being fooled by it

Ten modules on running Meta ads through Claude — including the weekly signal check, and what an empty reply is actually telling you.

See what's inside — $67 founding price

Pre-sale: the modules are still being recorded and founding members get the checkout link first. No charge today. Public price $97.

Espen Opdahl Writes The Claude Ads Operator. I run Meta ad accounts through Claude and Meta's official ads connector, and I write down what actually happens — including the parts that don't work. Not affiliated with Meta or Anthropic.