seo-growth-plan
Combined SEO action plan that fetches GSC + GA4 data, joins them by landing-page URL, and produces a ranked list of what to do next to grow organic traffic and conversions. Answers "wtf should I do SE
Files3
View on GitHubSKILL.md.claude/skills/seo-growth-plan/SKILL.md
GitHubSEO Growth Plan Skill
You are an SEO growth strategist who combines search demand data (GSC) with behavioral data (GA4) to produce a prioritized action plan. You don't just report metrics — you produce concrete, ranked actions with estimated conversion impact.
When this skill activates: SEO Growth Plan: Fetching and synthesizing data...
Quick Start
# Step 1: fetch both datasets in parallel
node ./.claude/skills/gsc-analysis/scripts/gsc-fetch.cjs --site=myimageupscaler.com --output=/tmp/gsc-miu.json &
node ./.claude/skills/ga-analysis/scripts/ga-fetch.cjs --site=myimageupscaler.com --output=/tmp/ga-miu.json &
wait
# Step 2: synthesize
node ./.claude/skills/seo-growth-plan/scripts/seo-synthesize.cjs \
--gsc=/tmp/gsc-miu.json \
--ga=/tmp/ga-miu.json \
--site=myimageupscaler.com \
--output=/tmp/seo-plan-miu.json
What Each Script Does
| Script | Purpose |
|---|---|
gsc-fetch.cjs |
Pulls GSC search analytics: queries, pages, CTR, rankings, cannibalization, indexing |
ga-fetch.cjs |
Pulls GA4 organic sessions, landing page conversions, engagement, device/country |
seo-synthesize.cjs |
Joins both datasets by URL and ranks actions by estimated conversion impact |
Configuration
Same service account for both APIs ($GCP_KEY_FILE or ~/projects/convertbanktoexcel.com/cloud/keys/coldstart-labs-service-account-key.json).
The service account needs:
- GSC: Full user (or Owner) on the property in Search Console
- GA4: Viewer in GA4 Admin → Property Access Management → property
519826120
What The Synthesis Produces
Priority Actions (top 15, ranked by estimated conversions recovered)
Each action includes:
type:conversion|engagement|ranking|content|consolidation|technicalpriority:high|medium|lowpage: the exact URL to act ontopQueries: GSC queries driving traffic thereevidence: GSC + GA metrics side by sideestimatedImpact.conversions: rough conversions unlocked if fixedsuggestedFix: what to actually do
Opportunity Clusters
| Cluster | Meaning |
|---|---|
conversionOpportunities |
Organic sessions ≥100 + conversion rate < 2% |
intentMismatch |
GSC CTR ≥ 2.5% + GA bounce ≥ 65% (users click but leave) |
strikingDistance.rankingOpportunities |
Position 8–25 + bounce ≤ 55% (safe to push rankings) |
strikingDistance.fixFirstOpportunities |
Position 8–25 + bounce ≥ 68% (fix page before pushing) |
correlatedDeclines |
GA sessions down ≥20% — with correlated GSC signal if available |
cannibalization |
Multiple GSC pages splitting same query intent |
trackingGaps.gscGhosts |
GSC shows clicks but GA shows zero sessions |
trackingGaps.gaGhosts |
GA shows organic sessions but GSC shows no impressions |
Summary Cross-Check
summary.crosscheck.gscClicksVsGaSessions.ratio should be between 0.6–1.6. Outside that range indicates a tracking issue worth investigating before trusting the data.
Output Shape
{
"meta": { "site": "...", "gscPeriod": {...}, "gaPeriod": {...}, "pagesWithBoth": 0 },
"summary": {
"gsc": { "clicks": 0, "impressions": 0, "avgCtr": 0, "avgPosition": 0 },
"ga": { "organicSessions": 0, "organicConversions": 0, "organicShare": {...} },
"crosscheck": { "gscClicksVsGaSessions": { "ratio": 1.0, "looksNormal": true } }
},
"priorityActions": [],
"opportunities": {
"conversionOpportunities": [],
"intentMismatch": [],
"strikingDistance": { "rankingOpportunities": [], "fixFirstOpportunities": [] },
"correlatedDeclines": [],
"cannibalization": [],
"trackingGaps": { "gscGhosts": [], "gaGhosts": [] }
},
"joinedPagesTop": []
}
Analysis Workflow
Step 1: Run All Three Scripts
node ./.claude/skills/gsc-analysis/scripts/gsc-fetch.cjs --site=myimageupscaler.com --days=28 --output=/tmp/gsc-miu.json 2>&1
node ./.claude/skills/ga-analysis/scripts/ga-fetch.cjs --site=myimageupscaler.com --days=28 --output=/tmp/ga-miu.json 2>&1
node ./.claude/skills/seo-growth-plan/scripts/seo-synthesize.cjs \
--gsc=/tmp/gsc-miu.json --ga=/tmp/ga-miu.json \
--site=myimageupscaler.com --output=/tmp/seo-plan-miu.json 2>&1
Step 2: Read The Plan JSON
Work through in this order:
- In the source GSC JSON, read
comparison.brandSplit, itsunclassifiedprivacy-suppressed segment,quarantinedQueries, andcomparison.stableCohortPosition; lead with classified non-brand clicks and separate branded, unclassified, and quarantined movement. summary.crosscheck— verify tracking is sane before reading anything else.summary.gsc+summary.ga— headline performance and organic share.priorityActions— ranked actions, followed by conversion, intent, ranking, decline, cannibalization, and tracking-gap clusters.
Do not convert falling impressions at position 1.0 into a metadata recommendation; that is branded demand movement, not a ranking decline.
Step 3: Produce The Report
See the prompt.md output format.
Troubleshooting
Low pagesWithBoth
If fewer than 30% of pages have both GSC + GA data, check:
- URL normalization: GSC uses full URLs, GA uses paths. The synthesizer strips protocol+host automatically.
- Locale prefixes:
/en/pagevs/pageare different keys. Don't collapse them. - Query strings: synthesizer strips them. If pages differ only by
?utm=Xthey'll merge.
GSC/GA click ratio way off
ratio < 0.6: GA is under-counting organic sessions. Check consent mode blocking GA, missing tag on some pages, or redirect chains breaking session attribution.
ratio > 1.6: GSC over-counting, or GA is inflating organic sessions. Check if GA4 channel grouping rule for "Organic Search" captures more than just web search.
Files
| Item | Path |
|---|---|
| Skill Doc | ./.claude/skills/seo-growth-plan/SKILL.md |
| Prompt | ./.claude/skills/seo-growth-plan/prompt.md |
| Synthesis Script | ./.claude/skills/seo-growth-plan/scripts/seo-synthesize.cjs |
| GSC Fetch Script | ./.claude/skills/gsc-analysis/scripts/gsc-fetch.cjs |
| GA Fetch Script | ./.claude/skills/ga-analysis/scripts/ga-fetch.cjs |