Skip to content
tech-seo.de

API

API and MCP docs

Every check is one function with one input schema. MCP tool, REST endpoint and web form all call the same code and return the same result. The machine-readable spec is /api/v1/openapi.json (OpenAPI 3.1).

Endpoints

Endpoints and limits

MCP (Streamable HTTP, stateless)
https://tech-seo.de/api/mcp/mcp
REST (POST with a JSON body)
https://tech-seo.de/api/v1/checks/{id}
Check list (GET)
https://tech-seo.de/api/v1/checks
llms.txt
https://tech-seo.de/llms.txt

Discovery also works via the MCP tool list_checks. Limits: 20 requests per 10 minutes per IP and 30 per 10 minutes per tested site (headers X-RateLimit-*, 429 with Retry-After). Each run is capped at 45 seconds; partial results come back with truncated: true.

MCP

Connect an MCP client

Clients with remote MCP support (Claude, ChatGPT connectors, Cursor, VS Code) take the URL directly. Example for Cursor (.cursor/mcp.json):

.cursor/mcp.json
{
  "mcpServers": {
    "tech-seo": {
      "url": "https://tech-seo.de/api/mcp/mcp"
    }
  }
}

Clients that only speak stdio can use mcp-remote:

stdio via mcp-remote
{
  "mcpServers": {
    "tech-seo": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://tech-seo.de/api/mcp/mcp"
      ]
    }
  }
}

Result

Result format

MCP tools return this object as structuredContent plus a short Markdown summary as text. REST returns it as the response body.

CheckResult
{
  "check": "redirect_matrix",
  "version": "1.0.0",
  "input": { ... },
  "startedAt": "2026-10-02T12:00:00.000Z",
  "durationMs": 2140,
  "requestCount": 12,
  "status": "pass" | "warn" | "fail" | "error",
  "summary": "1-2 sentences with numbers",
  "findings": [
    { "id": "redirect.302_in_chain", "severity": "fail", "message": "...",
      "evidence": { ... }, "recommendation": "..." }
  ],
  "data": { ... check-specific facts ... },
  "truncated": false
}

status is fail if any finding is fail, otherwise warn if any is warn, otherwise pass. error means the check could not run (DNS failure, invalid target). Finding ids are stable and safe to match on.

Redirect matrixredirect_matrix

Tests all protocol/host/path variants of a domain (http/https, www/non-www, trailing slash, uppercase, index files, query string) and reports every redirect hop with its status code. Use when asked whether a site's redirects are set up correctly or why a URL resolves inconsistently. Returns pass/warn/fail with findings and the full hop list per variant. Takes 3 to 10 seconds.

~6 s · up to 120 requests · Try it in the browser →

FieldTypeDefaultDescription
domain *stringHostname without protocol, e.g. "example.com". A www. prefix is stripped and both variants are tested. A full URL is accepted; only its host is used.
pathstring"/"Path to test variants for, e.g. "/blog/". Default "/".
includePathVariantsbooleantrueAlso test trailing slash, uppercase, /index.html, /index.php and query string variants on the canonical host.
userAgentstring"googlebot_smartphone"UA preset id (e.g. "googlebot_smartphone", "chrome_desktop") or a raw UA string.
POST /api/v1/checks/redirect_matrix
curl -X POST https://tech-seo.de/api/v1/checks/redirect_matrix \
  -H 'content-type: application/json' \
  -d '{"domain":"example.com"}'

Header inspectorheader_inspector

Requests one URL with several user agents (browser, Googlebot, Bingbot, AI crawlers) and compares status codes, raw HTTP headers (X-Robots-Tag, Link canonical, caching, Vary, compression) and HTML head signals per UA. Use when asked whether bots see the same page as users, whether a site blocks AI crawlers, or which headers a URL really sends. Returns pass/warn/fail with findings, captured headers per UA and a diff matrix. Takes 2 to 6 seconds.

~4 s · up to 60 requests · Try it in the browser →

FieldTypeDefaultDescription
url *uriAbsolute http(s) URL to inspect.
userAgentsarray of string["chrome_desktop","googlebot_smartphone","gptbot","claudebot"]UA preset ids (chrome_desktop, chrome_mobile, googlebot_desktop, googlebot_smartphone, bingbot, gptbot, oai_searchbot, chatgpt_user, claudebot, claude_user, perplexitybot, perplexity_user, techseobot, none) or raw UA strings. Max 8. The first browser UA (or the first entry) is the reference.
followRedirectsbooleantrueFollow redirects (max 5 hops) and compare the final responses.
includeBodySignalsbooleantrueAlso parse title, canonical, meta robots and h1 from the HTML to detect UA-dependent differences.
POST /api/v1/checks/header_inspector
curl -X POST https://tech-seo.de/api/v1/checks/header_inspector \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com/"}'

