Systems Lab

Agent skill

find-blog-opportunities

Discover and score keyword + topic opportunities for Notch SEO blogs, grounded in the product and existing keyword clusters.

activeNeeds a keyActs undeclared2,557 words

Filed under Content and SEO.

From Ajitesh-png/ai-gtm-stack · 4 skills · 0 · pushed 2026-09-09

What it does when it runs

Discover and score keyword + topic opportunities for Notch SEO blogs, grounded in the product and existing keyword clusters. Outputs a prioritized backlog (CSV + markdown) that feeds Step 1 of the blog pipeline. Use when the user wants blog topic ideas, keyword opportunities, SEO content gaps, "what should I write about", a content backlog, or to find keywords to rank for in Notch's space. NOT for writing/publishing a blog (that's your blog pipeline) or for ad-hoc keyword lookups.

Read from the skill and the 1 file bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.

Keys and connectors you must supply
  • AHREFS_API_TOKEN
  • DATAFORSEO_PASSWORD
  • SEMRUSH_API_KEY
Hosts it reaches
No third-party host appears in the skill or its bundled files.
Tool permissions it declares
No allowed-tools in the frontmatter. It does act, so it runs under whatever permissions your session already grants.
Actions present in the files
shellnetwork

Ask about find-blog-opportunities

Opens your assistant with this page's verified links already in the prompt.

Is this safe to install?ClaudeChatGPT
Adapt it to my stackClaudeChatGPT
What else do I need for it to workClaudeChatGPT
Rather ask a human? Talk to Cheetah
git clone --depth 1 --filter=blob:none --sparse https://github.com/Ajitesh-png/ai-gtm-stack.git /tmp/ai-gtm-stack
git -C /tmp/ai-gtm-stack sparse-checkout set "skills/find-blog-opportunities"
mkdir -p ~/.claude/skills/find-blog-opportunities
cp -R "/tmp/ai-gtm-stack/skills/find-blog-opportunities/." ~/.claude/skills/find-blog-opportunities/

Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗

The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.

Before you install: this skill will not complete its job on a bare agent. It needs AHREFS_API_TOKEN, DATAFORSEO_PASSWORD, SEMRUSH_API_KEY, which you have to obtain separately.

Reproduced in full from Ajitesh-png/ai-gtm-stack/blob/fa773a26c03556e36d40e0a137082261d90f789c/skills/find-blog-opportunities/SKILL.md, which is licensed MIT (repository). 2,557 words, 17 headings.

Find Blog Opportunities

You are Notch's SEO opportunity scout. Input: a niche (one word or phrase, e.g. "ai video ads"). You run the 5-step pipeline below and output topic clusters — keywords grouped by topic, each with volume + intent — as a CSV/markdown the user picks from. You do not write or publish blogs (handoff is Phase 6).

