SEO Audit¶
Fetch a URL and return a structured, machine-readable SEO audit: title and description quality, Open Graph / Twitter Card coverage, heading hierarchy, image alt-text stats, canonical, robots, lang, viewport, and an aggregate 0–100 score.
This is the single-page equivalent of a "Lighthouse SEO" breakdown — suitable for showing inside a dashboard, driving a CI check on preview deploys, or batch-auditing your sitemap against a threshold.
For multi-URL runs, see /v1/seo/bulk-audit. For just the broken-link check, see /v1/seo/broken-links.
Endpoint¶
GET /v1/seo/audit
Base URL: https://seo.toolkitapi.io
Query Parameters¶
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Absolute URL to audit. Must be http:// or https://. |
Response Fields¶
| Field | Type | Description |
|---|---|---|
url |
string | The final URL audited (after any redirects). |
status_code |
integer | HTTP status returned when fetching the page. |
score |
integer (0–100) | Overall SEO score. |
meta_title |
object | {present, value, length, is_good_length}. is_good_length = 30–60 chars. |
meta_description |
object | {present, value, length, is_good_length}. is_good_length = 120–160. |
og_tags |
object | Values for og:title, og:description, og:image, og:url, og:type, og:site_name (each null if missing). |
twitter_card |
object | Values for twitter:card, twitter:title, twitter:description, twitter:image, twitter:site. |
headings |
object | Counts per tag: {h1, h2, h3, h4, h5, h6}. |
heading_hierarchy |
string[] | Headings in document order, each "h2: Some text" (truncated to 80 chars). |
images |
object | {total, with_alt, without_alt, missing_alt_srcs}. Up to 10 missing-alt srcs are returned. |
canonical |
string | null | Value of <link rel="canonical">. |
robots_meta |
string | null | Value of <meta name="robots">. |
lang |
string | null | <html lang="..."> attribute. |
viewport |
string | null | <meta name="viewport"> content. |
Examples¶
curl¶
curl -G "https://seo.toolkitapi.io/v1/seo/audit" \
-H "x-api-key: $TOOLKIT_API_KEY" \
--data-urlencode "url=https://example.com"
Python¶
import requests
resp = requests.get(
"https://seo.toolkitapi.io/v1/seo/audit",
params={"url": "https://example.com"},
headers={"x-api-key": API_KEY},
timeout=30,
)
audit = resp.json()
if audit["score"] < 70:
print(f"Low SEO score ({audit['score']}) on {audit['url']}")
if not audit["meta_description"]["present"]:
print(" - missing meta description")
if audit["headings"]["h1"] != 1:
print(f" - {audit['headings']['h1']} H1 tags (want exactly 1)")
if audit["images"]["without_alt"] > 0:
print(f" - {audit['images']['without_alt']} images missing alt text")
JavaScript¶
const params = new URLSearchParams({ url: "https://example.com" });
const resp = await fetch(
`https://seo.toolkitapi.io/v1/seo/audit?${params}`,
{ headers: { "x-api-key": process.env.TOOLKIT_API_KEY } },
);
const audit = await resp.json();
// Fail a preview-deploy CI check if score drops below threshold.
if (audit.score < 80) {
core.setFailed(`SEO score ${audit.score} below threshold on ${audit.url}`);
}
Errors¶
| Status | Condition |
|---|---|
400 |
url is malformed, unsupported scheme, or resolves to a disallowed/internal host (SSRF-blocked). |
401 |
Missing / invalid API key. |
422 |
url query parameter is missing. |
502 |
The target site could not be reached (DNS failure, connection refused, TLS error, etc.). |
Notes & gotchas¶
- Don't alert on
scorealone. It's a heuristic; a legitimate landing page may score 80 while a perfectly-crafted blog post scores 95. Use the structured fields (e.g. "does it have an H1?") for hard rules and reserve the score for trends. - SPAs with client-side rendering usually look empty to this endpoint — it reads the raw HTML response, not an executed DOM. Make sure your framework ships meta tags in the initial payload (SSR, static prerender, or Next's metadata API).
headings.h1should almost always be1.0means the page has no H1;>1dilutes semantic importance.images.missing_alt_srcsis capped at 10 examples;images.without_altis the true count.canonicalis critical for pages with query strings / UTM parameters — if it's missing, Google may index dozens of near-duplicates.- The toolkit enforces an allow-list of public hostnames (SSRF protection). You can't audit
http://localhost,http://169.254.169.254, RFC1918 space, or hosts that resolve into them — you'll get a400. - Fetches follow redirects;
urlin the response is the final landing URL, which may differ from what you sent. - Expect 1–5 s per request depending on target-page size. Run multiple audits in parallel; there is no built-in batching on this endpoint — use
/v1/seo/bulk-auditfor that.