API Documentation
Authenticate every request with your API key in the X-API-Key header (or Authorization: Bearer). Base URL: https://seoscope.dev.
Audit a single URL
Three ways to call the same endpoint. Pick your stack.
curl "https://seoscope.dev/api/v1/audit?url=https://example.com" \
-H "X-API-Key: seo_your_key_here"const res = await fetch(
"https://seoscope.dev/api/v1/audit?url=" + encodeURIComponent("https://example.com"),
{ headers: { "X-API-Key": "seo_your_key_here" } }
);
const audit = await res.json();
console.log(audit.score, audit.checks);import requests
res = requests.get(
"https://seoscope.dev/api/v1/audit",
params={"url": "https://example.com"},
headers={"X-API-Key": "seo_your_key_here"},
timeout=30,
)
audit = res.json()
print(audit["score"], audit["checks"])Bulk audit (Premium+, max 10 URLs)
curl -X POST "https://seoscope.dev/api/v1/audit" \
-H "X-API-Key: seo_your_key_here" \
-H "Content-Type: application/json" \
-d '{"urls":["https://example.com","https://example.com/pricing"]}'Export as CSV or JSON
Add ?format=csv to download a spreadsheet-ready file (one row per check). Omit it (or use ?format=json) for the default JSON body. Works on the audit endpoint and on your dashboard request history.
# Audit result as CSV
curl "https://seoscope.dev/api/v1/audit?url=https://example.com&format=csv" \
-H "X-API-Key: seo_your_key_here" -o audit.csv
# Your recent API requests as CSV
curl "https://seoscope.dev/api/dashboard/requests?format=csv" \
-H "X-API-Key: seo_your_key_here" -o requests.csvCompetitor compare (Premium+)
Audit two URLs in parallel and get a side-by-side diff: scores, per-category breakdown, and exactly which checks each site is missing that the other passes.
curl -X POST "https://seoscope.dev/api/v1/audit/compare" \
-H "X-API-Key: seo_your_key_here" \
-H "Content-Type: application/json" \
-d '{"urlA":"https://example.com","urlB":"https://competitor.com"}'{
"a": { "url": "https://example.com/", "score": 86, "categories": { ... } },
"b": { "url": "https://competitor.com/", "score": 72, "categories": { ... } },
"comparison": {
"winner": "a",
"scores": { "a": 86, "b": 72 },
"gaps": {
"aShouldFix": [],
"bShouldFix": ["Meta description", "Open Graph tags"]
}
}
}Webhook callbacks (Premium+)
Register a callback URL to receive signed POSTs. Manage your config with GET / POST / DELETE on /api/v1/webhooks. The signing secret is returned once on creation — store it.
curl -X POST "https://seoscope.dev/api/v1/webhooks" \
-H "X-API-Key: seo_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url":"https://yourapp.com/hooks/seoscope","events":["audit.completed"]}'Every delivery is signed with HMAC-SHA256 over {timestamp}.{body}. Verify it in your handler:
import hmac, hashlib
def verify(secret, body_bytes, sig_header, ts_header):
expected = hmac.new(
secret.encode(),
f"{ts_header}.".encode() + body_bytes,
hashlib.sha256,
).hexdigest()
received = sig_header.removeprefix("sha256=")
return hmac.compare_digest(expected, received)
# headers: X-SEOScope-Signature, X-SEOScope-TimestampEmbeddable SEO score badge
Drop a live SEO score badge for any URL onto a site or README — no API key required. The score is cached for an hour. Perfect for agencies showcasing client sites.
<a href="https://seoscope.dev">
<img src="https://seoscope.dev/api/widget?url=https://example.com"
alt="SEO score by SEOScope" height="20" />
</a>[](https://seoscope.dev)Live preview:
Encrypted secret vault (BYOK)
Attach a third-party secret to your account without us ever seeing it in the clear. Encrypt it in your browser with AES-256-GCM (a passphrase only you know), then upload the resulting envelope — we store ciphertext only and can never decrypt it. GET reports whether a secret is configured (never the value);DELETE removes it.
curl -X POST "https://seoscope.dev/api/v1/secrets" \
-H "X-API-Key: seo_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3c…" \
-d '{"envelope":{"v":1,"salt":"…","iv":"…","keyIv":"…","wrappedKey":"…","ciphertext":"…","iter":210000}}'Versioning, request IDs & health
Every /api/v1 response carries X-API-Version: 1 and a uniqueX-Request-Id (echoed from your inbound header if you set one) for correlation in support tickets. Mutating endpoints accept an optionalIdempotency-Key header so a retried request never double-applies. Deprecation policy: a removed endpoint is announced 90 days in advance via anX-API-Deprecation header. A public, secret-free liveness probe lives at:
curl "https://seoscope.dev/api/health"
# → { "status": "ok", "version": "…", "time": "…", "uptimeSeconds": 0 }Audit response schema
{
"url": "https://example.com/",
"score": 86,
"categories": { "content": 90, "meta": 100, "social": 75, "technical": 80 },
"checks": [
{ "id": "title", "label": "Title tag", "category": "meta",
"status": "pass", "severity": "info", "detail": "Title is 48 characters.", "fix": "" },
{ "id": "og-tags", "label": "Open Graph tags", "category": "social",
"status": "warn", "severity": "medium", "detail": "Missing og:image.",
"fix": "Add the missing Open Graph tag(s) in <head> so social shares render a rich card." }
],
"page": {
"title": "Example Domain", "description": "...",
"wordCount": 742, "internalLinks": 12, "externalLinks": 5,
"imagesTotal": 8, "imagesWithAlt": 8,
"weight": { "htmlBytes": 38211, "scripts": 4, "stylesheets": 2,
"inlineStyleBytes": 0, "images": 8, "totalResources": 14 },
"brokenLinks": []
},
"fetchedAt": "2026-06-19T12:00:00.000Z"
}Every check returns pass, warn or fail with aseverity (critical → info) and a concrete fixstring. Audits cover titles & meta (length + duplicates), headings, schema.org JSON-LD, Open Graph/Twitter, canonical & robots (incl. X-Robots-Tag), hreflang, internal links & broken-link detection, image alt coverage and page-weight signals. Broken-link probing runs on Premium and higher.