robots.txt analyzerrobots_analyzer

Fetches and parses the robots.txt of a URL's origin exactly like Google (RFC 9309: most specific group, longest match, allow wins ties, HTTP status rules) and evaluates the URL for search engines and AI crawlers (Googlebot, Bingbot, GPTBot, ClaudeBot, PerplexityBot, Google-Extended and more). Also checks noindex vs. robots conflicts, whether CDN hosts block CSS/JS for Googlebot, and whether Sitemap directives resolve. Use when asked whether a page or bot is blocked, or why Google cannot see a noindex. Returns per-bot allow/deny with the matched rule. Takes 2 to 6 seconds.

~4 s · up to 40 requests · Try it in the browser →

FieldTypeDefaultDescription
url *uriPage URL to evaluate. The robots.txt of its origin is fetched automatically.
checkAssetHostsbooleantrueAlso fetch robots.txt of CDN/asset hosts referenced in the HTML (max 5) and check whether Googlebot may load the page CSS/JS.
botsarray of stringBot tokens to evaluate, e.g. ["Googlebot", "GPTBot"]. Default: full catalog (search engines, AI training and AI retrieval crawlers).
POST /api/v1/checks/robots_analyzer
curl -X POST https://tech-seo.de/api/v1/checks/robots_analyzer \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com/"}'

Sitemap auditsitemap_audit

Loads all XML sitemaps of a domain (from robots.txt, sitemap indexes recursively, gzip supported), takes inventory (URL count, lastmod quality, duplicates, foreign hosts, http URLs, size limits) and checks a stratified sample of URLs for status 200, redirects, noindex, robots.txt blocking and self-canonical. Only URLs on the own site of the sitemap (same host or subdomains) are fetched; URLs on other hosts are counted, not fetched. Use when asked whether a sitemap is clean or why sitemap URLs are not indexed. Returns findings with counts extrapolated to the whole sitemap and examples. Takes 10 to 40 seconds.

~25 s · up to 400 requests · Try it in the browser →

FieldTypeDefaultDescription
source *stringDomain (e.g. "example.com"; sitemaps are taken from robots.txt, fallback /sitemap.xml) or the absolute URL of a sitemap or sitemap index.
mode"sample" | "full""sample""sample" checks a stratified sample of URLs synchronously. "full" (every URL as a background job) is not available yet.
sampleSizeinteger (10..300)100Number of sitemap URLs to fetch and check in sample mode (10-300).
userAgentstring"googlebot_smartphone"UA preset id or raw UA string used for all requests.
POST /api/v1/checks/sitemap_audit
curl -X POST https://tech-seo.de/api/v1/checks/sitemap_audit \
  -H 'content-type: application/json' \
  -d '{"source":"example.com"}'

hreflang validatorhreflang_validator

Builds the hreflang graph of a language cluster starting from one URL (HTML link tags, HTTP Link headers and optionally XML sitemaps), fetches every alternate URL and checks return links, self-references, ISO language/region codes, x-default, status 200, self-canonical and noindex on targets. Use when asked whether hreflang is implemented correctly or why the wrong language version ranks. Returns findings, the URL × language matrix and all edges with their source. Takes 3 to 20 seconds.

~8 s · up to 150 requests · Try it in the browser →

FieldTypeDefaultDescription
url *uriAny URL of the language cluster to start from.
includeSitemapbooleanfalseAlso read xhtml:link hreflang annotations from the XML sitemaps of the origin.
maxUrlsinteger (2..100)40Maximum number of cluster URLs to fetch (2-100).
POST /api/v1/checks/hreflang_validator
curl -X POST https://tech-seo.de/api/v1/checks/hreflang_validator \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com/"}'

Bot verifierbot_verifier

Verifies whether IP addresses really belong to the crawler their user agent claims (Googlebot, Bingbot, GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, Applebot and others) using the operators' published IP ranges and forward-confirmed reverse DNS. Accepts IPs, IP plus user agent pairs, or raw access log lines. Use when asked whether bot traffic in logs is real, how much of it is spoofed, or before allowlisting or blocking a crawler. Returns pass/warn/fail with findings, a verdict per IP and totals per bot. Takes 1 to 15 seconds.

~8 s · up to 0 requests · Try it in the browser →

