[CODE]

The Negative Keyword That Blocks the Booking You Want Most

A negative keyword's damage is invisible by construction — what it blocks never appears in any report. Here's how a sibling account became the control group that exposed it, and why 'the resource exists' is not 'the resource works.'

8 min read AI-generated
google-ads automation typescript bnb ai

One account carried “20人” and “30人” as broad negative keywords. The logic was sound the day it was set: Property A sleeps up to fifteen, so a party of twenty can’t be served. Block the query, save the click.

Except the two properties can be booked together — fifteen plus eighteen is thirty-three — and combined bookings are exactly the order type the operator wants most. Meanwhile the sibling account, which never set those negatives, was converting 5.5 times a month on precisely those queries. Same market, same owner, opposite outcome. One property couldn’t house the party. Two could. The negative keyword didn’t know the difference.

Nobody could see the loss. That is the whole problem, and it’s the thread that runs through almost two weeks of work across this ad-management system.

A negative keyword’s damage is invisible by construction

Every other mistake in an ad account leaves a trace. Spend too much on a bad keyword and it shows up in the cost column. Point an ad at the wrong page and the bounce rate tells you. But a negative keyword blocks a query before it ever produces an impression — so what it costs you never appears in any report, because from the report’s point of view, that demand never existed.

This is not a subtle edge case. It’s the default behavior of the control, and it makes the wrong negative keyword and the right one look permanently identical. Add a good one, your junk traffic drops. Add a bad one, your junk traffic drops and a booking you’d have taken silently disappears. The two are indistinguishable from inside the account.

The only way to find the damage is to look somewhere the block isn’t in force. There are exactly two such places.

The first is another account. If account A carries a negative that account B doesn’t, then B’s converting search terms are a live picture of what A is throwing away. B is the control group A can’t be for itself. That’s how the “20人/30人” loss surfaced at all — not through analysis of the account that had the problem, but by comparing it against the one that didn’t.

The second is leakage inside the same account. A negative only blocks the queries it literally matches. If a query like “恆春泳池民宿” still converts because it fired on a different keyword, then you have evidence that the demand is real even while you’re about to blacklist the word “泳池” outright. Stopping a single zero-conversion keyword is not the same as sealing off an entire class of query — one is releasing a bet, the other is walling off a road.

I built these two cross-checks into a read-only audit tool. For every negative in each account, it looks for converting search terms in the sibling accounts that this negative would have blocked, and it looks for leaked conversions inside the same account. It answers one question: what would we see if this negative weren’t here?

The AI kept using last month’s data to veto this morning’s strategy

The same blindness has a second face, and it showed up in the copy generator. Three separate times in one day, the LLM writing ad copy tried to delete a keyword because it “had no conversion data” — when the keyword had been bought that same morning, a few hours earlier, specifically to chase a term a competitor account was winning on. Zero data wasn’t a verdict on the keyword’s value. It was a verdict on the keyword’s age.

That is a trap that closes on itself: a new keyword has no copy, so it never gets an impression, so it never produces data, so it’s judged worthless and deleted, so it never gets copy. The self-fulfilling prophecy runs in both directions — a blocked query looks like absent demand, and a brand-new keyword looks like failed demand. Both are the same illusion: no signal read as no value.

The fix was to give the model the list of keywords that are live but have no history yet, separately and explicitly, and forbid it from treating their silence as a judgment. I also had to write a per-account copy-rules file — a set of hard constraints the generator reads before every run — because the model, once it had a rule about “deferring to the sibling account’s strong keyword,” over-applied it to a product fact. It refused to write “five-room property, sleeps fifteen” on the grounds that “15人” was the sibling’s main keyword. But avoidance applies to what you bid to win, not to what you are. A property that houses fifteen people can’t refuse to say so because another account also bought the word.

When rules stack three layers deep, the model starts deriving conclusions from its own derivations, and each step drifts further from the ground. That’s the boundary I now draw: the LLM is good at optimizing existing copy against real data. It is bad at executing a strategy decided today whose data doesn’t exist yet. The second case gets hand-written copy from a file, not a generation.

”It exists” is not “it works” — and I got this wrong three times in one day

The deepest version of the principle isn’t about keywords at all. It’s about believing you’ve checked something when you’ve only checked one layer of it.

In a single day I made the same category of error three times, and each one nearly caused a wrong action:

I claimed an orphan sitelink was actively sending paid clicks to another property’s site. It wasn’t — the asset existed in the library but was attached to no serving layer, so it couldn’t spend a cent. I’d looked at the asset and not at the three tables that govern whether an asset is linked — campaign, customer, ad-group level.

I claimed the fleet’s negative keyword lists were all failing to take effect. They weren’t — every one was attached at the account level and working fine. I’d checked one attachment layer and missed the one they actually lived on.

And I wrote into a tool that “PMax doesn’t support shared negative-keyword lists.” It does — one of the accounts had a PMax campaign with exactly such a list attached. I’d ruled it out from an impression of how the API behaves, without querying.

In the Google Ads API, “a resource exists” and “which layer a resource takes effect on” are different tables, and the places a resource can take effect are usually more than one. Assets need three link layers checked. Negatives need four — account, shared list, campaign, ad group. Audience signals need two. Check one layer and you will misjudge; it’s not a risk, it’s a certainty.

The uncomfortable part: the tool I’d written to audit whether negatives were in force had the same bug baked in — it queried a single layer and would have declared the whole fleet compromised. A tool whose entire purpose is to check whether something is working failed because it only looked in one place. So the rule I encoded for both the humans and the machines is: before claiming any ad setting is or isn’t in force, query every layer it could be attached to. If you can’t find it, say “couldn’t find it,” never “it’s not there.” And don’t judge from an impression of how an API behaves — go query, go measure, or say plainly that you’re guessing.

Why this work only exists now

None of this — a read-only audit that cross-references six accounts to find invisibly-blocked demand, a copy generator that knows which keywords are too young to judge, a discipline of querying every attachment layer before touching anything — would have been done for a set of small B&Bs a few years ago. Not because nobody wanted it, but because the arithmetic never worked. Fine-grained cross-account ad hygiene at this depth was a full-time analyst’s job, and no B&B budget reaches for one. The reason it exists now is that AI collapsed the cost of building and running the tooling to something a portfolio of small clients can actually carry.

But the same AI is what made the invisibility problem sharper, not softer — because a model told to “remove keywords with no conversions” will confidently execute the self-fulfilling prophecy at scale. The judgment that has to stay human is the one that asks: is this zero a verdict, or just an absence I created by not looking where the answer lives?

The negative keyword blocking thirty-three-person bookings had been running for weeks, costing the owner the exact order they most wanted, and it left no trace anywhere a report could show it.