A content gap analysis only helps if it ends in a short list of pages you will write. Most do not, because the raw output is thousands of keywords. We ran the process below on our own site against five competitors on 2026-10-11 and show the real count at every step, so you can compare yours with ours.

Before you start
- Time: about three hours. Skill: comfortable with spreadsheets; Python 3 helps.
- Your data: a Search Console export of queries for the last three months (Performance, Queries, export).
- Competitor data: a ranking-keyword export per competitor from any rank tool. We used the DataForSEO Labs ranked keywords endpoint (US, English); all five exports cost under one US dollar in API fees.
- You will end with: ten ranked page briefs, each tied to a competitor page you can read.
Step 1: Pick five competitors by who ranks
Search the five to ten queries your buyers use and note which domains hold the top 10. Keep the five that appear most often. Expect uneven sizes: ours ranged from 461 to 8,176 ranking keywords, and that spread shapes every later count.

Expected result: five domains and the queries that surfaced them. Common mistake: choosing brands you admire instead of domains that share your results pages. Honest caveat: we picked our five as B2B SaaS content and SEO peers rather than from a results-page overlap run, so our numbers illustrate the method and do not prove our choice.
Step 2: Export and merge their top-10 keywords
Export every keyword where each competitor ranks in positions 1 to 10, with search volume and the ranking URL. Stack the five files into one table with a competitor column. Keep the URL, because Step 4 depends on it. Our merge held 679 rows and 672 unique keywords. Animalz had 222 top-10 keywords and Single Grain 25.
One limit applies. For four of our five, we pulled each domain’s 1,000 highest-volume keywords at any position, not every keyword, so large-site counts are floors. Single Grain’s 25 mostly reflects that cut.

So do not require agreement. If you keep only terms several competitors share, you keep one keyword. Expected result: one CSV with five competitors. Common mistake: deduplicating by keyword and losing the URLs.
Step 3: Filter for fit and subtract what you cover
Raw competitor lists are mostly noise. Keep terms you could credibly serve, then remove anything you already rank or earn impressions for. This script runs Steps 3 and 4 on two CSV files: competitor_rankings.csv and our_queries.csv. We executed it on our data.
import csv, re, collections
FIT = re.compile(r"b(seo|content|b2b|saas|agency|agencies|abm|link building|backlink|geo|aeo)b")
SKIP = re.compile(r"b(new york|nyc|ny|chicago|miami|dallas|houston|boston|austin|atlanta|seattle|denver|london|near me)b")
rows = [r for r in csv.DictReader(open("competitor_rankings.csv", encoding="utf8")) if int(r["position"]) <= 10]
mine = {r["keyword"] for r in csv.DictReader(open("our_queries.csv", encoding="utf8"))}
unique = {r["keyword"] for r in rows}
fit = {k for k in unique if FIT.search(k) and not SKIP.search(k)}
open_kw = fit - mine
clusters = collections.defaultdict(list)
for r in rows:
if r["keyword"] in open_kw:
clusters[r["url"]].append((int(r["volume"]), r["keyword"]))
print("unique top-10 keywords:", len(unique))
print("pass fit filter:", len(fit), "| not already ours:", len(open_kw), "| competitor URLs:", len(clusters))
ranked = sorted(clusters.items(), key=lambda kv: -max(kv[1])[0])
for url, kws in ranked[:5]:
top = max(kws)
print(f"{top[0]:>6} {len(kws):>2} kw {top[1][:34]:<34} {url.split('//')[1][:60]}")
What this does: filters by topic words, removes city terms, subtracts exact-match queries you already have, and groups the rest by URL. Output from our run:
unique top-10 keywords: 672
pass fit filter: 234 | not already ours: 231 | competitor URLs: 95
8100 4 kw content creation firm www.siegemedia.com/strategy/best-content-creation-services
5400 14 kw business to business marketing age www.siegemedia.com/strategy/best-b2b-content-marketing-agenc
5400 2 kw b2b marketing company directiveconsulting.com/blog/the-best-b2b-marketing-agencies
5400 4 kw b2b marketing firm directiveconsulting.com/blog/top-25-b2b-enterprise-marketing
4400 1 kw internet marketing agency directiveconsulting.com/blog/18-top-internet-marketing-agenc
Common error: a UnicodeDecodeError on Windows means the file was opened without encoding="utf8".

