Detect keyword cannibalization across blog posts by extracting primary keywords from titles and headings, clustering semantically similar targets, and flagging posts competing for the same search intent. Supports local-only mode (grep-based) and DataForSEO API mode (Page Intersection endpoint at ~$0.01/call). Outputs severity-scored report with merge or differentiate recommendations. Use when user says "cannibalization", "keyword overlap", "competing pages", "duplicate keywords", "cannibalize".
68
82%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Detect when multiple blog posts compete for the same search keywords. Two modes: local-only analysis (default) and DataForSEO API mode for SERP-level data.
| Mode | Flag | Cost | Data Source |
|---|---|---|---|
| Local | (default) | Free | File content analysis via Grep/Read |
| API | --api | ~$0.01/call | DataForSEO Page Intersection + Ranked Keywords |
Local mode works without any API keys. API mode requires DataForSEO credentials
set as environment variables: DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD.
Use Glob to find all content files in the target directory:
**/*.md, **/*.mdx, **/*.htmlnode_modules/, .git/, drafts/For each file, read and extract keyword signals from:
Primary keyword extraction method:
Group posts into clusters using these matching rules (in priority order):
For each cluster with 2+ posts, assess severity and generate a recommendation.
Display the results table and per-cluster recommendations.
Requires the --api flag and a dedicated local CLI wrapper that reads
DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD from the environment and emits
JSON. Do not use WebFetch for DataForSEO POST calls and never expose Basic auth
headers, login, password, or encoded credentials in prompts or reports. If no
wrapper exists in the project, report SKIPPED: DataForSEO wrapper unavailable
and run local mode.
Page Intersection - find keywords where multiple URLs rank:
POST https://api.dataforseo.com/v3/dataforseo_labs/google/page_intersection/live
{
"pages": {
"1": "https://example.com/post-a",
"2": "https://example.com/post-b"
},
"language_code": "en",
"location_code": 2840
}Cost: ~$0.01 per call. Returns overlapping keywords with position, volume, CPC.
Ranked Keywords - get all keywords a single URL ranks for:
POST https://api.dataforseo.com/v3/dataforseo_labs/google/ranked_keywords/live
{
"target": "https://example.com/post-a",
"language_code": "en",
"location_code": 2840
}The wrapper sends DataForSEO auth headers from environment variables and never prints them.
Four severity levels based on overlap signals:
| Level | Criteria | Action Urgency |
|---|---|---|
| Critical | Same exact keyword, both pages in top 20 | Immediate |
| High | Same keyword cluster, one page outranks the other | This week |
| Medium | Related keywords with partial SERP overlap | This month |
| Low | Semantic similarity but different confirmed intents | Monitor |
severity_score = overlap_count x avg_search_volume x (1 / position_gap)Where:
overlap_count = number of shared ranking keywordsavg_search_volume = mean monthly volume of shared keywordsposition_gap = absolute difference in average ranking position (min 1)Higher score = more urgent cannibalization problem.
Without SERP data, use a simplified scoring:
| Post A | Post B | Shared Keywords | Severity | Recommendation |
|--------|--------|-----------------|----------|----------------|
| /best-crm-tools | /top-crm-software | best crm, crm tools, crm software | Critical | MERGE |
| /email-tips | /email-marketing-guide | email marketing | High | DIFFERENTIATE |
| /seo-basics | /seo-for-beginners | seo basics, beginner seo | Critical | CANONICAL |
| /react-hooks | /react-state-mgmt | react, state | Low | NO ACTION |For each flagged cluster, provide:
Four possible actions for each cannibalization cluster:
When both pages are thin or cover the same intent with similar depth.
When pages serve different intents but keyword targeting overlaps.
When one post is clearly the authority and the other is a lesser duplicate.
rel="canonical" on the weaker page pointing to the authorityWhen a page should be removed from search results but still exist for users.
When intent is genuinely different despite surface-level keyword similarity.
aec971a
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.