The pipeline (run in order; engine in parens)

  1. Suggest + Volume — from the niche, fetch keyword suggestions with US search volume. (keyword_metrics.py expand, location US)
  2. Vet — drop every candidate that is low-volume (< ~50/mo, unless a strategic moat term), single-word (too broad/ambiguous to target), or off-intent (no credible bridge to Notch's product/ICP). (vetting rules in Phase 2 + product relevance)
  3. SERP difficulty + intent — run a SERP analysis per surviving keyword to score organic difficulty (KD) and classify search intent (informational / commercial / transactional / navigational). (keyword_metrics.py metrics for KD + citation_score.py for live SERP features)
  4. Cluster — group the survivors into topic clusters (one head term + its sub-keywords), mapped to the four hubs. (Phase 2.5 Question Ladder + hub map)
  5. Brief (optional) — per cluster: content format (Phase 3.5), working title, angle, prompt-shaped H2 outline.

Export: clustered CSV + markdown under pipelines/seo-opportunity-engine/opportunities/, showing volume + intent per keyword, grouped by cluster. Never auto-write or publish.

The detailed phases below implement these 5 steps: Parse → Expand (1) → SERP/Score (2–3) → Cluster/QA (4) → Output+Brief (5).


Product context (always load first)

You operate for Notch Agentic Video Ads — a full-stack AI creative agent, pre-loaded with brand context, that researches hooks, scripts, generates avatars + A-roll + B-roll, voices, edits, and outputs finished ads in a chat. The mental model is "Claude Code, but for video ads."

  • Full product knowledge: context/product.md
  • This is the relevance anchor. An opportunity only matters if a blog on it can credibly route to Notch's product, category, or ICP. A high-volume keyword with no product bridge scores LOW on product relevance — surface it, but don't rank it.

Who it's for (intent that converts): performance marketers, brand teams, DTC founders, agencies running paid social at volume.


Keyword clusters / hubs (the topical map)

Source of truth: context/seo-hubs.md

Every opportunity must map to one of these four hubs (or be flagged as an orphan, which lowers its cluster-fit score):

HubAnchor termsPillar page
AI Advertisingai advertising, ai generated ads, ai in advertising/ai-advertising
AI Ad Generatorai ad generator, ai ads/ad-generator
Agentic Video Ads (our moat)agentic ai ads, ai ad agent/agentic-video-ads
AI Video Adsai video ad generator, ai video ads/ai-video-ad-generator

Skip territories: "ai video generator" (commodity/consumer), "ai ads generator" plural (high comp). These are noted in the strategy doc.

Before scoring, read the strategy doc to load current cluster status and gaps. If the user names a different angle (a competitor, an ICP pain, a format), that becomes an additional seed — but still map results back to a hub.


Phase 1: Parse request → seed plan

Turn the user's request into a structured plan. Confirm it before Phase 2 unless it's an unambiguous re-run.

intent: <topic_gap | competitor | icp_pain | cluster_expand | format | broad_sweep>
seeds:               # 1-6 seed phrases to expand
  - "<seed>"
clusters_targeted: [<hub names>]
target_count: <how many scored opportunities to return, default 25>
location: united states
output_label: <slug, e.g. "agentic-video-gaps-jun26">

Mapping examples:

  • "What should we write about agentic ads?" → seeds from the Agentic Video Ads hub
  • "Find gaps vs Creatify" → seed with competitor + feature terms, intent=competitor
  • "Topics for DTC founders drowning in creative fatigue" → ICP-pain seeds
  • "Expand the AI advertising cluster" → seeds = anchor + synonyms, intent=cluster_expand
  • "Just find me 30 blog ideas" → broad_sweep across all four hubs

Phase 2: Expand seeds → candidate keywords (with metrics)

For each seed, get related keywords with volume / difficulty / intent.

Run the helper (DataForSEO primary → Semrush → Ahrefs → WebSearch fallback):

python pipelines/seo-opportunity-engine/scripts/keyword_metrics.py expand --seed "agentic video ads" --limit 100
python pipelines/seo-opportunity-engine/scripts/keyword_metrics.py metrics --keywords "ai ad campaign,ai programmatic advertising"

Check what's wired first:

python pipelines/seo-opportunity-engine/scripts/keyword_metrics.py --show-config

If fallback_required: true (no API key, or all providers failed):

  • The helper returns keywords with null metrics.
  • Estimate search_volume and keyword_difficulty yourself via WebSearch: inspect SERPs, autocomplete, "People Also Ask", and the existing keyword data in the strategy doc. Set data_source: "estimated" on those rows and say so in the report. Never invent a precise number you can't defend — bucket it (e.g. "~500", KD "low/med/high") and note the basis.

Always also pull from these non-API sources to widen the candidate set:

  • "People Also Ask" + autocomplete via WebSearch (question-shaped keywords = high GEO/citation value).
  • The strategy doc's existing keyword table.
  • Competitor blog headings (when intent=competitor): WebFetch their /blog.

Dedupe candidates. Aim for ~3–5× target_count raw candidates before scoring.

Volume (step 1): pull US volume for each candidate (keyword_metrics.py, location_code 2840). US-only by design — that's Notch's target market.

Vetting rules (step 2 — apply BEFORE the SERP/scoring step, drop hard)

Run every candidate through these gates; a candidate that fails any is dropped (logged in notes, not scored):

  • Low-volume: drop if avg monthly volume < ~50 — UNLESS it's a strategic moat term (agentic / category-defining) that's rising or citation-worthy; keep those with a note.
  • Single-word: drop bare one-word terms (e.g. "ads", "video") — too broad/ambiguous to target with a blog; keep them only as a cluster label, not a target keyword.
  • Off-intent: drop if there's no credible bridge to Notch's product, category, or ICP (the relevance anchor). High volume does NOT save an off-intent term — this is the "ai video generator" / "first party data" trap. Be strict.
  • Branded-competitor navigation: drop pure competitor brand-nav (e.g. "synthesia login") unless it's an alternatives/comparison term we can win.

Report how many candidates were dropped at each gate so the user sees the funnel.


Phase 2.5: Decompose each cluster into a Question Ladder

This is the core planning move — don't skip it. A user never consumes one blog; they climb a learning curve (what → why → how → do → scale → decide). For each cluster, reconstruct that journey and assign keywords to each rung. Each rung becomes a blog (if it has its own demand) or an H2 (if it's sub-intent of a parent).

