You set up the catalog, the pixel fires, products look perfect in Commerce Manager, and yet the match rate reads 0%, or some demoralizing single digit. Nothing is "down," no error appears anywhere, and dynamic ads keep running — just badly. A low catalog match rate is one of the most common silent failures in Meta ecommerce setups, and it almost always comes down to one unglamorous fact: your pixel and your catalog are using different ID systems for the same products.
What the match rate actually measures
Every time someone views a product, adds to cart, or purchases, your pixel (and Conversions API) sends an event carrying a content_ids field: the identifier of the product involved. Separately, your catalog is a list of products, each with its own id (the retailer ID). The match rate is simply the percentage of your events whose content_ids correspond to an id that exists in the catalog. When the IDs line up, Meta knows which producteach person engaged with. When they don't, Meta knows someone did something on your site, but not what, and every feature that depends on product-level knowledge starts running blind.
That's why 0% is such a specific clue. Partial breakage, some products matching, produces middling rates. Zero percent means the two systems share no vocabulary at all, which is almost never a configuration slip on one product — it's a systematic ID mismatch.
The usual suspect: variant IDs vs product IDs
On Shopify, the overwhelmingly common cause: every product has a product ID, and each size/color combination has its own variant ID. Shopify's native Facebook integration sends variant IDsin the pixel's content_ids. If your catalog was built by the same native integration, it lists variant IDs too, and everything matches. But if the catalog came from anywhere else — a third-party feed app, a manually uploaded CSV, a data feed built around product IDs, or an agency's old setup — the catalog speaks product ID while the pixel speaks variant ID. Same store, same products, zero overlap. The match rate flatlines.
The same class of mismatch happens outside Shopify: SKUs vs database IDs on WooCommerce, a feed tool that prefixes IDs (shopify_US_12345_67890 formats) while the pixel sends bare numbers, or a migration that regenerated IDs while the old catalog kept the originals. Different costumes, same disease: two ID dialects that never intersect.
The 15-minute diagnosis
- Capture what the pixel says. Open Events Manager, pick your ViewContent event, and inspect recent events (Test Events works too: browse a product page yourself). Find the content_ids value and copy a few examples. This is dialect A.
- Capture what the catalog says.In Commerce Manager, open the catalog's items and look at the Content ID / retailer ID column for the same products you just viewed. This is dialect B.
- Compare them character by character.Not approximately — exactly. A prefix, a suffix, product-vs-variant numbering, or even formatting differences make the match fail. The moment you see "pixel sends 4432198710 but catalog lists shopify_US_887privatize_4432198710," you have your answer.
- Check the event coverage too.While you're in Events Manager, confirm ViewContent, AddToCart, and Purchase all carry content_ids at all. Some custom themes fire the events but drop the product payload, which produces the same symptom from a different wound. Our pixel troubleshooting checklist covers verifying event payloads.
The fix: one source of truth
The repair principle is always the same: make one system generate both sides. In practice, in order of preference:
- Rebuild the catalog from the same integration that runs the pixel.On Shopify, that means letting the Facebook & Instagram app own the catalog feed instead of a separate CSV or feed tool. Both sides then emit variant IDs from the same source and match by construction. This fixes it permanently, including for products you add next month.
- If you must keep a third-party feed(extra fields, multi-country feeds), configure it to output the same ID form the pixel sends — most serious feed tools have an "ID" setting offering variant vs product ID precisely because of this problem. Change it, re-sync, and give it a day.
- Avoid the tempting half-fix of editing pixel code to match the catalog. Custom pixel overrides on a platform store create a fork that breaks again at the next theme or app update, and future you will spend a day rediscovering it.
After the fix, the match rate doesn't jump instantly — it's computed over a trailing window of events, so expect it to climb over several days toward the healthy range. Anything above ~90% is good; 99% is achievable when catalog and pixel share a source. Persistent stragglers are usually discontinued products still getting traffic, or bundles/gift cards that exist on the site but not in the feed.
Why this quietly costs more than it looks
A broken match rate never announces its price, so here is the itemized bill. Dynamic retargeting dies first: showing people the product they viewed requires knowing which product they viewed. With no match, Meta falls back to guessing from your catalog — generic carousels of your bestsellers to someone who wanted one specific jacket. Advantage+ catalog ads degrade:the machine that decides which product to lead with for each person is exactly the machine you've blinded. Product-level learning disappears:Meta can't learn that your ads sell product A to one audience and product B to another when every event says "something happened." And most insidiously, everything still runs. Campaigns deliver, money spends, some sales happen, and the account just performs a permanent 20-40% below its potential with no error message to blame. It joins the family of failures we call the expensive quiet ones, alongside the cases in the hidden cost of broken tracking.
Beyond Shopify: where each platform hides the same bug
The variant-vs-product confusion is Shopify's signature version, but every stack has a native dialect problem worth knowing. WooCommerce: pixel plugins typically send the WordPress post ID, while feed plugins default to SKU — two different plugins, two different vocabularies, and the fix is forcing both to the same setting (most plugins expose it). Custom builds: the frontend team hardcodes whatever ID the template had handy while the feed comes from the ERP, and nobody ever compared them; here the diagnosis step matters more than anywhere because the mismatch can be subtle, like a numeric ID sent as a string with whitespace. Multi-country catalogs: localized feeds often prefix IDs per market (US_, EU_) while the pixel sends the bare ID everywhere, so match rate looks fine in your home market and quietly dies abroad — segment the check by country if you sell in several. After any replatform or migration: assume the IDs changed until proven otherwise, and re-run the 15-minute diagnosis as part of the cutover checklist; it belongs on the same list as the items in what breaks tracking after a migration.
Don't confuse it with its cousins
Two neighboring problems get mistaken for catalog match rate, and chasing the wrong one burns afternoons. First, Event Match Quality measures how well Meta can match events to people (via hashed emails, phones, click IDs), while catalog match rate measures matching to products. An account can have a 9.0 EMQ and a 0% catalog match, or the reverse — they fail independently and are fixed in different places. Second, catalog rejectionsin Commerce Manager (items disapproved for policy, missing images, or broken links) reduce which products can serve in ads at all, which looks like "dynamic ads are broken" but has nothing to do with IDs. Quick sorting rule: products not showing at all points to rejections, wrong or generic products showing points to match rate, and ads reaching the wrong people entirely points to EMQ — start with improving Event Match Quality for that last one.
What a healthy setup looks like when you're done
A quick definition of finished, so you know when to stop: match rate above 90% and stable; ViewContent, AddToCart, and Purchase all carrying content_ids from the same generator that feeds the catalog; dynamic retargeting showing people the exact items they viewed (test it yourself: browse two products logged into a test profile and watch what the ad shows you a day later); and a one-line note in your runbook recording which system owns the IDs, so the next person — or the next feed app — doesn't innocently fork the vocabulary again.
How it breaks again (because it will)
The bitter epilogue to most catalog-match fixes: the mismatch returns. The common relapses are all routine business events. Someone installs a new feed app for Google Shopping and points it at the Meta catalog too. A migration or replatform regenerates product IDs. An agency inherits the account and uploads "their" feed template. A multi-country expansion adds prefixed IDs for the new locale. None of these announce themselves as tracking changes, and match rate has no alarm attached — it just sags, dynamic ads get vaguely worse, and someone eventually blames creative fatigue. If catalog ads are a meaningful share of your spend, put match rate on your monthly checklist next to the reconciliation habit from platform revenue vs actual revenue, or have monitoring watch it continuously. A number that only breaks silently is a number somebody has to be watching on purpose.
Stop finding out about broken tracking from your client.
Taglert monitors your pixels and conversion tracking 24/7 and alerts you the moment something breaks. 7-day free trial, no credit card.
Start your free trial