Listicle Gap Finder is a small Python script for link building outreach. Give it your brand, 3 to 5 other brands in your category and 5 to 8 buyer searches. It reads the pages that rank for those searches and flags the "gap" pages: best-of lists that name 2 or more of the other brands but not yours.
The problem
"Best X tools" and "X alternatives" pages send buyers straight to the brands they list, and AI answers read them too. Finding the ones that leave you out means searching, opening every page and counting names by hand.
What the app does
- Takes your brand, other brand names and buyer searches.
- Collects ranking pages from two search sources and removes duplicates.
- Reads each page and counts which brands it names, as whole words.
- Flags pages that name 2 or more other brands but not you.
- Writes a Markdown outreach list and a CSV with one row per page.
How it works
- Search.
GET /webfor ranked results andPOST /desearch/ai/search/links/webfor a second list of links per search. - Sort for reading. On-topic and roundup-style titles first.
- Read the top pages with
GET /web/extractas text. - Count brands and flag the gaps.
- Write the outreach list, CSV and a cost line.
The code
def web(self, query, start):
return self._search("web", f"{query} [start={start}]",
lambda: self.s.get(f"{BASE}/web", params={"query": query, "start": start}, timeout=120), "data")
def links(self, query, count):
return self._search("links", f"{query} [count={count}]",
lambda: self.s.post(f"{BASE}/desearch/ai/search/links/web",
json={"prompt": query, "tools": ["web"], "count": count}, timeout=180), "search_results")
def extract(self, url, attempt):
t0 = time.time()
try:
r = self.s.get(f"{BASE}/web/extract", params={"url": url, "format": "text", "js": "false"}, timeout=120)
except requests.RequestException as e:
self._log("extract", url, None, round(time.time() - t0, 2), 0, attempt)
return {"status": "error", "error": str(e)[:200], "text": "", "attempt": attempt}
text = r.text if r.ok else ""
self._log("extract", url, r, round(time.time() - t0, 2), len(text.strip()), attempt)
return {"status": r.status_code, "text": text, "attempt": attempt, "error": None if r.ok else r.text[:200]}
The folder has one script, listicle_gap.py, and an example queries.txt.
Endpoints
GET /webPOST /desearch/ai/search/links/webGET /web/extract
Example output
One full run with the defaults read up to 40 pages and cost $0.047, about 2 minutes 25 seconds. The outreach list shows each gap page with its title, URL, which other brands it names and which searches found it.
Run it yourself
The public examples folder linked above includes the script and a README. Install its requirements, set DESEARCH_API_KEY from console.desearch.ai, and use the run command in that README.
Useful flags: --max-pages (pages to read, default 40), --links-count (links per search, default 10), --redact (hide words before sharing a sample), --from-raw FILE (offline, no cost).
What it costs
A full run cost $0.047, summed from the X-Desearch-Cost-Usd headers. See pricing.
Ideas to extend it
- Re-run monthly; lists get updated.
- Sort gap pages by domain strength from your SEO tool.
- Pair it with the AI visibility tracker to pitch pages AI answers cite.