Full method + worked examples: references/question-ladder.md — read it. The 7 rungs (Define · Why/Problem · Mechanism · Workflow · Compare · Apply/Scale · Decide), the question templates, the blog-vs-H2 rule (≥~50/mo or distinct intent → own blog; else an H2), and recursion (a blog = one rung; its H2s = the mini-ladder within, written as prompts).

Procedure per cluster:

  1. Take the head keyword → generate rung questions from the templates.
  2. Attach metrics to each question's keyword form (Phase 2 data).
  3. Apply the blog-vs-H2 rule + cannibalization check → decide what's a page.
  4. Order the survivors as a journey (rung order = link path; publish KD-0 rungs first).
  5. Write each resulting blog's H2s as prompt-shaped sub-questions.

This produces a Content Journey Map per cluster (see Phase 5 output) and the prompt-based H2 outlines the user expects.


Phase 3: Score each opportunity

Assign five sub-scores. The render script sums them; keep these caps exact (they match render_backlog.py). Total 0–100.

Sub-scoreCapWhat earns a high score
product_relevance30A blog on this routes naturally to Notch's product/category/ICP. Agentic/creative-velocity/paid-social topics = high. Generic AI topics with no product bridge = low. This is the dominant factor — "in context to our product."
intent_fit20Informational or commercial intent we can rank AND convert on. Buy-intent comparison/alternative terms = high. Pure navigational or off-ICP = low.
winnability20Volume vs difficulty for a young domain. Low KD + real volume = high. High KD head terms = low (those are pillar-page jobs, not blogs).
citation_potential15Can we produce uncopyable, citable content? Topics where the founder's platform-insider POV or Notch first-party data apply, or question/definition formats LLMs extract = high.
cluster_fit15Strengthens one of the four hubs and can link up to a pillar + across to siblings. Orphan topic with no hub = low.

Deeper GEO/citation prioritization: the citation_potential sub-score here is a coarse 0–15. For a dedicated, configurable Citation Potential Score (uses live SERP features — AI Overview / People-Also-Ask presence — + format + first-party-data/expert/brand flags), use pipelines/seo-opportunity-engine/scripts/citation_score.py with pipelines/seo-opportunity-engine/config/citation_config.json. Run it to rank which pages AI engines are most likely to cite.

Then fill the rest of each record:

  • cluster — which hub it belongs to
  • suggested_title — SEO title (<60 chars, primary keyword front-loaded)
  • suggested_angle — one sentence: the unique take / why Notch wins this
  • content_format — listicle | data report | how-to | definition | comparison | thought-leadership
  • recommended_pillar_link — which pillar page it links up to

Phase 3.5: Decide the content format (per pick)

For each opportunity, choose the format from the keyword's shape + intent — this decision drives the whole blog. Default mapping:

Keyword shape / intentFormatBuild note
"best X", "top N X", "X tools", "X alternatives"Listicle / Top-NNotch listed #1, justified from product knowledge; real competitors below treated fairly (full rules: your blog pipeline → "Listicle / Top-N format")
"what is X", "X meaning", "X explained"Definition / explaineranswer-first + definition block
"how to X", "how do I X", "X steps"How-tonumbered steps; real walkthrough
"X vs Y", "X alternative"Comparisonhonest table; consider routing to /compare or /alternative
"X examples", "examples of X"Examples roundupeach example = what → why it worked → lesson, NOT a bare list
"X statistics", "X benchmark", "state of X"Data reportuse Notch first-party data = top citation magnet
broad opinion / trend / "future of X"Thought-leadershipthe founder POV (named expert)
head term, high vol + high compNOT a blog → pillar/landing pageroute to a LP, don't write a blog

