From seo-skills
Captures SEO baseline snapshots for domains/URLs and compares later runs to surface regressions in authority, traffic, keywords, backlinks, and on-page content.
How this skill is triggered — by the user, by Claude, or both
Slash command
/seo-skills:seo-driftThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Example output: [examples/seo-drift-wix-com-20260514/compare/DRIFT-REPORT.md](../../examples/seo-drift-wix-com-20260514/compare/DRIFT-REPORT.md)
Example output: examples/seo-drift-wix-com-20260514/compare/DRIFT-REPORT.md
Git for SEO. Capture a snapshot of a domain or URL's SEO state ("baseline"), then on later runs diff the current state against the baseline and surface regressions. Catches the things that get worse silently after a deploy, redesign, or content cull.
Acknowledgements: drift-as-an-SEO-skill framework originated in
claude-seoby AgriciDaniel (with the original concept credited to Dan Colta, Pro Hub Challenge). MIT-licensed both directions; this implementation is independent but the framing is theirs.
WebFetch tool available (for URL-mode page fingerprinting).baseline, compare, history).| Flag | Mode | Effect |
|---|---|---|
--no-firecrawl | baseline, compare | Skip Firecrawl-based <head> + JSON-LD capture even when Firecrawl is installed (saves credits at the cost of canonical / robots / og:* / JSON-LD diff coverage). |
--skip-cwv | baseline, compare | Skip the Google CrUX capture (step 4b) even when google-api.json is configured. Useful when you only care about content/structural drift, or when CrUX rate-limit concerns outweigh CWV coverage. Mirrors theirs at seo-drift/SKILL.md:107, 131. |
--baseline-id <n> | compare | Compare against a specific baseline by ID rather than the most recent. |
--limit <n> | history | Cap the number of historical entries shown. |
baseline <target>Capture the current SEO state and write it to a snapshot file. No diff produced.
compare <target>Load the most recent baseline for the target. Capture the current state. Diff. Produce DRIFT-REPORT.md.
history <target>List all stored baselines for the target with their dates and key metrics (DA, traffic, keyword count). No diff produced.
example.com; URL = anything starting with http(s)://.
python3 -c "from scripts.google_auth import validate_url; import sys; sys.exit(0 if validate_url('{target}') else 1)" (or import validate_url directly in any helper script). Reject loopback (127.0.0.1, ::1, localhost), private IP ranges (10/8, 172.16/12, 192.168/16), link-local (169.254/16), and Google metadata endpoints. If validation fails, abort with a clear error and don't proceed to fetch — feeding an unvalidated URL into Firecrawl / WebFetch / Google APIs would risk SSRF against internal services. Mirrors theirs at seo-drift/SKILL.md:97.skills/seo-firecrawl/references/preflight.md for the canonical 3-stage preflight (credit balance, Firecrawl availability, Google APIs). Skill-specific notes:
<head> + JSON-LD content so canonical / robots / og:* / JSON-LD changes are detectable on diff. Without it the snapshot is partial. Pass --no-firecrawl to skip Firecrawl even when available (saves credits at the cost of diff coverage).indexStatusVerdict, googleCanonical, lastCrawlTime) so subsequent compares can flag field-data and indexation drift. See skills/seo-google/references/cross-skill-integration.md § "seo-drift" for the full recipe.DATA_getDomainOverviewWorldwide — DA, traffic, organic + paid keyword counts.DATA_getDomainKeywords — top 100 organic keywords with positions.DATA_getBacklinksSummary — backlinks total, referring domains total.DATA_getBacklinksRefDomains — top 20 referring domains with authority.WebFetch (always) + mcp__firecrawl-mcp__firecrawl_scrape (when available)
<title>, all <h1..h6>, lang, word count, internal-link count, image count, body markdown for prose-level diff.<head> and <script> content WebFetch strips:
metadata: canonical URL, robots meta, og:title, og:description, og:image, twitter:card.html: every <script type="application/ld+json"> block. Capture both detected @types and a hash of the full block content (so any schema-content change is detected on diff, not just type-list changes).--no-firecrawl passed): only WebFetch fields enter the fingerprint. BASELINE.md notes: Snapshot fields recovered via WebFetch only — canonical, robots, og:*, twitter:*, and JSON-LD changes will not be detected on subsequent compares. Install Firecrawl for full coverage.DATA_getPageAuthority.
4b. Google field-data snapshot (only if google-api.json is present AND --skip-cwv not set)python3 scripts/pagespeed_check.py "{target}" --crux-only --json (URL mode) or python3 scripts/pagespeed_check.py "https://{domain}" --crux-only --json (domain mode, origin-level CrUX). Store the resulting p75 LCP / INP / CLS / FCP / TTFB and the source label ("URL" or "origin").python3 scripts/crux_history.py "{target_or_origin}" --json for the 25-week trend window snapshot — store as crux_history_baseline. Subsequent compares can detect drift against the most recent week.python3 scripts/gsc_inspect.py "{target_url}" --site-url "{config.default_property}" --json. Store indexStatusVerdict, coverageState, googleCanonical, userCanonical, lastCrawlTime.--skip-cwv was passed, skip this step entirely and store null for cwv / crux_history fields. The compare-mode rules then surface "Field-data drift: skipped — --skip-cwv flag passed at baseline."null for the affected metrics and continue.seo-drift-{target-slug}-{YYYYMMDD}/snapshot.json.seo-drift-{target-slug}/baselines.json — append {date, snapshot_path} entry.seo-drift-{target-slug}/baselines.json.
validate_url() check as baseline mode. Refuses to fetch private/loopback/metadata addresses.references/drift-thresholds.md:
not comparable — Firecrawl-only fields missing from {baseline | current} snapshot rather than as a green pass.INDEXED to anything else → red.googleCanonical changed → yellow.lastCrawlTime >60 days old → yellow.Field-data / indexation drift: not comparable — Google fields missing from {baseline | current} snapshot.DRIFT-REPORT.md — red findings first, then yellow, then green/positive deltas. End with a "what to investigate first" recommendation.baselines.json.HISTORY.md with the table.seo-drift-{target-slug}-{YYYYMMDD}/:
seo-drift-{target-slug}-{YYYYMMDD}/
├── snapshot.json (the captured state)
└── BASELINE.md (one-page human summary of what was captured)
seo-drift-{target-slug}-{YYYYMMDD}/:
seo-drift-{target-slug}-{YYYYMMDD}/
├── DRIFT-REPORT.md (synthesised: red/yellow/green changes — primary deliverable; inlines 01-domain-deltas, 02-keyword-churn, 03-backlink-deltas, 04-page-deltas as sections)
└── evidence/
├── baseline-snapshot.json (the prior reference — kept for replay)
├── current-snapshot.json (today's state — kept for replay)
├── 01-domain-deltas.md (DA, traffic, keyword count changes — raw step output)
├── 02-keyword-churn.md (top-100 entries/exits)
├── 03-backlink-deltas.md (new + lost backlinks/domains)
└── 04-page-deltas.md (URL mode only: HTML fingerprint diff)
Top-level: DRIFT-REPORT.md only. The four delta step files are inlined into named sections in DRIFT-REPORT.md; evidence/ keeps the raw delta dumps and both snapshot JSONs so a future re-diff or audit can replay against them.
DRIFT-REPORT.md shape:
# Drift Report: {target}
> Baseline: {baseline date} · Current: {today's date}
## RED — investigate today
- {finding} ({severity rationale})
- ...
## YELLOW — investigate this week
- {finding}
- ...
## GREEN — positive deltas
- {finding}
- ...
## Field-data drift (CrUX + URL Inspection)
- LCP p75: {baseline} → {current} ms ({Δ%}) {↑ red / ↑ yellow / stable / ↓ green}
- INP p75: {baseline} → {current} ms ({Δ%}) {…}
- CLS p75: {baseline} → {current} ({Δ absolute}) {…}
- Indexation status: {baseline INDEXED → current EXCLUDED} (URL mode)
- googleCanonical: {baseline → current} (if changed)
- (Or: `Field-data / indexation drift: not configured` / `not comparable — missing from {snapshot}`)
## What to investigate first
1. {prioritised action with reasoning}
2. ...
seo-drift-{target-slug}-{YYYYMMDD}/HISTORY.md:
# History: {target}
| Date | DA | Traffic | Keywords | Top-3 |
|---|---|---|---|---|
| 2026-04-27 | 42 | 18,500/mo | 1,247 | 89 |
| 2026-03-15 | 41 | 17,200/mo | 1,213 | 85 |
| ...
DATA_getCreditBalance before running. Domain baseline ~10–15 SE Ranking credits; URL baseline ~15–20 SE Ranking credits + 1 Firecrawl credit; compare ~20–30 SE Ranking credits + 1 Firecrawl credit (current-state capture).seo-drift-{target-slug}/ directory in a shared filesystem or commit it to a private repo. Baselines are JSON — git-friendly.DATA_getPageAuthorityHistory (URL mode) or DATA_getDomainAuthorityHistory returns flat-zero values across the window, treat as "insufficient history" — don't compute a delta or surface a regression based on missing data. Cross-check the current-value endpoint (DATA_getPageAuthority / DATA_getDomainOverviewWorldwide) — if the current value is meaningful but history is flat, surface that as a data-quality flag in DRIFT-REPORT.md rather than fabricating a trend.npx claudepluginhub seranking/seo-skills --plugin seo-skillsCaptures baselines of SEO-critical elements (title, meta, headings, schema, Open Graph, Core Web Vitals), compares current pages to stored baselines, and tracks regressions over time. Useful for deployment checks and SEO change monitoring.
Captures SEO baselines, compares current state to known-good snapshots, and tracks regressions over time for on-page elements like titles, meta descriptions, headings, schema, and Core Web Vitals.
Compares two SEO snapshots (GSC, GSC AI Performance, rank tracker, AEO probe) and surfaces biggest movers per metric for monthly reviews, core update triage, AI citation tracking, or content refresh attribution.