Expected result: well under half of the raw list survives. Common mistake: trusting the topic words blindly. Read 30 random survivors and tighten the word list. Matching is exact, so near variants slip through.
Step 4: Group the gaps by competitor URL
One page often ranks for many variants, so each URL is one topic to judge, not many. Our 231 keywords sit on 95 URLs. Fifty-four URLs rank for a single keyword. Twenty-one URLs carry 140 of 238 keyword rows (59%), so read those first.

| Competitor URL | Gap keywords | Top keyword | Monthly searches |
|---|---|---|---|
| siegemedia.com/strategy/best-content-creation-services | 4 | content creation firm | 8,100 |
| siegemedia.com/strategy/best-b2b-content-marketing-agencies | 14 | business to business marketing agency | 5,400 |
| directiveconsulting.com/blog/the-best-b2b-marketing-agencies-for-technology-companies-in-2026 | 2 | b2b marketing company | 5,400 |
| directiveconsulting.com/blog/top-25-b2b-enterprise-marketing-agencies | 4 | b2b marketing firm | 5,400 |
Source: DataForSEO Labs ranked keywords, US, 2026-10-11, n = 95 URLs.
Expected result: a few dozen groups. Common mistake: adding volumes across variants. They share the same searchers, so use the largest number.
Step 5: Score the groups and pick ten
Score each group on three columns: demand (largest monthly volume), breadth (keywords on the URL) and fit (can you add something the competitor page lacks, such as first-party data or a format they skip). The first two come from the data. Fit is your judgment.

Worked example, measured: a Siege Media agency list ranks top 10 for six technical SEO variants (services, service, agency, company, consultants, companies), with 2,400 monthly searches at most. None of the six is an exact query in our Search Console export. We do publish a technical SEO guide for SaaS, yet the six terms are missing from our rankings. That is one gap, and it is an intent mismatch: a guide against a ranked agency list.
Common mistake: picking by volume alone. Our biggest cluster is a ranked list of content agencies, a format where an agency ranking itself has a conflict of interest. Judge format fit before volume.
What did our own gap analysis show?

Over 90 days to 2026-10-08, our site had 15,337 queries and 236,756 impressions in Search Console, with 59 clicks. We are not short of queries. We are short of queries that match the pages competitors win with, mostly ranked lists and service pages. That is a finding about these five domains on one date, and it is not a forecast.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Survivors include off-topic terms | Topic word list too broad | Add a skip list and read 30 random rows |
| Almost no overlap between competitors | Normal (666 of 672 were unique to one) | Do not filter on agreement |
| A competitor shows very few top-10 rows | Export capped by volume | Export by position or paginate |
| Gap list includes topics you cover | Exact-match comparison | Also check Search Console by page |
Checklist
- Five domains chosen from shared results pages.
- Top-10 keywords exported with URLs and volume.
- Fit filter applied, 30 survivors read by hand.
- Your Search Console queries subtracted.
- Gaps grouped by competitor URL.
- Ten groups scored on demand, breadth and fit.
FAQ
How many competitors should a content gap analysis include?
Five keeps the merge readable. In our data, 666 of 672 keywords belonged to a single competitor, so more domains add reading, not agreement.
How often should I repeat it?
We rerun ours quarterly, and after each batch of new pages ships. That is our practice, not a tested benchmark.
Do I need a paid tool?
No. A Search Console export and any ranking-keyword export work. Ours cost under one US dollar in API fees, plus about three hours. For a related method, see our Search Console API workflow and what AI competitor analysis uncovers.
Disclosure: MV3 Marketing sells SEO and content services, so we benefit if you hire us for this work. To have us run it, book a call.
Share this article
Ready to audit your organic growth opportunity?
$2,500 flat. 5 business days. Six deliverables tied to pipeline , not rankings. No retainer required.
Get the Organic Growth Audit →