optbrew 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +6 -0
- package/.claude-plugin/plugin.json +6 -0
- package/.codex-plugin/plugin.json +13 -0
- package/CHANGELOG.md +50 -0
- package/LICENSE +21 -0
- package/README.ko.md +149 -0
- package/README.md +148 -2
- package/THIRD_PARTY_NOTICES.md +10 -0
- package/bin/optbrew.mjs +4 -0
- package/docs/platforms.md +107 -0
- package/docs/release.md +14 -0
- package/lib/cli.mjs +116 -0
- package/lib/doctor.mjs +45 -0
- package/lib/install.mjs +158 -0
- package/lib/paths.mjs +30 -0
- package/lib/records.mjs +44 -0
- package/lib/reinstall.mjs +92 -0
- package/lib/transaction.mjs +205 -0
- package/lib/update.mjs +189 -0
- package/package.json +39 -4
- package/plugin.json +13 -0
- package/skills/optbrew/LICENSE +21 -0
- package/skills/optbrew/SKILL.md +17 -0
- package/skills/optbrew/THIRD_PARTY_NOTICES.md +10 -0
- package/skills/optbrew/agents/openai.yaml +4 -0
- package/skills/optbrew/bundle-manifest.json +687 -0
- package/skills/optbrew/licenses/firecrawl-seo-audit.txt +15 -0
- package/skills/optbrew/licenses/magnus-seo.txt +21 -0
- package/skills/optbrew/licenses/marketing-search.txt +21 -0
- package/skills/optbrew/licenses/mblode-seo.txt +21 -0
- package/skills/optbrew/licenses/naver-searchadvisor.txt +29 -0
- package/skills/optbrew/licenses/seranking-seo.txt +21 -0
- package/skills/optbrew/modules/firecrawl-seo-audit/GUIDE.md +64 -0
- package/skills/optbrew/modules/magnus-seo/GUIDE.md +79 -0
- package/skills/optbrew/modules/magnus-seo/README.md +45 -0
- package/skills/optbrew/modules/magnus-seo/assets/audit-report-template.md +76 -0
- package/skills/optbrew/modules/magnus-seo/references/aeo-methodology.md +105 -0
- package/skills/optbrew/modules/magnus-seo/references/agent-readable-content.md +28 -0
- package/skills/optbrew/modules/magnus-seo/references/content-and-entity-architecture.md +100 -0
- package/skills/optbrew/modules/magnus-seo/references/content-strategy-seo.md +142 -0
- package/skills/optbrew/modules/magnus-seo/references/discovery-and-freshness.md +47 -0
- package/skills/optbrew/modules/magnus-seo/references/evidence-boundaries.md +75 -0
- package/skills/optbrew/modules/magnus-seo/references/ghost-metadata.md +204 -0
- package/skills/optbrew/modules/magnus-seo/references/implementation-playbook.md +114 -0
- package/skills/optbrew/modules/magnus-seo/references/measurement-and-experimentation.md +47 -0
- package/skills/optbrew/modules/magnus-seo/references/onpage-seo.md +154 -0
- package/skills/optbrew/modules/magnus-seo/references/platform-guidance.md +30 -0
- package/skills/optbrew/modules/magnus-seo/references/schema-markup.md +244 -0
- package/skills/optbrew/modules/magnus-seo/references/source-index.md +36 -0
- package/skills/optbrew/modules/magnus-seo/references/structured-data.md +56 -0
- package/skills/optbrew/modules/magnus-seo/references/technical-seo.md +99 -0
- package/skills/optbrew/modules/magnus-seo/scripts/aeo_audit.py +158 -0
- package/skills/optbrew/modules/magnus-seo/scripts/build_prompt_matrix.py +52 -0
- package/skills/optbrew/modules/magnus-seo/templates/aeo-implementation-plan.md +53 -0
- package/skills/optbrew/modules/magnus-seo/templates/citation-observation-log.md +16 -0
- package/skills/optbrew/modules/magnus-seo/templates/llms.txt.template +15 -0
- package/skills/optbrew/modules/magnus-seo/templates/question-cluster.md +27 -0
- package/skills/optbrew/modules/magnus-seo/templates/robots-ai-crawlers.txt +23 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/GUIDE.md +483 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/agent-readiness.md +57 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/citations-vs-recommendations.md +85 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/content-patterns.md +287 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/content-types.md +71 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/format-volatility.md +98 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/okf.md +104 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/platform-ranking-factors.md +154 -0
- package/skills/optbrew/modules/marketing-search/skills/ai-seo/references/youtube-ai-citations.md +60 -0
- package/skills/optbrew/modules/marketing-search/skills/analytics/GUIDE.md +303 -0
- package/skills/optbrew/modules/marketing-search/skills/analytics/references/event-library.md +260 -0
- package/skills/optbrew/modules/marketing-search/skills/analytics/references/ga4-implementation.md +300 -0
- package/skills/optbrew/modules/marketing-search/skills/analytics/references/gtm-implementation.md +390 -0
- package/skills/optbrew/modules/marketing-search/skills/content-strategy/GUIDE.md +432 -0
- package/skills/optbrew/modules/marketing-search/skills/content-strategy/references/content-distribution.md +83 -0
- package/skills/optbrew/modules/marketing-search/skills/content-strategy/references/headless-cms.md +194 -0
- package/skills/optbrew/modules/marketing-search/skills/programmatic-seo/GUIDE.md +231 -0
- package/skills/optbrew/modules/marketing-search/skills/programmatic-seo/references/playbooks.md +308 -0
- package/skills/optbrew/modules/marketing-search/skills/schema/GUIDE.md +172 -0
- package/skills/optbrew/modules/marketing-search/skills/schema/references/schema-examples.md +398 -0
- package/skills/optbrew/modules/marketing-search/skills/seo-audit/GUIDE.md +492 -0
- package/skills/optbrew/modules/marketing-search/skills/seo-audit/references/ai-writing-detection.md +200 -0
- package/skills/optbrew/modules/marketing-search/skills/seo-audit/references/international-seo.md +230 -0
- package/skills/optbrew/modules/marketing-search/skills/site-architecture/GUIDE.md +350 -0
- package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/mermaid-templates.md +216 -0
- package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/navigation-patterns.md +305 -0
- package/skills/optbrew/modules/marketing-search/skills/site-architecture/references/site-type-templates.md +293 -0
- package/skills/optbrew/modules/mblode-seo/GUIDE.md +74 -0
- package/skills/optbrew/modules/mblode-seo/references/answer-engines.md +58 -0
- package/skills/optbrew/modules/mblode-seo/references/audit.md +78 -0
- package/skills/optbrew/modules/mblode-seo/references/brief-template.md +47 -0
- package/skills/optbrew/modules/mblode-seo/references/monitoring.md +56 -0
- package/skills/optbrew/modules/mblode-seo/references/nextjs-implementation.md +65 -0
- package/skills/optbrew/modules/mblode-seo/references/research-protocol.md +37 -0
- package/skills/optbrew/modules/mblode-seo/references/sources.md +47 -0
- package/skills/optbrew/modules/mblode-seo/references/validation-evidence.md +21 -0
- package/skills/optbrew/modules/naver-searchadvisor/GUIDE.md +75 -0
- package/skills/optbrew/modules/naver-searchadvisor/NOTICE.md +59 -0
- package/skills/optbrew/modules/naver-searchadvisor/references/INDEX.md +97 -0
- package/skills/optbrew/modules/naver-searchadvisor/scripts/audit.mjs +742 -0
- package/skills/optbrew/modules/naver-searchadvisor/scripts/robots.mjs +77 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-ai-search-share-of-voice/GUIDE.md +94 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-api/GUIDE.md +186 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/api-surface-map.md +207 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/auth-and-keys.md +182 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/integration-patterns.md +283 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/rate-limits-and-credits.md +235 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-competitor-gap-analysis/GUIDE.md +117 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/GUIDE.md +155 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/references/cite.md +84 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/references/core-eeat.md +147 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-content-audit/templates/verdict.md +63 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-firecrawl/GUIDE.md +141 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-firecrawl/references/preflight.md +73 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-geo/GUIDE.md +159 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/GUIDE.md +393 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/cwv-audit-report.md +48 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/gsc-performance-report.md +44 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/assets/templates/indexation-status-report.md +43 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/auth-setup.md +160 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/cross-skill-integration.md +187 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/ga4-data-api.md +184 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/indexing-api.md +109 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/keyword-planner-api.md +66 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/nlp-api.md +55 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/pagespeed-crux-api.md +204 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/rate-limits-quotas.md +75 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/search-console-api.md +156 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/supplementary-apis.md +99 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-google/references/youtube-api.md +49 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-keyword-niche/GUIDE.md +174 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-page/GUIDE.md +174 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-plan/GUIDE.md +220 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/GUIDE.md +135 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/references/google-rich-results.md +89 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/article.json +23 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/breadcrumb-list.json +18 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/faq-page.json +22 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/local-business.json +32 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-schema/templates/product.json +25 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-sitemap/GUIDE.md +128 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-technical-audit/GUIDE.md +235 -0
- package/skills/optbrew/modules/seranking-seo/skills/seo-technical-audit/references/severity-mapping.md +133 -0
- package/skills/optbrew/references/ai-crawlers.json +21 -0
- package/skills/optbrew/references/ai-crawlers.md +24 -0
- package/skills/optbrew/references/api.md +45 -0
- package/skills/optbrew/references/check-registry.json +28 -0
- package/skills/optbrew/references/evidence.md +9 -0
- package/skills/optbrew/references/intake.md +9 -0
- package/skills/optbrew/references/measurement.md +29 -0
- package/skills/optbrew/references/policies.md +7 -0
- package/skills/optbrew/references/profiles.json +13 -0
- package/skills/optbrew/references/remediations.json +12 -0
- package/skills/optbrew/references/report.md +29 -0
- package/skills/optbrew/references/routing.md +15 -0
- package/skills/optbrew/references/schemas/contract.schema.json +51 -0
- package/skills/optbrew/references/scoring.md +62 -0
- package/skills/optbrew/scripts/adapters/anthropic.mjs +116 -0
- package/skills/optbrew/scripts/adapters/imports.mjs +81 -0
- package/skills/optbrew/scripts/audit.mjs +124 -0
- package/skills/optbrew/scripts/cli.mjs +41 -0
- package/skills/optbrew/scripts/core/bootstrap.mjs +31 -0
- package/skills/optbrew/scripts/core/bundle.mjs +73 -0
- package/skills/optbrew/scripts/core/capabilities.mjs +19 -0
- package/skills/optbrew/scripts/core/collector.mjs +250 -0
- package/skills/optbrew/scripts/core/compare.mjs +47 -0
- package/skills/optbrew/scripts/core/contracts.mjs +144 -0
- package/skills/optbrew/scripts/core/cost-ledger.mjs +95 -0
- package/skills/optbrew/scripts/core/crawlers.mjs +11 -0
- package/skills/optbrew/scripts/core/derive.mjs +168 -0
- package/skills/optbrew/scripts/core/facts.mjs +189 -0
- package/skills/optbrew/scripts/core/findings.mjs +54 -0
- package/skills/optbrew/scripts/core/html.mjs +53 -0
- package/skills/optbrew/scripts/core/metrics.mjs +97 -0
- package/skills/optbrew/scripts/core/protocol.mjs +125 -0
- package/skills/optbrew/scripts/core/report.mjs +742 -0
- package/skills/optbrew/scripts/core/scope.mjs +52 -0
- package/skills/optbrew/scripts/core/score.mjs +71 -0
- package/skills/optbrew/scripts/core/weights.mjs +139 -0
- package/skills/optbrew/source-lock.json +2686 -0
- package/source-lock.json +2686 -0
package/skills/optbrew/modules/seranking-seo/skills/seo-api/references/rate-limits-and-credits.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Rate Limits & Credits
|
|
2
|
+
|
|
3
|
+
Everything the `seo-api` skill needs to forecast cost and pace requests against the Data API and Project API.
|
|
4
|
+
|
|
5
|
+
## Rate limits
|
|
6
|
+
|
|
7
|
+
**Per API key, not per IP.** All threads, workers, and servers sharing one key contribute to the same RPS budget. For production fan-outs, mint multiple keys via the API Dashboard.
|
|
8
|
+
|
|
9
|
+
| API | Standard limit | Trial accounts |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Data API | **10 requests per second** | 1 RPS (can be raised on request — email api@seranking.com) |
|
|
12
|
+
| Project API | **5 requests per second** | 1 RPS (same path to raise) |
|
|
13
|
+
|
|
14
|
+
**Async operations.** Endpoints like `/backlinks/export` create a task; subsequent polls of `*ExportStatus` count against the same RPS budget (but cost 0 credits).
|
|
15
|
+
|
|
16
|
+
**Custom limits.** Production workloads needing higher throughput: contact `api@seranking.com`. Custom plans are available.
|
|
17
|
+
|
|
18
|
+
## Handling 429 — exponential backoff with jitter
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import random, time, requests
|
|
22
|
+
|
|
23
|
+
def call(url, headers, max_attempts=5):
|
|
24
|
+
delay = 1.0
|
|
25
|
+
for attempt in range(max_attempts):
|
|
26
|
+
r = requests.get(url, headers=headers)
|
|
27
|
+
if r.status_code != 429:
|
|
28
|
+
return r
|
|
29
|
+
jitter = random.uniform(-0.2, 0.2) * delay
|
|
30
|
+
time.sleep(delay + jitter)
|
|
31
|
+
delay *= 2
|
|
32
|
+
r.raise_for_status()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
async function call(url: string, headers: HeadersInit, maxAttempts = 5): Promise<Response> {
|
|
37
|
+
let delay = 1000;
|
|
38
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
39
|
+
const r = await fetch(url, { headers });
|
|
40
|
+
if (r.status !== 429) return r;
|
|
41
|
+
const jitter = (Math.random() * 0.4 - 0.2) * delay;
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
43
|
+
delay *= 2;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`Rate-limited after ${maxAttempts} attempts`);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Why jitter matters.** Without it, multiple clients hitting the limit simultaneously synchronise their retries and re-hit the limit at the next interval — the thundering-herd problem. A ±20% randomisation spreads them out.
|
|
50
|
+
|
|
51
|
+
**Simple delays (use with caution).** Because the API uses a rolling 1s window, a brief pause (200–500ms) is enough for occasional, isolated overages. But it doesn't survive bursty traffic — use exponential backoff for anything production.
|
|
52
|
+
|
|
53
|
+
## Client-side throttling
|
|
54
|
+
|
|
55
|
+
Better than reactive 429 handling: proactively pace requests to stay under the limit.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import time
|
|
59
|
+
from collections import deque
|
|
60
|
+
|
|
61
|
+
class RateLimiter:
|
|
62
|
+
def __init__(self, rps: int):
|
|
63
|
+
self.rps = rps
|
|
64
|
+
self.calls = deque()
|
|
65
|
+
|
|
66
|
+
def acquire(self):
|
|
67
|
+
now = time.monotonic()
|
|
68
|
+
while self.calls and self.calls[0] < now - 1.0:
|
|
69
|
+
self.calls.popleft()
|
|
70
|
+
if len(self.calls) >= self.rps:
|
|
71
|
+
time.sleep(1.0 - (now - self.calls[0]))
|
|
72
|
+
self.calls.append(time.monotonic())
|
|
73
|
+
|
|
74
|
+
limiter = RateLimiter(rps=10)
|
|
75
|
+
for url in urls:
|
|
76
|
+
limiter.acquire()
|
|
77
|
+
call(url, headers)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Credit system (Data API)
|
|
81
|
+
|
|
82
|
+
Pay-as-you-go. Plans start at 1 million credits.
|
|
83
|
+
|
|
84
|
+
### Two billing models
|
|
85
|
+
|
|
86
|
+
1. **Cost per record** — credits charged per row returned. Example: `/backlinks/summary` at 100 credits/record, request 250 domains → 25,000 credits.
|
|
87
|
+
2. **Cost per request** — flat fee per successful call regardless of payload size. Example: `/keywords/research` at 5 credits/request, 1000 keywords returned → still 5 credits.
|
|
88
|
+
|
|
89
|
+
**Failed requests are free.** 4xx and 5xx never consume credits. Don't over-engineer error retries to "save credits".
|
|
90
|
+
|
|
91
|
+
### Reading current balance
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl -X GET 'https://api.seranking.com/v1/account/subscription' \
|
|
95
|
+
-H 'Authorization: Token YOUR_API_KEY'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or via MCP: `DATA_getCreditBalance` (0 credits, returns `units_left`).
|
|
99
|
+
|
|
100
|
+
Response shape:
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"subscription_info": {
|
|
104
|
+
"status": "active",
|
|
105
|
+
"start_date": "2026-01-18 14:20:02",
|
|
106
|
+
"expiration_date": "2027-01-18 14:20:02",
|
|
107
|
+
"units_limit": 5000000,
|
|
108
|
+
"units_left": 4975033
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Forecasting
|
|
114
|
+
|
|
115
|
+
Before running a large workflow:
|
|
116
|
+
|
|
117
|
+
1. List every API call.
|
|
118
|
+
2. Multiply by per-call cost (see canonical table at <https://seranking.com/api/data/getting-started/#unit-costs>, or check each MCP tool's own description for the per-tool figure).
|
|
119
|
+
3. Sum.
|
|
120
|
+
4. Compare against `units_left`.
|
|
121
|
+
|
|
122
|
+
**Worked example.** Pull backlink summaries for 250 domains + full export for one:
|
|
123
|
+
|
|
124
|
+
| Call | Records | Per-record cost | Total |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| `/backlinks/summary` | 250 | 100 credits | 25,000 |
|
|
127
|
+
| `/backlinks/export` (task creation) | 50,000 (links in target domain) | 1 credit | 50,000 |
|
|
128
|
+
| `/backlinks/export/status` (poll 2×) | 2 | 0 credits/request | 0 |
|
|
129
|
+
| **Total** | | | **75,000 credits** |
|
|
130
|
+
|
|
131
|
+
### Insufficient credits — 403
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"error": {
|
|
136
|
+
"code": 403,
|
|
137
|
+
"message": "Insufficient funds",
|
|
138
|
+
"description": "Your current credit balance is too low to process this request."
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
No partial billing — the entire request is rejected. Top up at the API Dashboard or contact `api@seranking.com` for overage billing.
|
|
144
|
+
|
|
145
|
+
## Plan limits (Project API)
|
|
146
|
+
|
|
147
|
+
The Project API doesn't use credits. It consumes the same limits as the SE Ranking web platform.
|
|
148
|
+
|
|
149
|
+
| Action | Limit consumed | When |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `PROJECT_createProject` | 1 Site | On creation |
|
|
152
|
+
| `PROJECT_addKeywords` | N Keywords | Each new keyword tracked |
|
|
153
|
+
| `PROJECT_runPositionCheck` | (free — uses tracking schedule) | — |
|
|
154
|
+
| `PROJECT_addAuditSourcePages` + audit run | N Audit Pages | Per page crawled |
|
|
155
|
+
| `PROJECT_createAudit` (advanced) | Audit Pages × pages | Per crawl |
|
|
156
|
+
| `PROJECT_addPrompts` | N AIRT Prompts | Per prompt added to AIRT |
|
|
157
|
+
| `PROJECT_addCompetitor` | (free) | — |
|
|
158
|
+
| `PROJECT_addProjectBacklink` | (free — uses Backlinks tier) | — |
|
|
159
|
+
|
|
160
|
+
**Read the user's plan limits before mutating.** `PROJECT_getUserProfile` returns the current plan tier and remaining quota. If the integration would push a limit over, surface upfront and ask whether to proceed or downsize.
|
|
161
|
+
|
|
162
|
+
### Insufficient plan limits
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"error": {
|
|
167
|
+
"code": 403,
|
|
168
|
+
"message": "Limit reached",
|
|
169
|
+
"description": "Cannot add 50 keywords — you have 32 keywords remaining on the Pro plan."
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Fix paths:
|
|
175
|
+
- Upgrade plan: <https://seranking.com/subscription.html>.
|
|
176
|
+
- Free up quota: delete unused projects, keywords, AIRT prompts via the matching `PROJECT_delete*` tool.
|
|
177
|
+
|
|
178
|
+
## Per-endpoint cost cheat-sheet
|
|
179
|
+
|
|
180
|
+
Credit costs are **not** carried in the MCP tool descriptions — the canonical source is the per-endpoint pages on `seranking.com/api/data/*` and the unit-costs table at <https://seranking.com/api/data/getting-started/#unit-costs>. Always confirm there before a large run; SE Ranking updates costs periodically.
|
|
181
|
+
|
|
182
|
+
**Verified against the docs (2026-05-22):**
|
|
183
|
+
|
|
184
|
+
| Endpoint | MCP tool | Cost |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| Account subscription | `DATA_getSubscription` | 0 |
|
|
187
|
+
| Credit balance | `DATA_getCreditBalance` | 0 |
|
|
188
|
+
| Domain overview (worldwide) | `DATA_getDomainOverviewWorldwide` | 100 / request |
|
|
189
|
+
| Domain overview (regional) | `DATA_getDomainOverviewDatabases` | 100 / request |
|
|
190
|
+
| Domain competitors | `DATA_getDomainCompetitors` | 100 / request (flat — *not* per-competitor) |
|
|
191
|
+
| Backlinks summary | `DATA_getBacklinksSummary` | 100 / target |
|
|
192
|
+
|
|
193
|
+
For any endpoint not in this table, read the **"Cost:"** line on its docs page before forecasting — do not guess a figure. Apply the two billing models described above: **per request** (flat fee per call — most overview / summary / competitor endpoints) and **per record** (one charge per row — list and export endpoints; e.g. `DATA_exportBacklinksData` charges per backlink record, `DATA_getDomainKeywords` per keyword returned). When unsure, run the smallest possible request first and read the actual charge against the docs.
|
|
194
|
+
|
|
195
|
+
## Combined rate-limit + credit safety pattern
|
|
196
|
+
|
|
197
|
+
The end-to-end shape for any production integration:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import time, random, requests
|
|
201
|
+
|
|
202
|
+
API_KEY = os.environ["SERANKING_API_KEY"]
|
|
203
|
+
HEADERS = {"Authorization": f"Token {API_KEY}"}
|
|
204
|
+
BASE = "https://api.seranking.com/v1"
|
|
205
|
+
|
|
206
|
+
class SERankingClient:
|
|
207
|
+
def __init__(self, rps=10):
|
|
208
|
+
self.rps = rps
|
|
209
|
+
self.calls = deque()
|
|
210
|
+
|
|
211
|
+
def _throttle(self):
|
|
212
|
+
now = time.monotonic()
|
|
213
|
+
while self.calls and self.calls[0] < now - 1.0:
|
|
214
|
+
self.calls.popleft()
|
|
215
|
+
if len(self.calls) >= self.rps:
|
|
216
|
+
time.sleep(1.0 - (now - self.calls[0]))
|
|
217
|
+
self.calls.append(time.monotonic())
|
|
218
|
+
|
|
219
|
+
def get(self, path, params=None, max_attempts=5):
|
|
220
|
+
delay = 1.0
|
|
221
|
+
for attempt in range(max_attempts):
|
|
222
|
+
self._throttle()
|
|
223
|
+
r = requests.get(f"{BASE}{path}", headers=HEADERS, params=params)
|
|
224
|
+
if r.status_code == 429:
|
|
225
|
+
time.sleep(delay + random.uniform(-0.2, 0.2) * delay)
|
|
226
|
+
delay *= 2
|
|
227
|
+
continue
|
|
228
|
+
if r.status_code == 403 and "Insufficient funds" in r.text:
|
|
229
|
+
raise RuntimeError("Out of credits — top up before retrying.")
|
|
230
|
+
r.raise_for_status()
|
|
231
|
+
return r.json()
|
|
232
|
+
raise RuntimeError(f"Rate-limited after {max_attempts} attempts on {path}")
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
This handles the common cases: rate-limit pacing, exponential backoff with jitter, terminal 403, transient 5xx. Tune `rps` to match the API (5 for Project, 10 for Data).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
> Example output: examples/seo-competitor-gap-analysis-wix-com-20260514/REPORT.md (upstream-only reference; route through optbrew)
|
|
2
|
+
|
|
3
|
+
# Competitor Gap Analysis
|
|
4
|
+
|
|
5
|
+
Identify the specific keywords your competitors rank for in the top 20 that your domain does not, ranked by commercial value and realistic capture difficulty.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- SE Ranking MCP server connected.
|
|
10
|
+
- User provides: (a) target domain, (b) 3 to 5 competitor domains (or ask the skill to auto-discover them), (c) market country (default: `us`), and optionally filters (min volume, max KD, intent).
|
|
11
|
+
|
|
12
|
+
## Process
|
|
13
|
+
|
|
14
|
+
1. **Validate or discover competitors** `DATA_getDomainCompetitors`
|
|
15
|
+
- If the user did not provide competitors, pull the top 5 organic competitors for the target in the target market.
|
|
16
|
+
- Surface the list to the user and ask them to confirm or override before proceeding.
|
|
17
|
+
- **Note:** the upstream API does not support `limit`/`offset`, so this call returns the full set (~60KB for popular domains) and the MCP harness writes it to a file. Read that file path, parse the `{data: [...]}` JSON, sort by `common_keywords` desc, and take the top 5.
|
|
18
|
+
|
|
19
|
+
2. **Pull competitor keyword sets** `DATA_getDomainKeywords`
|
|
20
|
+
- For each competitor, pull keywords where they rank in the top 20 of the target country.
|
|
21
|
+
- Save per-competitor lists.
|
|
22
|
+
|
|
23
|
+
3. **Pull target keyword set** `DATA_getDomainKeywords`
|
|
24
|
+
- For the target domain, pull all ranking keywords in the target country (any position).
|
|
25
|
+
- This is the exclusion set.
|
|
26
|
+
|
|
27
|
+
4. **Compute the gap** `DATA_getDomainKeywordsComparison` (cross-check)
|
|
28
|
+
- Keywords ranked by at least one competitor in the top 20 but not ranked by the target domain at all.
|
|
29
|
+
- Use the comparison endpoint as a cross-check.
|
|
30
|
+
|
|
31
|
+
5. **Filter and segment**
|
|
32
|
+
- Apply user-specified filters on volume, KD, and intent.
|
|
33
|
+
- Segment by intent: informational, commercial, transactional, navigational.
|
|
34
|
+
- Segment by competition: how many of the N competitors rank for each gap keyword.
|
|
35
|
+
|
|
36
|
+
6. **Score and prioritise**
|
|
37
|
+
- Score each gap keyword: traffic potential (volume × CTR model) + intent weighting + inverse KD.
|
|
38
|
+
- Surface the top 50 opportunities with reasoning per keyword.
|
|
39
|
+
|
|
40
|
+
7. **Map to content actions**
|
|
41
|
+
- For each top opportunity, recommend: new article, expand existing page, refresh existing page, programmatic template.
|
|
42
|
+
- Flag quick wins: competitors rank with thin content (detected by URL pattern and content-length heuristics).
|
|
43
|
+
|
|
44
|
+
## Output format
|
|
45
|
+
|
|
46
|
+
Create a folder `seo-competitor-gap-analysis-{target-slug}-{YYYYMMDD}/` with:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
seo-competitor-gap-analysis-{target-slug}-{YYYYMMDD}/
|
|
50
|
+
├── REPORT.md (synthesised report — primary deliverable)
|
|
51
|
+
├── gaps.csv (full gap list — load-bearing CSV the writers/planners paste into briefs)
|
|
52
|
+
└── evidence/
|
|
53
|
+
├── 01-competitors.md (competitor list / discovery — raw step output)
|
|
54
|
+
├── 02-competitor-keywords-{domain}.md (one per competitor — raw step output)
|
|
55
|
+
├── 03-target-keywords.md (target's existing ranking set — raw step output)
|
|
56
|
+
└── 04-gap-raw.md (human-readable full gap list before filtering — raw step output)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Top-level: `REPORT.md` + `gaps.csv`. The numbered step files preserve the raw API outputs in `evidence/` for reproducibility.
|
|
60
|
+
|
|
61
|
+
`REPORT.md` follows this shape:
|
|
62
|
+
|
|
63
|
+
```markdown
|
|
64
|
+
# Competitor Gap: {target}
|
|
65
|
+
Market: {country}
|
|
66
|
+
Competitors analysed: {list}
|
|
67
|
+
|
|
68
|
+
## Summary
|
|
69
|
+
- Competitor keywords in top 20: {n}
|
|
70
|
+
- Target keywords overall: {n}
|
|
71
|
+
- Gap keywords (opportunities): {n}
|
|
72
|
+
- Gap traffic potential: ~{n}/mo
|
|
73
|
+
|
|
74
|
+
## Top 50 opportunities
|
|
75
|
+
|
|
76
|
+
### Informational intent
|
|
77
|
+
| # | Keyword | Volume | KD | Competitors ranking | Action | Score |
|
|
78
|
+
|---|---|---|---|---|---|---|
|
|
79
|
+
| 1 | {kw} | {n} | {n} | {3 of 5} | New article | {score} |
|
|
80
|
+
| 2 | ... | ... | ... | ... | ... | ... |
|
|
81
|
+
|
|
82
|
+
### Commercial intent
|
|
83
|
+
| # | Keyword | Volume | KD | Competitors ranking | Action | Score |
|
|
84
|
+
|---|---|---|---|---|---|---|
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
### Transactional intent
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
## Quick wins (top 10)
|
|
91
|
+
Keywords where competitors rank in positions 5 to 20 with thin content, low DT, or old dates.
|
|
92
|
+
|
|
93
|
+
| # | Keyword | Weakest competitor position | Suggested angle |
|
|
94
|
+
|---|---|---|---|
|
|
95
|
+
| 1 | {kw} | example.com at #14 (2022 article, 800 words) | Fresh, comprehensive guide |
|
|
96
|
+
|
|
97
|
+
## Recommended next steps
|
|
98
|
+
1. Run `content-brief` on the top 3 opportunities to generate writer-ready briefs.
|
|
99
|
+
2. Run `keyword-cluster-planner` on the full gap list to build a sequencing plan.
|
|
100
|
+
3. Add the gap keywords to an SE Ranking project for rank tracking once content ships.
|
|
101
|
+
|
|
102
|
+
## Files
|
|
103
|
+
- gaps.csv: full gap list for spreadsheet analysis
|
|
104
|
+
- evidence/04-gap-raw.md: human-readable full gap list before filtering
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`gaps.csv` columns:
|
|
108
|
+
`keyword,volume,kd,cpc,intent,competitors_ranking,top_competitor_position,target_position,action,score`
|
|
109
|
+
|
|
110
|
+
## Tips
|
|
111
|
+
|
|
112
|
+
- Data API rate limit: 10 requests per second. For large sites, `DATA_getDomainKeywords` may paginate heavily; set a ceiling (e.g., top 1,000 keywords per domain) unless the user explicitly asks for the full set.
|
|
113
|
+
- Call `DATA_getCreditBalance` before running. A full pass on 10 seeds typically consumes 30–80 credits; 20 seeds can exceed 150.
|
|
114
|
+
- The `competitors_ranking` count is the best signal of realism. Keywords ranked by 4 of 5 competitors are validated opportunities; keywords ranked by only 1 may be noise.
|
|
115
|
+
- Do not recommend capturing branded competitor keywords unless the user explicitly asks. Pivoting to compete on "competitor brand review" is a viable strategy but only if the user opts in.
|
|
116
|
+
- When many gap keywords cluster around a theme, recommend a hub page plus cluster rather than 50 individual articles.
|
|
117
|
+
- Chain this skill with `content-brief` and `keyword-cluster-planner` naturally. Mention them in the Recommended next steps section of the output.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
> Example output: examples/seo-content-audit-stripe-rate-limiters-20260514/VERDICT.md (upstream-only reference; route through optbrew)
|
|
2
|
+
|
|
3
|
+
# Content Quality Audit
|
|
4
|
+
|
|
5
|
+
Score an existing piece of content against modern E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness) and CITE (Clear answer, Include primary stats, Timestamp, Entity authority) rubrics. Surface veto items that block publication regardless of overall score. Produce a clear publish / publish-with-fixes / no-publish verdict with the top 5 fixes.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- SE Ranking MCP server connected.
|
|
10
|
+
- Claude's `WebFetch` tool available.
|
|
11
|
+
- User provides: (a) the URL of an existing piece of content (or pasted content + intended URL), (b) target keyword the content is meant to rank for. Optional: target country (default `us`).
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
1. **Fetch content** `WebFetch` (always) + `mcp__firecrawl-mcp__firecrawl_scrape` (when available)
|
|
16
|
+
- **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:
|
|
17
|
+
- Estimated SE Ranking cost for this skill: ~10–15 credits typical (AIO context + AIO prompt sampling for the target keyword + audited URL).
|
|
18
|
+
- Firecrawl: optional with WebFetch fallback, 1 Firecrawl credit per URL audited (default cap 50 URLs, hard cap 200). Surface the projected Firecrawl credit count before continuing. Pass `--no-firecrawl` to force WebFetch-only inspection (lower-confidence veto checks; see step 4 caveat).
|
|
19
|
+
- Google APIs: tier 2 (GA4 available) unlocks step 3b (GA4 organic traffic on the audited URL) after the AIO context step. See `skills/seo-google/references/cross-skill-integration.md` § "seo-content-audit" for the full recipe.
|
|
20
|
+
- **WebFetch first** (free, instant): pull the markdown for word count, H-tag hierarchy, source citations (links to authorities, numbered references), images, tables, code blocks, comment thread.
|
|
21
|
+
- **Page-type detection.** From the URL pattern, H1 phrasing, and JSON-LD `@type`, classify the page as one of: ultimate guide / pillar, how-to, listicle / best-of, comparison (X vs Y), explainer, review (single product), landing page (commercial). Look up the corresponding word-count floor from `references/core-eeat.md` → "Word-count floors by page type". Surface the detected type, the applied floor, and the actual word count in `evidence/01-content-snapshot.md`. If the actual word count is materially below the floor, flag it for the depth E-E-A-T items (auto ✗ unless the auditor justifies the exception).
|
|
22
|
+
- **Firecrawl second** — recovers what WebFetch's markdown loses:
|
|
23
|
+
- From `metadata`: canonical URL, robots, lang, `og:title`.
|
|
24
|
+
- From the returned `html`: every `<script type="application/ld+json">` block. Parse for `Article` / `BlogPosting` schema and extract `author` (name, `@type: Person`, optional `url` + `sameAs`), `datePublished` / `dateModified` (ISO 8601), `publisher`, `mainEntityOfPage`. Detect `Person` schema standalone if present.
|
|
25
|
+
- DOM-level byline detection: locate the structural byline (`<a rel="author">`, `<meta name="author">`, `<span class="byline">`, `[itemprop="author"]`). Distinguish a real byline element from prose mentions ("Written by Jane in collaboration..." in body text is not a byline; `<a rel="author">Jane Doe</a>` is).
|
|
26
|
+
- **If Firecrawl unavailable:** WebFetch portion runs unchanged. Mark schema-type detection and structural byline detection as `(skipped — Firecrawl required)` in `evidence/01-content-snapshot.md`. Step 4's veto checks #1 and #4 fall back to prose-level inspection (less reliable) — surface that caveat in `VERDICT.md`.
|
|
27
|
+
|
|
28
|
+
2. **AIO context** `DATA_getAiOverview` and `DATA_getAiOverviewLeaderboard`
|
|
29
|
+
- For the target keyword: is there an AIO?
|
|
30
|
+
- Who is cited in the AIO?
|
|
31
|
+
- Is the candidate URL cited?
|
|
32
|
+
- What patterns characterise the cited sources (publication tier, freshness, structure)?
|
|
33
|
+
|
|
34
|
+
3. **AIO prompt sampling** `DATA_getAiPromptsByTarget`
|
|
35
|
+
- Sample LLM prompts where the target URL's domain appears as a source.
|
|
36
|
+
- Cross-reference with the candidate URL — does it show up in any sampled prompts?
|
|
37
|
+
|
|
38
|
+
3b. **GA4 organic traffic on the audited URL** *(only if google-api.json is present, tier ≥ 2)*
|
|
39
|
+
- Replaces the implicit traffic estimation with actual measured organic sessions for the audited URL.
|
|
40
|
+
- Pull the top organic landing pages (last 28 days):
|
|
41
|
+
`upstream script excluded; use optbrew capability routing top-pages --days 28 --json`
|
|
42
|
+
- Filter the result client-side for the audited URL's path. Surface in `VERDICT.md` "## Snapshot" alongside the existing AIO citation cross-check:
|
|
43
|
+
- GA4 organic last 28d: `{sessions} sessions / {users} users / avg engagement {n}s`
|
|
44
|
+
- If the URL doesn't appear in the top-100 organic landing pages: "GA4: not in top-100 organic landing pages last 28d — low or zero traffic."
|
|
45
|
+
- **This is a signal, not a veto.** Low GA4 traffic on a YMYL page with high E-E-A-T is informative ("we're not earning the visibility our content quality should support") but doesn't change the publish decision.
|
|
46
|
+
- See `skills/seo-google/references/cross-skill-integration.md` § "seo-content-audit" for the full recipe.
|
|
47
|
+
|
|
48
|
+
4. **Score E-E-A-T** using `references/core-eeat.md`
|
|
49
|
+
- 60-item rubric across 4 dimensions (15 items each).
|
|
50
|
+
- Per-item: yes/no/partial. Compute dimension scores (0–100% each).
|
|
51
|
+
- Score the 8-item **AI-content markers** subsection (see references/core-eeat.md → "AI-content markers"). Mark each fired/not-fired.
|
|
52
|
+
- Apply 4 veto checks. Any veto = no-publish.
|
|
53
|
+
1. Anonymous author on YMYL topic. (High-confidence with Firecrawl-recovered author schema + DOM byline; medium-confidence with prose-only inspection.)
|
|
54
|
+
2. Factual claims with no sources cited.
|
|
55
|
+
3. Undisclosed affiliate / sponsored relationships.
|
|
56
|
+
4. AI-generated YMYL content with no human-review markers (≥4 AI-content markers fired AND YMYL topic AND no editor byline / "reviewed by" credit / "last reviewed" or "fact-checked on" date). The "editor byline / reviewed by" check uses Firecrawl-recovered DOM byline + Article-schema `author` when available; falls back to prose-level pattern match if Firecrawl is absent (lower confidence — note in `VERDICT.md`).
|
|
57
|
+
|
|
58
|
+
5. **Score CITE** using `references/cite.md`
|
|
59
|
+
- 30-item CITE rubric (Clear answer in 1st 200 words, Include primary stats, Timestamp freshness, Entity authority).
|
|
60
|
+
- Per-item: yes/no/partial. Compute dimension scores.
|
|
61
|
+
- Apply 3 veto checks (no answer in first 300 words / no datestamp on time-sensitive content / no entity disambiguation for proper-noun queries).
|
|
62
|
+
|
|
63
|
+
6. **Cross-check against AIO winners**
|
|
64
|
+
- For the patterns characteristic of cited sources (from step 2), evaluate the candidate against each: does it have what the cited sources have?
|
|
65
|
+
- Surface specific gaps.
|
|
66
|
+
|
|
67
|
+
7. **Synthesise verdict** using `templates/verdict.md`
|
|
68
|
+
- **Publish:** E-E-A-T ≥ 75%, CITE ≥ 70%, no vetoes triggered.
|
|
69
|
+
- **Publish with fixes:** E-E-A-T 60–74% OR CITE 55–69%, no vetoes. Top 5 fixes specified.
|
|
70
|
+
- **No publish:** any veto triggered, OR E-E-A-T < 60%, OR CITE < 55%. Substantial rewrite needed.
|
|
71
|
+
|
|
72
|
+
## Output format
|
|
73
|
+
|
|
74
|
+
Create a folder `seo-content-audit-{target-slug}-{YYYYMMDD}/` with:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
seo-content-audit-{target-slug}-{YYYYMMDD}/
|
|
78
|
+
├── VERDICT.md (publish / publish-with-fixes / no-publish — primary deliverable; inlines content snapshot + AIO context)
|
|
79
|
+
├── 03-eeat-scoring.md (60-item rubric scored — load-bearing reference an editor consults item-by-item)
|
|
80
|
+
├── 04-cite-scoring.md (30-item rubric scored — load-bearing reference)
|
|
81
|
+
├── 05-aio-winner-comparison.md (gap vs cited sources — must remain top-level; live AIO competitive evidence per EVAL_RESULT_v2.md §9)
|
|
82
|
+
└── evidence/
|
|
83
|
+
├── 01-content-snapshot.md (HTML extracts + page metadata — raw step output)
|
|
84
|
+
└── 02-aio-context.md (AIO presence, citations, patterns — raw step output)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Step files 01 + 02 are inlined as a "Snapshot" / "AIO context" section in `VERDICT.md`; the copies in `evidence/` preserve raw step output. `03-eeat-scoring.md`, `04-cite-scoring.md`, and `05-aio-winner-comparison.md` stay at top level — editors consult the rubric scoring detail directly, and the AIO winner comparison is the live competitive evidence the rubric verdict rests on.
|
|
88
|
+
|
|
89
|
+
`VERDICT.md` follows this shape (also see `templates/verdict.md`):
|
|
90
|
+
|
|
91
|
+
```markdown
|
|
92
|
+
# Content Audit: {URL or title}
|
|
93
|
+
|
|
94
|
+
> Audited {YYYY-MM-DD} · Target keyword: "{keyword}" · Country: {country}
|
|
95
|
+
|
|
96
|
+
## Verdict: {PUBLISH | PUBLISH WITH FIXES | NO PUBLISH}
|
|
97
|
+
|
|
98
|
+
{One sentence summary of why}
|
|
99
|
+
|
|
100
|
+
## Scores
|
|
101
|
+
|
|
102
|
+
| Dimension | Score | Threshold | Status |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| Experience | {n}% | 75% | {✓/✗} |
|
|
105
|
+
| Expertise | {n}% | 75% | {✓/✗} |
|
|
106
|
+
| Authoritativeness | {n}% | 75% | {✓/✗} |
|
|
107
|
+
| Trustworthiness | {n}% | 75% | {✓/✗} |
|
|
108
|
+
| **E-E-A-T composite** | {n}% | 75% | {✓/✗} |
|
|
109
|
+
| Clear answer | {n}% | 70% | {✓/✗} |
|
|
110
|
+
| Include stats | {n}% | 70% | {✓/✗} |
|
|
111
|
+
| Timestamp | {n}% | 70% | {✓/✗} |
|
|
112
|
+
| Entity authority | {n}% | 70% | {✓/✗} |
|
|
113
|
+
| **CITE composite** | {n}% | 70% | {✓/✗} |
|
|
114
|
+
|
|
115
|
+
## Veto checks
|
|
116
|
+
|
|
117
|
+
- Anonymous author on YMYL: {triggered / not triggered}
|
|
118
|
+
- Unsourced factual claims: {triggered / not triggered}
|
|
119
|
+
- Undisclosed affiliate / sponsored: {triggered / not triggered}
|
|
120
|
+
- AI-generated YMYL with no human review: {triggered / not triggered} ({n}/8 AI-content markers fired)
|
|
121
|
+
- ...
|
|
122
|
+
|
|
123
|
+
## AI Search readiness
|
|
124
|
+
- AIO present for "{keyword}": {yes/no}
|
|
125
|
+
- Top citation patterns: {list}
|
|
126
|
+
- Candidate URL cited in any sampled AIO: {yes/no}
|
|
127
|
+
- Gap vs cited sources: {bulleted gaps}
|
|
128
|
+
|
|
129
|
+
## Snapshot (measured)
|
|
130
|
+
- GA4 organic last 28d: {sessions} sessions / {users} users / avg engagement {n}s *(or `not in top-100` / `not configured (Tier 2 required)`)*
|
|
131
|
+
|
|
132
|
+
## Top 5 fixes
|
|
133
|
+
|
|
134
|
+
1. {Specific fix linked to a low-scored item or veto}
|
|
135
|
+
2. ...
|
|
136
|
+
5. ...
|
|
137
|
+
|
|
138
|
+
## Detailed scoring
|
|
139
|
+
|
|
140
|
+
See:
|
|
141
|
+
- 03-eeat-scoring.md (item-by-item E-E-A-T)
|
|
142
|
+
- 04-cite-scoring.md (item-by-item CITE)
|
|
143
|
+
- 05-aio-winner-comparison.md (gap analysis)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Tips
|
|
147
|
+
|
|
148
|
+
- Respect rate limit. AIO + AIO-prompts queries are ~5–10 calls; plenty of headroom.
|
|
149
|
+
- Call `DATA_getCreditBalance` before running. ~10–15 SE Ranking credits typical, plus 1 Firecrawl credit per URL audited when Firecrawl is installed (default cap 50 URLs).
|
|
150
|
+
- The thresholds (75% E-E-A-T, 70% CITE) are starting points. Tune per domain — a YMYL site (medical, financial) should require higher (85%/80%); a general-interest blog can run lower (65%/60%).
|
|
151
|
+
- The veto checks are not negotiable. A piece with anonymous authorship on a YMYL topic doesn't pass regardless of score.
|
|
152
|
+
- For pieces that score "publish with fixes," the top-5 list is the deliverable. Hand it to the writer; re-audit after fixes.
|
|
153
|
+
- Pair with `seo-content-brief` for the new-article counterpart: this skill audits existing content, content-brief produces new content.
|
|
154
|
+
- Pair with `seo-sxo` if the page has technical/page-type issues — that's a different diagnosis.
|
|
155
|
+
- The 60-item E-E-A-T rubric and 30-item CITE rubric are in `references/`. They are opinionated — adjust for your domain's editorial standards.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# CITE rubric (30 items)
|
|
2
|
+
|
|
3
|
+
CITE = Clear answer, Include primary stats, Timestamp freshness, Entity authority. Tuned for AI-search citation readiness — passages from pages with high CITE scores are the ones LLMs cite.
|
|
4
|
+
|
|
5
|
+
## Veto items (any one triggers automatic NO PUBLISH)
|
|
6
|
+
|
|
7
|
+
1. **No clear answer in the first 300 words.** LLMs scan early paragraphs; if the answer isn't there, the page won't be cited.
|
|
8
|
+
2. **No datestamp on time-sensitive content.** "Best X 2024" published in 2026 with no last-updated indicator gets passed over.
|
|
9
|
+
3. **No entity disambiguation for proper-noun queries.** A page about "Apple" that doesn't specify whether it's Apple Inc. (computers) or Apple Corp (Beatles) loses to clearer pages.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Clear answer (8 items)
|
|
14
|
+
|
|
15
|
+
The answer is findable, fast, and direct.
|
|
16
|
+
|
|
17
|
+
1. The primary question / topic of the piece is answered in the first 200 words.
|
|
18
|
+
2. The first paragraph is ≤ 60 words and stands alone as a TL;DR.
|
|
19
|
+
3. The answer is stated in declarative sentences, not buried in prose ("X is …", not "many people wonder whether X might be…").
|
|
20
|
+
4. Headings preview the answer ("How long does X take?" → "X takes 4–6 weeks", not "Let's explore the timeline").
|
|
21
|
+
5. Lists, tables, or step blocks present the answer in scannable form where applicable.
|
|
22
|
+
6. Numbers, dates, and named entities appear in the answer (specifics that LLMs can extract).
|
|
23
|
+
7. The answer is followed by supporting context, not the other way round.
|
|
24
|
+
8. No clickbait deferral ("read on to find out…"). The answer is given, then explained.
|
|
25
|
+
|
|
26
|
+
## 2. Include primary stats (8 items)
|
|
27
|
+
|
|
28
|
+
The piece references concrete data, with attribution.
|
|
29
|
+
|
|
30
|
+
1. At least one statistic, percentage, or number cited in the first half.
|
|
31
|
+
2. Sources are linked inline next to the stat (not a footnote pile at the end).
|
|
32
|
+
3. Sources are primary (the original study, government data, the company's own report) — not aggregator sites.
|
|
33
|
+
4. Date of the data is stated near the stat ("per the 2025 X report").
|
|
34
|
+
5. Multiple stats triangulate the same claim where possible (one source, then a corroborating source).
|
|
35
|
+
6. Methodology of cited data is briefly contextualised (sample size, population, time window).
|
|
36
|
+
7. Original data the author generated (survey, analysis, A/B test) is presented with methodology.
|
|
37
|
+
8. Where data is contested, that's acknowledged; the piece doesn't cherry-pick.
|
|
38
|
+
|
|
39
|
+
## 3. Timestamp freshness (7 items)
|
|
40
|
+
|
|
41
|
+
The reader (and LLM) can tell when this piece is from.
|
|
42
|
+
|
|
43
|
+
1. Original publish date visible on the page.
|
|
44
|
+
2. Last-updated date visible (and different from publish date if updated).
|
|
45
|
+
3. Year appears in title, URL, or first H1 if the topic is time-sensitive.
|
|
46
|
+
4. Schema.org `datePublished` and `dateModified` populated correctly (ISO 8601).
|
|
47
|
+
5. Time-sensitive claims (e.g., "current pricing," "best as of") have explicit dates.
|
|
48
|
+
6. Updates log visible if the piece has been substantively revised.
|
|
49
|
+
7. Old data points (>2 years) are flagged or replaced.
|
|
50
|
+
|
|
51
|
+
## 4. Entity authority (7 items)
|
|
52
|
+
|
|
53
|
+
Proper nouns, brands, people, places are disambiguated and authoritative.
|
|
54
|
+
|
|
55
|
+
1. The primary entity (brand, product, person, place) is named with full identifier (e.g., "Apple Inc.", not just "Apple") at first mention.
|
|
56
|
+
2. Wikipedia / Wikidata disambiguation is referenced or implicit through context.
|
|
57
|
+
3. Schema.org markup includes the entity (`Organization`, `Person`, `Product`, `Place`) with `sameAs` links to authoritative profiles.
|
|
58
|
+
4. Author byline links to a profile that establishes the author's expertise on this entity.
|
|
59
|
+
5. Brand name is consistent across the piece (no informal abbreviations that confuse LLMs).
|
|
60
|
+
6. Related entities are introduced with their own brief disambiguation (especially for B2B / niche topics).
|
|
61
|
+
7. Citations include canonical entity references (e.g., DOI for papers, official URLs for products).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## How to score
|
|
66
|
+
|
|
67
|
+
For each item, mark ✓ (1), ~ (0.5), or ✗ (0). Dimension score = sum / item-count × 100%. CITE composite = average of 4 dimensions.
|
|
68
|
+
|
|
69
|
+
## Threshold
|
|
70
|
+
|
|
71
|
+
- CITE composite ≥ 70% AND no veto = pass for citation-readiness.
|
|
72
|
+
- 55–69% = "publish with fixes" — improve the lowest-scoring dimension first.
|
|
73
|
+
- < 55% OR any veto = the piece will likely not be cited by AI search; substantial rework needed.
|
|
74
|
+
|
|
75
|
+
## Why this matters in 2026
|
|
76
|
+
|
|
77
|
+
LLM citation behavior consistently rewards passage-level extractability. Pages that score well on Clear answer + Include primary stats are the ones that get pulled into AIO, Perplexity, ChatGPT, and Gemini answers. CITE is a working approximation of what those models prefer; it's not a guarantee, but the correlation is strong.
|
|
78
|
+
|
|
79
|
+
## Calibration tips
|
|
80
|
+
|
|
81
|
+
- For news content, Timestamp freshness items 1, 2, 3, 5 should all be ✓ — undated news is dead.
|
|
82
|
+
- For evergreen topics, Timestamp items 5–7 are about confirming the freshness window, not chasing dates.
|
|
83
|
+
- For brand-monitoring content (where citation matters most), Entity authority should be ≥ 80%.
|
|
84
|
+
- For commercial content, Include primary stats lifts pieces from "marketing-fluff" to "decision-grade" — invest here.
|