Spend but no purchases: what Meta's connector returns
Question one of the audit, taken apart: what the official connector actually hands back when you ask where the money went that bought nothing.
Ask Meta's official ads connector which campaigns spent money and bought nothing, and the
list that comes back is not safe to act on. A campaign with no purchases does not return a
zero. It returns null, or the string Not available, or the field is missing
from the row altogether. Filters cannot catch what is not there: asked for campaigns with purchases
above zero, the connector handed back every campaign in the account, every one of them
null. Ask for the spend column, never the list of names.
The question is the right one. It is the first thing to check in any account and usually the most expensive thing you will find: money that went out and brought nothing back. Old prospecting left running, a geography that never converted, a test somebody forgot.
What almost nobody checks is the answer. You get a clean list of campaign names, it has the shape of a finding, and you start pausing things. This page is about what that list is actually made of, because on 25 August 2026 I asked the connector this exact question against live accounts and the answer was wrong in a way that leaves no trace on the page.
Three different ways of saying nothing
Every reporting row that comes back has to represent an absence somehow, and the connector has three separate conventions for it. Here is a real campaign row, values removed, from a purchase- optimised campaign that never recorded a purchase:
"name": "…",
"amount_spent": "…",
"omni_purchase": null,
"results": {"indicator": "actions:offsite_conversion.fb_pixel_purchase",
"value": "Not available"}
Purchases come back as null. The result count comes back as the words
Not available. And in a window where a campaign did not deliver at all, the spend field is
not zero either — the key is simply not in the row, so a request for spend returns a name and an id
and nothing else. Three renderings of nothing, and not one of them is the digit that the question
was about.
The field name is worth noting too, because it is not the one you would guess. Ask the
connector's own field-metadata tool about purchases and it does not resolve; the
canonical name is omni_purchase, and purchases is only its display label. It
counts purchase events from connected Meta business tools and from Meta itself, and its description
excludes messaging-ad purchases delivered to and from Europe and Japan.
Verified 25 August 2026 against a live account. Filtering at campaign level on
omni_purchase with the GREATER_THAN operator, whole-history window, the
connector returned eleven campaigns with spend attached and omni_purchase: null on
every single one. The opposite filter — EQUAL 0 — returned the same eleven rows. Two
contradictory questions, one identical answer. Meta's public documentation covers none of this.
The filter is not the safety net
The field metadata says omni_purchase is filterable and offers the full set of
comparison operators, so asking for zero-purchase campaigns looks like a solved problem. It is not,
because a filter compares values and a null is not a value. Rows with nothing to compare pass
straight through, in both directions.
It is not inert everywhere, and it would be dishonest to say it is. Narrow the same query to the last seven days on the same account and the spend filter returns an empty list, correctly, because there is nothing to return. The failure is specific and it is worse for being specific: the filter works when the data is there and quietly waves everything through when it is not — which is exactly the condition you were asking about.
The habit that catches this is asking for the number rather than the verdict, every time, on every question you would spend money on. See what the weekly routine checks →
Why "results" is not the shortcut either
The obvious workaround is to stop asking about purchases and ask about results instead, since
every campaign has some. The connector will give you that, and it tells you plainly why you should
not trust the column: each row's results object carries an indicator
naming which action it counted.
| What the indicator said | So a "result" in that row meant |
|---|---|
actions:offsite_conversion.fb_pixel_purchase | A purchase |
actions:offsite_conversion.fb_pixel_lead | A form fill |
actions:landing_page_view | A page that finished loading |
actions:post_engagement | A like or a comment |
mixed | More than one of the above, in one campaign |
Those all came out of live accounts on the same afternoon. Ranked in a single table they look
comparable and they are not; one campaign's result costs what a page load costs and another's costs
what a customer costs. The companion field is more honest about it —
cost_per_result comes back with the meaning printed in brackets after the figure,
(Website leads) or (Website purchases). That bracket is the part to read first,
and it is the part that gets dropped when a table is summarised for you.
How to ask it so the answer is worth something
-
Ask for the spend column, not the list START HERE
"Which campaigns bought nothing" invites a list of names. "Show every campaign with its spend and its purchases for this window, sorted by spend" invites a table you can audit, and the leak is whatever you can see and add up. A row whose spend is blank is not a small leak, it is a campaign that did not run. The trade: a longer answer with rows you have to read rather than a verdict you can act on — which is the point.
-
Ask the mirror-image question
Run the question and its opposite: which campaigns had zero purchases, then which had at least one. If both lists come back the same, the filter did not filter and neither answer means anything. This takes one extra prompt and it is the only check here that catches the failure from the outside, without knowing anything about how the connector works.
-
Read the indicator before the result
Whenever a results or cost-per-result column appears, ask what each row's result actually is before you compare any two of them. The trade: it kills the tidy account-wide "cost per result" number some reports lead with — there is no single one, and the connector will not produce it at account level for the same reason.
-
Total it against Ads Manager once
Take the leak figure and check it in Ads Manager with the same two dates before you pause anything. This is the one step that cannot be delegated and the one that makes the rest worth doing — the same arithmetic check that catches invented numbers.
A null is not a zero, and a list of names is not a finding. The money is in the column you did not ask for.
- Accepting a list of campaign names as the answer. Without spend attached, a trivial test and the biggest line in the account look identical.
- Assuming a blank cell means zero. It far more often means the campaign did not deliver in your window, which is a different fact with a different action attached.
- Pausing on the first pass. A campaign optimising for something other than purchases will always show zero purchases and may be doing its job perfectly.
- Letting the window drift. Two figures from two windows do not subtract, and the connector will happily produce both without mentioning it.
Try this tonight Ask for zero-purchase campaigns, then ask for the ones with at least one purchase, and compare the two lists. If they overlap at all, stop and re-ask with the spend and purchase columns shown — and the total you get from that second pass is the number worth taking into tomorrow.
This is question one of the four-question audit, taken apart. If you have not connected an account yet, setup runs to about three minutes.
Frequently asked questions
Is this a bug in the connector?
It behaves consistently, so it is more likely a design decision about how absent metrics are represented than a defect. Either way, Meta documents none of it, and the description here is based on watching the responses rather than on anything published.
Does the same thing happen in Ads Manager?
No. Ads Manager renders an empty metric as a dash and its filters are built around the same rendering, so filtering there does not have this failure mode. That is part of why it is a reasonable place to check a figure you intend to act on.
Which field should I actually ask for?
omni_purchase, which the connector displays as "purchases". The bare word purchases does not resolve as a field name, so a prompt that uses it is relying on the assistant to translate — usually it does.
Does zero purchases always mean wasted money?
No, and this is the judgement the tooling cannot make for you. An awareness or traffic campaign, a campaign younger than a couple of weeks, or one taking a trivial share of budget are all normal. Something older than that, optimising for purchases, spending real money and never converting is the one to look at.
The check that catches answers like this one
The course is the weekly routine that produced this page: the prompts, the verification step that stops a confident wrong answer reaching a decision, and the standing limits on what an assistant is allowed to change.
See what's inside — $67 founding pricePre-sale: the modules are still being recorded and founding members get the checkout link first. No charge today. Public price $97.