How to automate getting mentioned in third-party listicles
A simple workflow to get your brand into more third-party listicles.
There’s been a lot written about the value of prompt tracking and its various flaws. I won’t weigh in too heavily on the efficacy, but I have seen mention rates differ by about 80% between two leading platforms — Peec and AirOps — for the same brand. Not exactly something that instills great confidence.
Either way, I’ve stopped treating these tools as dashboards and started treating them as a CRM import file.
A lot of AEO advice makes it sound like becoming the best-known brand in your category is something you simply forgot to do between breakfast and lunch.
So how do you do it as a smaller startup? The glib answer is: do a million things well. But one of them you can set up in a single morning — a simple citation outreach workflow that gets you listed, hopefully, in the articles already being cited by the all-powerful models that determine your fate, somewhere between the coffee and the daily pang of existential dread about whether cricket farming or tilapia farming is the safer career in the age of AI.
This workflow could be applied to broken backlinks, pitching website’s on new data studies your brand launches, and a hundred other things.
First, the results
I’ve been running this for about six weeks, and it’s netted 7 inclusions so far. The emails achieve about a 10% conversion rate, which seems reasonably strong. (I do expect it to decline over time as the easier targets bite first.)
What you need to run this
On top of Claude Code or Codex, for the most basic version of this to run you need:
Peec Starter $95/mo
Apollo Free $0
Notion + Gmail + Slack $0
Node scripts $0
─────────────────────────────────────────
$95/moThis also works with AirOps, but their pricing is a lot less startup friendly.
A caveat: this is the logic and the prompts, not a copy-paste repo. The two Node scripts are a few hundred lines each and tuned to my category, so I’ve described what they do rather than pasting them. Hand any section here to Claude or ChatGPT and it’ll write you the script… that’s how mine got written. 😅
The architecture
This flow consists of several cloud routines that run on a weekly basis. I considered writing a post about setting up Claude Code and cloud-based routines, but there are tons of helpful resources out there (here’s a 19-minute video on the subject) and Codex/Claude will also walk you through it.
The workflow is as follows:
[Discovery: 3 sources] → [Dedupe vs cache] → [Verify on the LIVE page]
→ [Score by leverage] → [CRM row] → [Draft — human sends] → [Closed loop]I made the decision early-on to separate the two agents.
Radar (weekly + a daily fresh-listicle pass): this is discovery only. This finds and verifies targets and writes Notion rows. No contact database and no mailbox access. Those connectors are switched off on this agent.
Drafter (weekly): picks up rows marked ready, finds the human, writes the pitch, leaves it as a draft in a Gmail account.
Source 1: AI prompt tracking tool
Start here, because it needs no scripts at all. Your AI-visibility tool's MCP can provide the data. The agent just asks it two questions in order.
PRIMARY SOURCE — [AI-VISIBILITY TOOL], project “[COMPANY]” ([PROJECT_ID]):
STEP 1 — Gap domains. get_domain_report, last 90 days, filters
domain_classification in [Editorial, Reference], having gap >= 2 ([COMPANY] absent
but >= 2 competitor brands present), order_by citation_rate desc, limit 50. Then run
a second pass restricted to the ICP topics ([TOPIC A], [TOPIC B] — resolve topic ids
via list_topics) with gap >= 1: lower volume, highest buyer fit. Ignore
academic/irrelevant domains (nih.gov, sciencedirect, springer, *.edu).
STEP 2 — Exact pages. Top ~15 domains: get_url_report per domain, filter
url_classification in [Listicle, Comparison, Alternative]. Skip competitor-owned and
our own domains. Note each page’s retrieval_count and mentioned_brand_ids.Note: Your tracked-prompt list is the radar’s aim. The scan can only ever surface domains that get cited for prompts you’re actually tracking. So if you point it at the wrong questions, it will diligently find you the wrong pages. Make sure queries map to buying-intent and are likely to surface multiple brands.
Source 2: The backlink link-gap
I use Ahref’s API for this step. (A good example where Claude can write you a script to pull the key information.)
The logic is simple subtraction. I pull each competitor’s backlinks with aggregation: 1_per_domain, pull my own, subtract mine out, and keep whatever links to two or more of them. Write it to CSV sorted by competitor count first and domain rating second, then send the URLs straight into verification.
Source 3: Firehose API
This is useful for finding articles that have just been published. These have been some of the most receptive recipients of outreach. (Its API works very nicely with your LLM of choice.)
const LISTICLE_SIGNALS = [
"best ", "top ", " vs ", "vs.", "alternative", "alternatives", "comparison",
"compare", "roundup", "tools for", "software for", "platforms for",
"options for", "solutions for", "list of", "reviewed", "buyer", "guide to",
];
const CATEGORY_TERMS = [
INSERT YOUR TERMS
];
const isListicle = LISTICLE_SIGNALS.some(s => hay.includes(s));
const inCategory = CATEGORY_TERMS.some(t => hay.includes(t));
return isListicle && inCategory; // BOTH, not eitherCheck and score the list
The next step is to vet the URLs you’ve pulled to ensure they’re the right ones for you to target.
You find CITATION OPPORTUNITIES for [COMPANY] ([DOMAIN]), a [CATEGORY] company.
The lever is being listed in the third-party roundups, listicles, and comparison articles that AI engines
cite. Your job: from web pages just published, keep only the ones where [COMPANY]
could realistically be pitched for INCLUSION.
KEEP if the page is a third-party listicle / roundup / "best X tools" / "top software" /
"[competitor] alternatives" / comparison article in the [CATEGORY] space — i.e. a page
that lists multiple vendors and a writer or editor could add [COMPANY] to.
SKIP if: a single-vendor product/pricing page; a competitor's OWN domain; a job posting;
generic industry news with no vendor list; off-topic; or low-quality auto-generated spam.
For each item return: keep (bool), topic (short), tools_listed (array of vendor names
you can see), company_listed (bool — are we already in it), authority ("high"/"medium"/
"low" guess from domain + writing), pitch_angle (one line: why we fit this list), reason.
Return ONLY valid JSON, no fences:
{"results":[{"index":0,"keep":true,"topic":"...","tools_listed":["..."],
"company_listed":false,"authority":"medium","pitch_angle":"...","reason":"..."}]}The next step is to rank the pages.
live_score = retrievals × competitors_on_the_live_page
Boost: page frames the category the way your ICP does
fresh last_updated
stale vendors present (hook)
submission route found
Demote: pay-to-play directories
wire-service aggregatorsTaken together it looks like this, running on a schedule with three connectors: the AI-visibility tool, the CRM, and Slack.
You find where [COMPANY] ([DOMAIN]) should get CITED to improve AEO visibility.
DISCOVERY ONLY: you write verified targets to the Outreach CRM. The outreach-drafter
routine handles contacts and emails. Never draft or send email here.
STEP 1 — Gap domains. Pull the domain report, last 90 days, filters
domain_classification in [Editorial, Reference], having gap >= 2 (we are absent but
>=2 competitor brands are present), order by citation_rate desc, limit 50. Run a
second pass restricted to the ICP topics with gap >= 1 — lower volume, highest buyer
fit. Ignore academic/irrelevant domains (nih.gov, sciencedirect, springer, *.edu).
STEP 2 — Exact pages. For the top ~15 domains, pull the URL report, filter
url_classification in [Listicle, Comparison, Alternative]. Skip competitor-owned and
our own domains. Note each page's retrieval_count and mentioned_brand_ids.
STEP 3 — SECOND SOURCE (backlink link-gap):
node scripts/ahrefs-linkgap.mjs --limit 500 > /tmp/linkgap.csv
Take the top ~25 pages (>=2 competitors linked). Skip cleanly if the key is unset.
STEP 4 — THIRD SOURCE (fresh listicles):
node firehose/citation-radar.mjs --since 24h
Take every keeper. These are hours old — they will have NO citation history, which is
expected. Do not drop them for zero retrievals; floor the score at 1.
STEP 5 — Dedupe BEFORE verifying (cheap first): check every candidate domain against
data/crm-domains.csv. Only for cache misses, double-check the CRM by domain. Work
only NEW domains/URLs.
STEP 6 — VERIFY against the live page:
node scripts/verify-targets.mjs --json candidates.json
Keep only verdict=LIST_CONFIRMED. Drop NOT_A_LIST (advice articles — the big-publisher
trap). If we're already present, record it as a WIN in the run log, not a target.
BLOCKED pages: retry once via WebFetch and judge manually; if EVERYTHING is BLOCKED,
it's the egress policy, not dead targets — stop and report.
STEP 7 — Score + tier. live_score = max(retrievals,1) x competitors_on_page. Boost
ICP-framed pages, fresh last_updated, stale vendors present, submission route found.
Demote pay-to-play directories and wire aggregators. Rank the batch.
STEP 8 — Write each verified NEW target to the CRM: Name (domain), Target Page,
Outlet Type, # competitors present, AI Citations (90d), Pitch Angle (the script's
hook_hints + why we belong on this specific page; ground every claim in
brand/positioning.md), Status = "Ready to contact", Source ([AI tool]/ahrefs/firehose),
Notes = leverage math + run date. Then APPEND the same domains to
data/crm-domains.csv and commit + push.
STEP 9 — CLOSED LOOP (first run of each month): pull CRM rows with Status = Added.
For each, pull the URL report for that exact URL, last 30 days: does mentioned_brand_ids
now include us? Write the verdict into Notes (cited-after-add / added-but-not-cited-yet)
and summarize conversion rate BY OUTLET TYPE in the Slack digest.
STEP 10 — Post the top 10 to Slack: target page, competitors on it, live-verified hook,
pitch angle. Include how many candidates each source contributed, how many died in
verification, and the monthly conversion report when STEP 9 ran.
HARD RULES: discovery only — no contact lookups, no mailbox, no sending. Never log an
unverified page as a target. Never claim [PRODUCT GAP A] or [PRODUCT GAP B] — those are
gaps, not features. Commit the crm-domains.csv update every run.Contacts and drafts
As mentioned, generating outreach is a separate routine with separate connectors: contact database (Apollo), mailbox (Gmail), CRM (Notion).
Since the volume is relatively low and all emails are reviewed by me, I simply have Claude write the drafts to Gmail directly, where I do a light edit and send on a weekly basis.
You are the outreach drafter. Pull CRM rows with Status = "Ready to contact", highest
live_score first, firehose-sourced rows first within that, max 12 per run.
1. FIND THE HUMAN. Use the page's byline first. Search the contact DB by DOMAIN in
batches of 10 — domain search is free; NEVER bulk-match individual people, that
burns a credit each. Save the person_id on the CRM row; reveal the email only at
draft time (1 credit). No contact found → mark channel = LinkedIn or web-form and
move on. Do not guess email patterns.
2. VERIFY THE HOOK ON THE PAGE. Before writing a line, re-fetch the target page and
confirm the hook is really there — the dead vendor, the stale date, the competitor
slot. If it's been fixed since discovery, pick a new hook or drop the row. Never
write a pitch from cached data.
3. WRITE THE DRAFT (template below). Sender = the founder for founder-to-founder
targets, or the regionally-matched teammate for regional outlets.
4. LEAVE IT AS A DRAFT. Never send. Post a Slack summary listing each draft, its
recipient, and the verified hook, so a human can review and send in one sitting.
HARD RULES: never offer money to an editorial listicle or any journalist. Never invent
an on-page detail. Every product claim must trace to positioning.md. LinkedIn connection
notes cap at 300 characters — count them.The pitch template:
Subject: [the specific thing that's wrong on their page]
Hi [name] —
[ONE verified, specific on-page detail. "Your best-X roundup still lists [defunct
vendor], which shut down in [year]." / "The list was last updated [date] and
[competitor] has since [changed thing]."]
[ONE sentence: the gap you fill on that specific page, in their framing, not yours.]
[SOMETHING YOU GIVE: a mention from your own cited guides, an integration, first-party
data, a startup deal — pick per segment.]
Happy to send a 40-word blurb in your format if it's useful.
[Sender]Let me know how you get on
I like this use case because it’s a repetitive research task with a clear definition of done. Pull the report, check the page is really a list, count who’s on it, find the byline. This is exactly the kind of work that is tedious and never gets done manually the minute more pressing stuff comes up and that an agent does happily.
The competitor list, the category terms, the prompt universe, the blocklists… all of that is specific to a category, and I got a lot of it wrong on the first pass. Yours will be wrong somewhere elses so be patient as you play around with it.


