From seo-skills
Analyzes a single URL's SEO performance: rankings, traffic, position history, SERP context, and AI Search citation status. Produces a keep/refresh/consolidate/kill verdict.
How this skill is triggered — by the user, by Claude, or both
Slash command
/seo-skills:seo-pageThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Example output: [examples/seo-page-notion-keyboard-shortcuts-20260514/PAGE.md](../../examples/seo-page-notion-keyboard-shortcuts-20260514/PAGE.md)
Example output: examples/seo-page-notion-keyboard-shortcuts-20260514/PAGE.md
Show what a single URL ranks for, what traffic it captures, where its weak and strong points are, and what to do about it. The deliverable is an opinionated verdict — KEEP, REFRESH, CONSOLIDATE, or KILL — anchored in objective signals from SE Ranking's URL-level data.
WebFetch tool available (for the page-level HTML sense-check).us), (c) primary topical keyword (auto-inferred from <title> + <h1> if not supplied).Validate target & preflight. See skills/seo-firecrawl/references/preflight.md for the canonical 3-stage preflight (credit balance, Firecrawl availability, Google APIs). Skill-specific notes:
og:*, twitter:*, canonical, robots meta, JSON-LD types, and hreflang count from raw <head> — WebFetch returns markdown only and strips those fields. Without Firecrawl, the affected lines in PAGE.md emit (skipped — Firecrawl not installed). Pass --no-firecrawl to treat Firecrawl as unavailable even when installed (credit conservation).skills/seo-google/references/cross-skill-integration.md § "seo-page" for the full recipe.URL-level overview DATA_getUrlOverviewWorldwide
Ranking keywords DATA_getDomainKeywords (use the url param for exact match — not filter_url)
volume × CTR-by-position (use a standard CTR curve: 1=28%, 2=15%, 3=11%, 4=8%, 5=7%, 6=5%, 7=4%, 8=3%, 9=2%, 10=2%, 11+=1%).Page authority DATA_getPageAuthority + DATA_getPageAuthorityHistory
4b. GSC URL performance + URL Inspection (only if google-api.json is present, tier ≥ 1)
python3 scripts/gsc_query.py --property "{config.default_property}" --url "{target_url}" --days 28 --jsonpython3 scripts/gsc_inspect.py "{target_url}" --site-url "{config.default_property}" --jsonPAGE.md "## Snapshot": GSC last 28d: {clicks}/{impressions}/{ctr}% CTR / pos {position} and Google sees: {INDEXED|EXCLUDED} · canonical {userCanonical} → {googleCanonical} · last crawled {date}.INDEXED + impressions > 100 + position 4–10 → harden REFRESH (clear quick-win). EXCLUDED (any reason) → harden KILL or CONSOLIDATE. userCanonical ≠ googleCanonical → flag as critical issue regardless of verdict.skills/seo-google/references/cross-skill-integration.md § "seo-page" for the full recipe.SERP context DATA_getSerpResults and DATA_getAiOverview
HTML sense-check WebFetch (always) + mcp__firecrawl-mcp__firecrawl_scrape (when available)
<title>, meta description, all <h1..h6>, lang, word count, internal-link count, image count. WebFetch returns markdown so anything in <head> beyond <title> is lost — the next bullet recovers it.formats: ["rawHtml"]) — recovers what WebFetch can't see. Pin the format to rawHtml; the default html is post-processed and silently strips canonical, hreflang, and <script type="application/ld+json"> blocks on many sites. If those head fields come back zero on a site that obviously has them (any major SaaS homepage), you forgot the rawHtml flag — re-run.
metadata: og:title, og:description, og:image, twitter:card, viewport, robots meta, canonical URL.rawHtml: every <script type="application/ld+json"> block. Parse each as JSON, list detected @types (Article, BreadcrumbList, Organization, Product, etc.). Note any block that fails to parse.<link rel="alternate" hreflang="…"> occurrences in rawHtml.(skipped — Firecrawl not installed). Don't infer from markdown.Domain context DATA_getDomainOverviewWorldwide (parent domain)
Cannibalization check DATA_getDomainPages
DATA_getDomainKeywords on the parent domain — that endpoint can return tens of thousands of rows on large sites and is unnecessarily heavy for this question. DATA_getDomainPages ranked by traffic surfaces the high-impact peers directly.Synthesise verdict
PAGE.md (output spec below).Create a folder seo-page-{target-slug}-{YYYYMMDD}/ with:
seo-page-{target-slug}-{YYYYMMDD}/
├── PAGE.md (synthesised verdict + plan — primary deliverable)
├── keywords.csv (full keyword list with positions — load-bearing CSV the auditor walks through row-by-row)
├── 04-serp-context.md (top 10 + AIO for top 3–5 keywords — load-bearing reference for SERP-driven REFRESH discussions)
└── evidence/
├── 01-url-overview.md (raw DATA_getUrlOverviewWorldwide)
├── 02-keywords.md (raw DATA_getDomainKeywords filtered)
├── 03-authority.md (PA + history)
├── 05-page-snapshot.md (HTML extracts)
└── 06-cannibalization.md (peer pages on the same domain competing for the top-3 keywords)
Top-level: PAGE.md + keywords.csv + 04-serp-context.md. The other step files preserve raw API/HTML extracts in evidence/ for reproducibility — auditors lean on the CSV and SERP context, not the per-call dumps.
PAGE.md follows this shape:
# Page Intelligence: {URL}
> Snapshot dated {YYYY-MM-DD} · Country: {country} · Primary keyword: {keyword}
## Snapshot
- Ranking keywords: {n}
- Estimated monthly organic traffic: {n}
- Page authority: {PA} ({↑/↓ trajectory over 90 days})
- Primary topic: {topic}
- AIO citations: {n} of {checked} primary-keyword AIOs cite this URL
- GSC last 28d: {clicks} clicks / {impressions} impressions / {ctr}% CTR / avg position {n} *(or `not configured` / `property not verified`)*
- Google sees: {INDEXED|EXCLUDED|...} · canonical {userCanonical} {→ googleCanonical if differ} · last crawled {YYYY-MM-DD}
## Page basics
- `<title>`: {value}
- meta description: {value | absent}
- `og:title`: {value | absent | skipped — Firecrawl required}
- `og:description`: {value | absent | skipped}
- `og:image`: {url | absent | skipped}
- `twitter:card`: {summary | summary_large_image | absent | skipped}
- `<link rel="canonical">`: {URL | self-referential | absent | skipped}
- meta robots: {index,follow | noindex,nofollow | absent | skipped}
- JSON-LD types detected: {Article, BreadcrumbList, Organization | none | skipped}
- hreflang variants: {n | skipped}
## What this page wins
- {keyword} — position {n}, ~{volume} monthly searches, ~{traffic} monthly clicks.
- ... (top 3–5)
## Almost-wins (page-2 refresh opportunities)
- {keyword} — position {n}, ~{volume} monthly searches. The top 3 SERP winners all do {pattern} that this page doesn't.
- ... (top 3 examples)
## What this page misses
- {keyword} — competitors {comp1}, {comp2} rank in top 5; this page is absent.
- ...
## Same-domain cannibalization
- {peer URL} — ranks position {n} for "{keyword}" (candidate ranks position {m}). Peer estimated traffic: {n}/mo.
- ... (only list rows where a peer URL ranks ≤ 20 for one of the candidate's top-3 keywords; if none, write "No same-domain cannibalization detected on top-3 keywords.")
## AI Search angle
- {n} of {checked} AIO queries on the URL's keywords cite this page.
- The AIOs that DON'T cite this page tend to cite {pattern} (e.g., comparison tables, step-by-step how-tos).
- Recommended GEO move: {one specific change}.
## Verdict: {KEEP | REFRESH | CONSOLIDATE | KILL}
Reasoning: {1–2 sentences anchored in objective signals from above}.
### If REFRESH — top 3 changes
1. {Specific change}
2. {Specific change}
3. {Specific change}
## Raw data
- keywords.csv — full enriched ranking-keyword list
- 04-serp-context.md — per-keyword SERP top-10 with AIO
- evidence/05-page-snapshot.md — HTML extracts
keywords.csv columns: keyword,volume,kd,position,intent,traffic_estimate,url
DATA_getCreditBalance before running. ~10–15 credits is typical for one URL.INDEXED + impressions > 100 + average position 4–10 (clear quick-win territory).EXCLUDED for any reason.userCanonical ≠ googleCanonical, flag this as a critical issue in PAGE.md regardless of the verdict — it points at indexing instability that the verdict on its own can't resolve.PAGE.md to a number from the raw data files.keywords.csv is the auditable trail. If a stakeholder questions the verdict, walk them through the CSV row by row.DATA_getPageAuthorityHistory all-zeros caveat: if the endpoint returns inlink_rank: 0 (or equivalent) for every date in the history window, treat as "insufficient history" — don't claim a drop or recommend REFRESH based on it. Often happens for very high-authority pages where the metric is saturated, or for URLs that haven't accumulated enough longitudinal data. Cross-check with DATA_getPageAuthority (current value) — if current PA is meaningful but history is flat-zero, flag the gap explicitly in PAGE.md rather than synthesising a trajectory.npx claudepluginhub seranking/seo-skills --plugin seo-skillsSingle-page SEO audit evaluating content quality against Google's E-E-A-T framework, on-page factors, search intent, technical signals, and readability. Fetches live HTML and optional GSC data for one URL.
Audits and diagnoses SEO issues on a live site using UnifAPI for real rendered page data, actual SERP positions, and backlink profiles.
Optimizes SEO using Google Search Console data: analyzes metrics like clicks/impressions/CTR/position, finds striking-distance keywords, fixes low-CTR pages, detects keyword cannibalization, identifies declining pages, builds data-driven strategies.