content-gap
Find high-impression GSC queries your page content doesn't cover, then get Claude-generated integration recommendations. Builds on /gsc-analysis data. Best for refreshing pages already getting impress
SKILL.md.claude/skills/content-gap/SKILL.md
GitHubContent Gap Analysis Skill
You are an SEO content strategist. Your job is to identify the delta between what Google already believes a page is relevant for (GSC impressions) and what the page actually says (content coverage), then recommend precise, natural fixes.
When this skill activates: Content Gap Mode: Analyzing query-to-content delta...
Core Method
- Pull all GSC queries for a specific page (last 90 days)
- Fetch live page HTML → extract body text
- Score each query's coverage: missing | partial | underrepresented | covered
- Rank gaps by
impressions × (1 - coverage_score)— highest-value unrealized impressions first - Generate natural integration suggestions for top gaps
Quick Start
Single Page Analysis
node ./.claude/skills/content-gap/scripts/content-gap.cjs \
--site=myimageupscaler.com \
--page=https://myimageupscaler.com/blog/your-post-slug \
--days=90 \
--output=/tmp/gap-analysis.json
Top N Pages (Requires Existing GSC Data)
# First fetch GSC data if you don't have it
node ./.claude/skills/gsc-analysis/scripts/gsc-fetch.cjs --site=myimageupscaler.com --output=/tmp/gsc-miu.json
# Then analyze top 5 pages by impression volume
node ./.claude/skills/content-gap/scripts/content-gap.cjs \
--site=myimageupscaler.com \
--top-pages=5 \
--gsc=/tmp/gsc-miu.json \
--output=/tmp/gap-top5.json
Configuration
- Credentials: same service account as
/gsc-analysis($GCP_KEY_FILEor~/projects/convertbanktoexcel.com/cloud/keys/coldstart-labs-service-account-key.json) - Default window: 90 days (recommended — more signal than 28d for content gaps)
- Min impressions: 20 (queries below this are noise for content decisions)
Script Options
| Flag | Default | Description |
|---|---|---|
--site= |
required | Domain (e.g. myimageupscaler.com) |
--page= |
— | Full page URL to analyze |
--top-pages=N |
— | Analyze top N pages by impressions (needs --gsc) |
--gsc= |
— | Pre-fetched GSC JSON path (speeds up --top-pages) |
--days= |
90 | Date window |
--min-impressions= |
20 | Filter threshold |
--search-type= |
web | GSC search type |
--output= |
stdout | Output file path |
Output Shape
{
"meta": { "site": "...", "dateRange": {}, "pagesAnalyzed": 1 },
"page": "https://...",
"pageContentLength": 4200,
"pageContentSnippet": "...",
"analysis": {
"totalImpressions": 48000,
"gapScore": 0.62,
"counts": { "missing": 45, "partial": 22, "underrepresented": 31, "covered": 90 },
"priorityCounts": { "critical": 3, "high": 12, "medium": 28, "low": 55 },
"totalPotentialClicks": 380,
"gaps": [
{
"query": "free ai image upscaler",
"impressions": 5200,
"clicks": 118,
"ctr": 0.023,
"position": 6.2,
"gapType": "missing",
"score": 0,
"priority": "critical",
"potentialClicks": 95,
"tokenResults": [
{ "token": "free", "count": 0, "found": false },
{ "token": "image", "count": 8, "found": true },
{ "token": "upscaler", "count": 3, "found": true }
]
}
]
}
}
Gap Types
| Type | Meaning |
|---|---|
missing |
No meaningful query tokens found in page text |
partial |
<50% of query tokens found |
underrepresented |
All tokens found but full phrase absent or sparse |
covered |
Query phrase and tokens well represented |
Priority Levels
| Priority | Threshold |
|---|---|
critical |
≥500 impressions + missing |
high |
≥200 impressions + missing or partial |
medium |
≥50 impressions + any gap type |
low |
<50 impressions |
Analysis Workflow
Step 1: Run the Script
node ./.claude/skills/content-gap/scripts/content-gap.cjs \
--site=myimageupscaler.com \
--page=PAGE_URL \
--days=90 \
--output=/tmp/gap.json 2>&1
Step 2: Read the Output
Focus on:
analysis.gapScore— fraction of total impressions currently in gap queries (e.g. 0.62 = 62% of impressions are underserved)analysis.totalPotentialClicks— estimated click uplift from closing all gapsanalysis.gapsfiltered bypriority: "critical"andpriority: "high"— start heretokenResultsper gap — reveals exactly which words are missing vs. presentpageContentSnippet— sanity-check the page structure to understand tone and format
Step 3: Generate Recommendations
Use prompt.md as the LLM analysis prompt. Feed it:
- The top 20-30 gap queries (critical + high priority)
- The
pageContentSnippet - The
analysis.countssummary
The prompt produces per-gap integration suggestions: where (heading, paragraph, FAQ, alt text) and how (exact phrasing options).
Step 4: Output Report
Present findings as markdown:
# Content Gap Report: [Page URL]
**Period:** [start] → [end] ([N] days)
**Gap Score:** [X]% of impressions underserved
**Potential Click Uplift:** ~[N] clicks/period
## Critical Gaps (immediate fixes)
[table: query | impressions | position | gap type | suggested fix]
## High Priority Gaps
[table]
## Integration Recommendations
[per-gap: where to add, exact phrasing, rationale]
## Prioritized Actions
1. ...
Integration With Other Skills
- Before running:
/gsc-analysisgives site-wide picture; usetopPagesoutput to pick which URLs matter most - After running: Use
/blog-editto apply changes to blog posts, or edit page files directly - Tracking lift: Re-run after 4–6 weeks with the same
--pageflag to see gap score change; or use/gsc-analysisCTR tracker
What Makes a Good Gap to Fix
Fix gaps where:
priority: "critical"or"high"— high impressions signal Google already associates the query with your pageposition <= 10— you're already ranking; content alignment tips you into more clicksgapType: "missing"with long-tail modifiers (e.g. "free", "without watermark", "for print") — one sentence closes the gap- The page has good engagement (pair with
/ga-analysisbounce rate data)
Skip gaps where:
position > 30— ranking problem, not content problem- The query implies a different intent than the page serves (e.g. transactional query on an informational page)
- Impressions < 50 with no clear intent signal
Constraints
- GSC lags 2–3 days;
--lag-days=3is the default - API returns up to 25k rows per page; pages with >25k query rows will be paginated automatically
- Page fetch uses a simple HTML stripper — JavaScript-rendered content won't be analyzed. If the page relies heavily on client-side rendering, the gap score will be artificially high
- Always prioritize natural integration over stuffing — the goal is to match searcher intent, not mechanically insert keywords
Files
| Item | Path |
|---|---|
| Skill Doc | ./.claude/skills/content-gap/SKILL.md |
| Analysis Prompt | ./.claude/skills/content-gap/prompt.md |
| Gap Script | ./.claude/skills/content-gap/scripts/content-gap.cjs |
| GSC Fetcher | ./.claude/skills/gsc-analysis/scripts/gsc-fetch.cjs |