Query Fan-Out Coverage Audit: Which Branches of AI Search Does Your Site Actually Answer?
Fan-out planning tells you what to write before you write it. This post is the other half: auditing a site that already exists. Google AI Mode expands one query into 8-12 sub-queries — pricing branches, audience branches, comparison branches — and cites the pages that answer them. A coverage audit asks one question: when that fan hits your site, how many branches does it find? Method is three steps and zero paid tools: generate the sub-query tree with geo_fanout, pull your own sitemap, match tokens, then hand-review. I ran it on this site: 55 sub-queries against 190 pages, 18% raw coverage, 15% after removing false positives. The interesting output isn't the score — it's the miss list, filtered through an intent-fit pass that kills branches your product strategy has no business chasing.
TL;DR — A fan-out plan generates the sub-queries an AI engine will expand your seed into, before you write. A coverage audit runs the same expansion against a site that already exists and measures how many branches have a page answering them. Three steps, stdlib only: python3 -m zens_ink.geo_fanout "open source seo tools" --icp "indie developers and solo builders" --json, pull your sitemap, match modifier tokens to page slugs, hand-review the hits. Real run on zens.ink: 55 sub-queries, 190 pages, 10 raw hits (18%), 7-8 that survive review (~15%). The raw score overcounts — token matching produced confident nonsense like matching "ai powered" to a glossary page about AI Overviews. What you keep is the miss list: 20+ gold-mine branches with no page, then an intent-fit filter that deletes the ones your strategy shouldn't chase anyway. Coverage percentage is a dashboard number. The filtered miss list is a content calendar.
A fan-out content plan answers a question you ask before writing: given a seed keyword, which sub-queries will Google AI Mode, ChatGPT, and Perplexity expand it into, and which branches deserve pages? It’s generative — it works on intent that doesn’t exist on your site yet.
But most sites that care about AI visibility aren’t starting from zero. They have 50, 200, 2,000 published pages. For them the sharper question is retrospective: when the fan-out hits, how many branches find something? That’s a coverage audit, and it’s the missing half of the loop — the plan says what to write, the audit says what’s still missing after you wrote it.
The method is three steps and no paid tools. This post runs it on this site, shows the raw numbers, the false positives the matcher invented, and the part everyone skips: deciding which uncovered branches you should refuse to write.
Step 1: Generate the fan-out tree
Same tool as the planning post, JSON output this time:
python3 -m zens_ink.geo_fanout "open source seo tools" \
--icp "indie developers and solo builders" --json
For this seed: 55 sub-queries across 7 intent dimensions, 23 scored as gold mines (high commercial intent, low competition proxy), 17 validated against live autocomplete. Keep the JSON — the audit reads sub_queries and gold_mines.
The seed choice matters more here than in planning. Audit the keyword you actually compete on, not the one you aspire to. For this site that’s open source seo tools — it’s the head term the tooling overview and most of this journal orbits.
Step 2: Pull your own inventory
Your sitemap is the inventory. One caveat learned the hard way: fetch it with a browser User-Agent or Cloudflare’s bot rules may 403 the request.
import urllib.request, re
req = urllib.request.Request(
"https://zens.ink/sitemap-0.xml",
headers={"User-Agent": "Mozilla/5.0 ... Chrome/130"})
xml = urllib.request.urlopen(req, timeout=30).read().decode()
pages = [u.replace("https://zens.ink/", "").rstrip("/")
for u in re.findall(r"<loc>([^<]+)", xml)]
190 pages. Everything — journal, docs, glossary, pricing. A branch can be answered by any page type, not just blog posts; the /pricing/ page legitimately answers the pricing branch.
Step 3: Match, then review by hand
For each sub-query, strip the seed words and stopwords, and check whether the remaining modifier tokens appear in any page slug:
seed = set("open source seo tools".split())
STOP = {"for","the","best","is","it","vs","and","to","of","a",
"in","with","free","open","source"}
def tokens(query):
return {w for w in re.findall(r"[a-z]+", query.lower())
if w not in seed and w not in STOP} or {"*"}
for sq in fanout["sub_queries"]:
t = tokens(sq["query"])
hits = [p for p in pages
if t & set(re.findall(r"[a-z]+",
p.replace("-"," ").replace("/"," ")))]
Raw result on this site:
seed: open source seo tools | sub-queries: 55 | site pages: 190
COVERED: 10/55 = 18%
+ open source seo tools pricing -> pricing
+ open source seo tools for indie developers -> developers
+ open source seo tools for agencies -> journal/seo-reporting-tools-agencies
+ open source seo tools for developers -> developers
+ open source seo tools workflow -> journal/seo-workflow-pipeline
+ open source seo tools for content marketing -> journal/content-brief-generator
+ open source seo tools api -> journal/bing-webmaster-api-search-volume
+ open source seo tools guide -> journal/competitor-analysis-seo-guide
+ open source seo tools ai powered -> glossary/ai-overviews <- false positive
+ how to use open source seo tools ... -> developers <- token reuse
18% looks damning. Then you review the hits and it gets worse before it gets better — two of the ten are noise:
ai poweredmatched the glossary page on AI Overviews because both contain the tokenai. That page is about a SERP feature, not about AI-powered tooling. Struck.- The
guidematch sent “open source seo tools guide” to a competitor-analysis guide because slugs contain the word guide. Generic tokens —guide,api,best,free— are match bait. Downgraded, not struck: the tooling overview genuinely answers part of the “best open source seo tools” branch even though the matcher missed it (the modifier was a stopword).
After review: 7-8 real hits out of 55. Call it 15%. When an AI engine fans this seed out, roughly one branch in seven finds an answer here.
The number is not the product
A 15% coverage score is a dashboard number. It becomes useful only when compared across time (did the journal move it?) or against a competitor (run the same audit on their sitemap — the method transfers as-is). What you actually act on is the miss list:
MISSING GOLD-MINE BRANCHES (20 of 46 misses)
x best open source seo tools x open source seo tools for small business
x top open source seo tools x open source seo tools for startups
x open source seo tools review x open source seo tools for ecommerce
x open source seo tools compared x open source seo tools for healthcare
x open source seo tools alternatives x open source seo tools for enterprise
x open source seo tools vs competitors x open source seo tools roi
...
Before this becomes a writing queue, it needs the filter nobody writes about: intent fit. Not every uncovered branch is a branch your strategy should cover.
Three deletions from this site’s own list:
cheapest,discount— the product is free; these branches are structurally unanswerable. Delete.for healthcare,for enterprise— real demand, wrong product. This toolchain is built for people who read changelogs, not procurement teams. Delete, even though autocomplete validated the demand.vs competitors,showdown— this site’s standing rule is no named competitor comparison tables; newer projects punching at competitors reads worse than no answer at all. The comparison intent still gets covered, but through methodology content — how to score SERP difficulty yourself, how to evaluate tools without a versus table — which is what the SERP difficulty model and this journal already do.
Roughly a third of the miss list died at the filter. That’s the filter working, not the audit failing. The branches that survive — audience-fit pieces like for small business and for ecommerce, the best/top roundup space where the overview page plays — those go to the calendar, get their real difficulty checked, and then get written.
Where this sits in the loop
The full GEO pipeline now has both directions:
- Plan — fan-out content planning: generate the tree before writing
- Audit — this post: measure which branches the existing site answers
- Verify difficulty — SERP difficulty scoring on the surviving shortlist
- Publish quotably — the GEO score methodology audits whether pages are actually extractable
- Measure visibility — the AI search visibility audit closes the loop
Run the audit quarterly per seed keyword. One command, one sitemap fetch, five minutes of review — and the miss list it produces is the only content calendar input that’s anchored to how AI engines actually retrieve.
FAQ
What is a query fan-out coverage audit?
It measures how many of the sub-queries an AI search engine would generate for your head keyword have a corresponding page on your site. You generate the fan-out tree for the seed (8-12 sub-queries across intent dimensions like pricing, alternatives, audience fit), list your site's pages from the sitemap, and match each sub-query's modifier tokens against your page slugs and titles. The result is a coverage ratio plus a list of uncovered branches — the branches are the actionable output, not the ratio.
How is this different from a content gap analysis?
Content gap analysis compares your pages against competitors' pages — it finds what exists elsewhere but not on your site. A fan-out coverage audit compares your pages against the sub-queries an AI engine generates from your seed — it finds what nobody may have written yet, including branches specific to your product. The two compose: fan-out coverage finds demand-side gaps, competitor gap analysis finds supply-side ones.
Why does the audit need a manual review step?
Token matching overcounts. In the real run, the sub-query 'open source seo tools ai powered' matched a glossary page about AI Overviews because both contain the token 'ai' — a confident false positive. Words like 'guide', 'api', and 'best' are too generic to trust as match evidence. Expect to strike one to three of every ten raw hits; the review pass is five minutes and it is the difference between an audit and a screenshot of an audit.
Should you write content for every uncovered branch?
No. The miss list needs an intent-fit filter first. For an open-source tool, the 'cheapest' and 'discount' branches are demand that doesn't apply — the product is free. Comparison branches may conflict with your positioning; if you don't do named competitor comparisons, you cover that intent with methodology content instead of a versus table. Roughly a third of any miss list dies at this filter, and that is the filter working.
Want to run this analysis on your own site?
ZensInk Pro automates this pipeline. One command, from seed keywords to content plan.
Get Pro →