$npx -y skills add indranilbanerjee/digital-marketing-pro --skill backlink-gapFind referring domains that link to your competitors but not to you, ranked by an opinionated outreach-priority score with DR / link-overlap / traffic / topical relevance. Use when: planning link-building campaigns, qualifying digital-PR prospects, or running quarterly backlink-g
| 1 | # /digital-marketing-pro:backlink-gap |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | Identify the highest-leverage backlink prospects — domains that link to multiple competitors but not to you — and rank them by an opinionated priority score that combines authority, link-overlap signal, downstream traffic, and topical relevance. Produces a numbered output bundle ready for outreach handoff. |
| 6 | |
| 7 | ## Context efficiency |
| 8 | |
| 9 | Heavy skill. **Grep before Read** any referenced file, then `Read` only matched ranges with `offset` + `limit`. List `${CLAUDE_PLUGIN_DATA}/<brand>/` before opening files. On re-invocation mid-session, skip files already in context. |
| 10 | |
| 11 | ## When to Use |
| 12 | |
| 13 | - Quarterly backlink audit — "where did our competitors grow links this quarter and we didn't?" |
| 14 | - Pre-launch link-building plan for a new product or content hub |
| 15 | - Digital PR qualification — separating "would-link-to-anyone" prospects from "high-confidence-will-link-to-our-space" |
| 16 | - Competitive recovery — a competitor displaced you and you want to know which links moved |
| 17 | - Onboarding a new client and need a "first 50 link targets" backlog |
| 18 | |
| 19 | **Don't use** when you just need backlink *quantity* numbers (use the brand's connected backlink MCP directly) or when you need *anchor-text* analysis of your own profile (that's a separate audit — covered in `seo-audit`). |
| 20 | |
| 21 | ## Brand context (auto-applied) |
| 22 | |
| 23 | 1. Read `~/.claude-marketing/brands/_active-brand.json` for the active slug, then load `~/.claude-marketing/brands/{slug}/profile.json` |
| 24 | 2. If no brand exists: ask "Set up a brand first (/digital-marketing-pro:brand-setup)?" — or proceed with defaults |
| 25 | 3. Apply `skills/context-engine/industry-profiles.md` for industry-specific link-quality thresholds (YMYL industries should set higher `--min-dr`) |
| 26 | 4. Apply `skills/context-engine/compliance-rules.md` to filter out blocked publishers (e.g., PBN-style or paid-link networks the brand has explicitly banned) |
| 27 | |
| 28 | ## Inputs |
| 29 | |
| 30 | | Input | Source | Required? | |
| 31 | |---|---|---| |
| 32 | | Our backlinks CSV | Export from connected backlink MCP (Ahrefs / Semrush / SE Ranking / Moz) for the brand's primary domain | yes | |
| 33 | | Competitor backlinks CSVs (2+) | Same exporter, one per competitor (2 minimum for the link-overlap signal; 3-5 is the sweet spot) | yes | |
| 34 | | Min DR / DA filter | CLI flag, brand-profile default, or industry standard | optional | |
| 35 | | Top-N count | How many prospects to surface | optional | |
| 36 | |
| 37 | **One competitor is allowed** (the script warns rather than errors) but the resulting "shared signal" is noise — single-competitor gap analysis is really just "who links to them" rather than "who consistently links in our space." |
| 38 | |
| 39 | ## Process (10 steps, numbered-file output) |
| 40 | |
| 41 | All outputs go to `${CLAUDE_PLUGIN_DATA}/{brand}/seo/backlink-gap/{YYYY-MM-DD}/`. |
| 42 | |
| 43 | 1. **`00-input.md`** — capture our domain, competitor list (with rationale: why these N?), filter parameters, run timestamp |
| 44 | 2. **`01-data-pull.md`** — pull backlinks for `{brand}.tld` and each competitor via brand's connected backlink MCP. **Budget guard**: if the MCP exposes credit cost, sum estimated cost and ask "Continue? (y/N — default N)" before fetching when total > 200 credits. |
| 45 | 3. **`02-ours.csv`** — our backlink export (raw) |
| 46 | 4. **`03-comp-{competitor}.csv`** — one CSV per competitor (raw) |
| 47 | 5. **`04-gap-run.json`** — run the script: |
| 48 | ```bash |
| 49 | python "${CLAUDE_PLUGIN_ROOT}/scripts/backlink_gap.py" \ |
| 50 | --ours "${CLAUDE_PLUGIN_DATA}/{brand}/seo/backlink-gap/{date}/02-ours.csv" \ |
| 51 | --competitors \ |
| 52 | "${CLAUDE_PLUGIN_DATA}/{brand}/seo/backlink-gap/{date}/03-comp-competitor1.csv" \ |
| 53 | "${CLAUDE_PLUGIN_DATA}/{brand}/seo/backlink-gap/{date}/03-comp-competitor2.csv" \ |
| 54 | --min-dr {brand.profile.min_link_dr or 20} \ |
| 55 | --top 100 \ |
| 56 | --out "${CLAUDE_PLUGIN_DATA}/{brand}/seo/backlink-gap/{date}/04-gap-run.json" |
| 57 | ``` |
| 58 | `--competitors` takes an explicit space-separated list of CSV paths (`nargs="+"`) — **enumerate each `03-comp-*.csv` file; the script does not expand a `*` glob**, so a quoted `03-comp-*.csv` would fail with FileNotFoundError. List one path per competitor. |
| 59 | 6. **`05-quality-scorecard.md`** — read `quality_scorecard` from `04-gap-run.json`. If `status: needs_review`, diagnose: |
| 60 | - `data_freshness: fail` → input CSV(s) older than 90 days. Re-pull data; backlink graphs decay fast. |
| 61 | - `sample_size: fail` → any input < 50 unique referring domains. Either the domain is too new or the export was truncated. Re-export with no row limit. |
| 62 | - `competitor_coverage: warn` → only 1 competitor. Add at least 1 more for genuine overlap signal. |
| 63 | - `link_overlap_signal: fail` → fewer than 5 referring domains link to ≥2 |