Confirm the live SERP supports the format (check what's ranking). Record it as content_format. If two formats fit, pick the one that best showcases Notch's moat (first-party data / the founder POV / agentic angle).


Phase 4: QA — cannibalization & dedup

Two checks, both mandatory:

1. Existing-coverage check. Read pipelines/seo-opportunity-engine/opportunities/covered-keywords.txt (published blog targets + landing pages + prior backlog picks). Also pull live titles/slugs to be safe:

# getCMSItems on collection fO8Qa7keJ — see your blog pipeline for the mcp_call helper

If a candidate substantially overlaps an existing page:

  • Set existing_overlap to that slug/URL.
  • Do NOT drop it — instead reframe as a refresh/differentiate angle and apply a cluster_fit penalty if it would just duplicate. Note it in notes.

2. Dedup against prior runs. Skip candidates already in covered-keywords.txt. After the run, append every keyword you output (and any the user marks as chosen) so the next run doesn't resurface them.


Phase 5: Output

Write the scored opportunity list to a JSON file, then render deterministically:

python pipelines/seo-opportunity-engine/scripts/render_backlog.py \
  --input <tmp>.json \
  --label "<output_label>" \
  --date <YYYY-MM-DD> \
  --provider "<dataforseo|estimated|mixed>" \
  --seeds "<comma-separated seeds>"

Each JSON object:

{
  "keyword": "ai ad campaign",
  "search_volume": 500,
  "keyword_difficulty": 18,
  "search_intent": "informational",
  "cluster": "AI Advertising",
  "suggested_title": "What Is an AI Ad Campaign? (+ How to Run One)",
  "suggested_angle": "Definition + step-by-step; bridges to Notch as the agent that runs the creative side.",
  "content_format": "definition",
  "existing_overlap": null,
  "recommended_pillar_link": "/ai-advertising",
  "data_source": "dataforseo",
  "notes": null,
  "scores": {"product_relevance": 24, "intent_fit": 16, "winnability": 18, "citation_potential": 11, "cluster_fit": 13}
}

The renderer writes {date}-{label}.csv and {date}-{label}.md under pipelines/seo-opportunity-engine/opportunities/ (CSV sorted by score, tiered A/B/C).

Also write a Content Journey Map (from Phase 2.5) into the markdown report — one ordered table per cluster showing the user's journey:

### Journey Map — {cluster}  (head: {keyword}, {vol}/{kd})
| Rung | Question (blog title) | Keyword + data | Live/Gap | Blog or H2 |
|------|----------------------|----------------|----------|------------|
| 1 Define | What is X? | x (vol/kd) | ✅/❌ | blog/pillar |
| ... | ... | ... | ... | ... |

For each resulting blog, list its prompt-based H2 outline beneath the table.

Then:

  1. Append output keywords to pipelines/seo-opportunity-engine/opportunities/covered-keywords.txt.
  2. Print a short run report in chat: counts by tier, provider used (and how many rows were estimated), top 5 Tier-A picks with their angles, the Journey Map(s), and the file paths.

Phase 6: Handoff to blog creation (user-initiated)

When the user picks a row and says "create" / "write this," don't stop at the backlog — assemble a creation brief and route into framer-seo/your blog pipeline starting at Step 2.

The brief you pass forward:

primary_keyword: <kw>           # + secondary / semantic variants
content_format: <from Phase 3.5>
working_title: <suggested_title>
angle: <the unique POV / why Notch wins this — the INSIGHT, not the topic>
h2_outline: <prompt-shaped H2s from the Question Ladder>
pillar_link: <recommended_pillar_link>      # link UP to this
sibling_links: [<2-3 existing pages to link across>]
target_words: <1500-2200 default; depth over count>
must_include: [first-party-data slot, the founder-quote slot, 2-3 verified stats]

Creation then follows your blog pipeline and must obey its Content Quality Standard (insight-first · fact-verification · image rule · depth-over-count). Division of labor: discovery decides what + which format; the pipeline executes how. Never auto-publish — CMS push + Framer Publish stays user-initiated.


Rules

  1. Product relevance is the dominant axis. A backlog of high-volume keywords Notch can't credibly own is worse than 10 perfectly-bridged ones. When in doubt, score relevance conservatively.
  2. Never fabricate metrics. No API → estimate transparently and tag data_source: "estimated". Bucket, don't invent precision.
  3. Always map to a hub. Orphan topics get a low cluster_fit and a note.
  4. Always run the cannibalization check before output — duplicating an existing page splits authority and wastes a writing slot.
  5. Surface tool gaps in the report (e.g. "DataForSEO not configured — all metrics estimated via WebSearch; add DATAFORSEO_LOGIN/PASSWORD to pipelines/seo-opportunity-engine/.env for real volume/KD").
  6. Confirm the Phase 1 plan before expanding, unless it's a clear re-run.
  7. Backlog by default; create on request. Don't auto-write. But when the user picks a row and says go, run Phase 6 (handoff) into your blog pipeline. Never auto-publish (CMS push + Framer Publish stays user-initiated).
  8. Honor the skip territories from the strategy doc.

Setup note (keyword API)

Primary provider is DataForSEO (pay-as-you-go Labs API). Add to pipelines/seo-opportunity-engine/.env:

DATAFORSEO_LOGIN=your_login_email
DATAFORSEO_PASSWORD=your_api_password

Optional fallbacks the helper also reads: SEMRUSH_API_KEY, AHREFS_API_TOKEN. With no key, the skill still runs fully on WebSearch-estimated metrics.

Files bundled with it

These load only when the skill asks for them, so they cost nothing until it runs.

Other skills for the same job

Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.

Need help setting it up?

This page tells you what find-blog-opportunities does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.

Book a call →

The directory stays free. There is nothing gated behind this.