FieldTypeDefaultDescription
entriesarray of objectIP with optional user agent. The user agent decides which bot is claimed.
ipsarray of stringIPs without user agent: the check reports which known crawler operator, if any, they belong to.
logstringRaw access log lines (max 1 MB, 5000 lines). Only IP, user agent and optionally path are read.
logFormat"auto" | "combined" | "common" | "jsonl""auto"
botsarray of stringLimit to bot ids from the registry (see list_bots). Default: all.
onlyClaimedBotsbooleantrueLog mode: only verify lines whose user agent claims a known bot.
methodsarray of "ip_ranges" | "rdns"["ip_ranges","rdns"]
includePathsbooleanfalseLog mode: include the top 5 requested paths per spoofed IP.
maxListItemsinteger (10..500)200
POST /api/v1/checks/bot_verifier
curl -X POST https://tech-seo.de/api/v1/checks/bot_verifier \
  -H 'content-type: application/json' \
  -d '{"ips":["66.249.66.1"]}'

Findings

  • bot.spoofed · fail from 20% of bot hits, otherwise warn
  • bot.wrong_bot · warn
  • bot.inconclusive · warn
  • bot.unverifiable_claims · info
  • bot.operator_level_only · info
  • bot.method_conflict · info
  • bot.ips_are_cdn · fail from 50% of claimed IPs, otherwise warn
  • bot.private_ips · info
  • bot.no_claims_found · warn
  • bot.unparsed_lines · warn
  • bot.input_truncated · warn
  • bot.ranges_stale · warn
  • bot.all_verified · info

Render diffrender_diff

Fetches one URL twice, as raw HTML and rendered in headless Chromium, and compares SEO signals: title, meta robots, canonical, hreflang, headings, main text, links, structured data and images. Use when asked whether content or links depend on JavaScript, whether search engines or AI crawlers that do not render see the page, or why JavaScript changes indexing signals. Returns pass/warn/fail with findings, both snapshots and a diff. Takes 8 to 30 seconds.

~18 s · up to 12 requests · Try it in the browser →

FieldTypeDefaultDescription
url *uriAbsolute http(s) URL to fetch raw and rendered.
userAgentstring"googlebot_smartphone"UA preset id or a raw user-agent string. Default googlebot_smartphone.
viewport"auto" | "mobile" | "desktop""auto"auto follows the user agent.
emulateRobotsTxt"auto" | "on" | "off""auto"auto is on for search, AI-training and AI-retrieval user agents.
waitFor"networkidle" | "load" | "domcontentloaded""networkidle"
renderTimeoutMsinteger (3000..25000)15000
settleMsinteger (0..5000)1000
expandViewportbooleantrueGrow the viewport to the page height, capped at 20000px.
blockResourceTypesarray of string["image","media","font"]
sectionsarray of "head" | "content" | "links" | "structuredData" | "images" | "network" | "console"["head","content","links","structuredData","images","network","console"]
maxListItemsinteger (10..2000)300
noCachebooleanfalseBypass the 10 minute cache. Counts twice against render limits.
POST /api/v1/checks/render_diff
curl -X POST https://tech-seo.de/api/v1/checks/render_diff \
  -H 'content-type: application/json' \
  -d '{"url":"https://example.com/"}'

Findings

  • render.not_renderable · warn
  • render.timeout · warn
  • render.noindex_in_raw · warn
  • render.noindex_removed_by_js · fail
  • render.noindex_added_by_js · fail
  • render.robots_changed · warn
  • render.canonical_changed · fail
  • render.canonical_removed_by_js · fail
  • render.canonical_added_by_js · warn
  • render.canonical_multiple · warn
  • render.canonical_in_body · warn
  • render.title_missing_raw · warn
  • render.title_changed · warn
  • render.description_missing_raw · warn
  • render.description_changed · info
  • render.hreflang_changed · warn
  • render.lang_changed · info
  • render.js_navigation · warn
  • render.status_mismatch · warn
  • render.content_js_dependent · fail below 0.5, otherwise warn below 0.8
  • render.h1_missing_raw · warn
  • render.h1_changed · warn
  • render.links_only_rendered · warn from 20%, otherwise info
  • render.links_removed_by_js · warn
  • render.non_crawlable_links · warn
  • render.form_post_navigation · info
  • render.link_rel_changed · warn
  • render.structured_data_only_rendered · warn
  • render.structured_data_removed_by_js · warn
  • render.structured_data_parse_error · fail
  • render.structured_data_changed · info
  • render.lazy_images_js_only · warn from 30%, otherwise info
  • render.images_missing_alt · info
  • render.resources_blocked_by_robots · fail for script/xhr/fetch, warn for stylesheet
  • render.failed_requests · warn
  • render.page_errors · warn
  • render.console_errors · info
  • render.request_limit_hit · warn
  • render.non_rendering_crawlers · info

list_bots

Lists crawler and fetcher bots with purpose, robots.txt token, verification methods and the age of each published IP list. MCP tool list_bots and GET /api/v1/bots. It does not contact the site you are discussing.