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 score alone. 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.h1 should almost always be 1. 0 means the page has no H1; >1 dilutes semantic importance.
  • images.missing_alt_srcs is capped at 10 examples; images.without_alt is the true count.
  • canonical is 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 a 400.
  • Fetches follow redirects; url in 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-audit for that.