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
https://tech-seo.de/api/mcp/mcphttps://tech-seo.de/api/v1/checks/{id}https://tech-seo.de/api/v1/checkshttps://tech-seo.de/llms.txtDiscovery 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):
{
"mcpServers": {
"tech-seo": {
"url": "https://tech-seo.de/api/mcp/mcp"
}
}
}Clients that only speak stdio can use 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.
{
"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 →
| Field | Type | Default | Description |
|---|---|---|---|
| domain * | string | Hostname 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. | |
| path | string | "/" | Path to test variants for, e.g. "/blog/". Default "/". |
| includePathVariants | boolean | true | Also test trailing slash, uppercase, /index.html, /index.php and query string variants on the canonical host. |
| userAgent | string | "googlebot_smartphone" | UA preset id (e.g. "googlebot_smartphone", "chrome_desktop") or a raw UA string. |
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 →
| Field | Type | Default | Description |
|---|---|---|---|
| url * | uri | Absolute http(s) URL to inspect. | |
| userAgents | array 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. |
| followRedirects | boolean | true | Follow redirects (max 5 hops) and compare the final responses. |
| includeBodySignals | boolean | true | Also parse title, canonical, meta robots and h1 from the HTML to detect UA-dependent differences. |
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 →
| Field | Type | Default | Description |
|---|---|---|---|
| url * | uri | Page URL to evaluate. The robots.txt of its origin is fetched automatically. | |
| checkAssetHosts | boolean | true | Also fetch robots.txt of CDN/asset hosts referenced in the HTML (max 5) and check whether Googlebot may load the page CSS/JS. |
| bots | array of string | Bot tokens to evaluate, e.g. ["Googlebot", "GPTBot"]. Default: full catalog (search engines, AI training and AI retrieval crawlers). |
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 →
| Field | Type | Default | Description |
|---|---|---|---|
| source * | string | Domain (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. |
| sampleSize | integer (10..300) | 100 | Number of sitemap URLs to fetch and check in sample mode (10-300). |
| userAgent | string | "googlebot_smartphone" | UA preset id or raw UA string used for all requests. |
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 →
| Field | Type | Default | Description |
|---|---|---|---|
| url * | uri | Any URL of the language cluster to start from. | |
| includeSitemap | boolean | false | Also read xhtml:link hreflang annotations from the XML sitemaps of the origin. |
| maxUrls | integer (2..100) | 40 | Maximum number of cluster URLs to fetch (2-100). |
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 →
| Field | Type | Default | Description |
|---|---|---|---|
| entries | array of object | IP with optional user agent. The user agent decides which bot is claimed. | |
| ips | array of string | IPs without user agent: the check reports which known crawler operator, if any, they belong to. | |
| log | string | Raw access log lines (max 1 MB, 5000 lines). Only IP, user agent and optionally path are read. | |
| logFormat | "auto" | "combined" | "common" | "jsonl" | "auto" | |
| bots | array of string | Limit to bot ids from the registry (see list_bots). Default: all. | |
| onlyClaimedBots | boolean | true | Log mode: only verify lines whose user agent claims a known bot. |
| methods | array of "ip_ranges" | "rdns" | ["ip_ranges","rdns"] | |
| includePaths | boolean | false | Log mode: include the top 5 requested paths per spoofed IP. |
| maxListItems | integer (10..500) | 200 |
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 warnbot.wrong_bot· warnbot.inconclusive· warnbot.unverifiable_claims· infobot.operator_level_only· infobot.method_conflict· infobot.ips_are_cdn· fail from 50% of claimed IPs, otherwise warnbot.private_ips· infobot.no_claims_found· warnbot.unparsed_lines· warnbot.input_truncated· warnbot.ranges_stale· warnbot.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 →
| Field | Type | Default | Description |
|---|---|---|---|
| url * | uri | Absolute http(s) URL to fetch raw and rendered. | |
| userAgent | string | "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" | |
| renderTimeoutMs | integer (3000..25000) | 15000 | |
| settleMs | integer (0..5000) | 1000 | |
| expandViewport | boolean | true | Grow the viewport to the page height, capped at 20000px. |
| blockResourceTypes | array of string | ["image","media","font"] | |
| sections | array of "head" | "content" | "links" | "structuredData" | "images" | "network" | "console" | ["head","content","links","structuredData","images","network","console"] | |
| maxListItems | integer (10..2000) | 300 | |
| noCache | boolean | false | Bypass the 10 minute cache. Counts twice against render limits. |
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· warnrender.timeout· warnrender.noindex_in_raw· warnrender.noindex_removed_by_js· failrender.noindex_added_by_js· failrender.robots_changed· warnrender.canonical_changed· failrender.canonical_removed_by_js· failrender.canonical_added_by_js· warnrender.canonical_multiple· warnrender.canonical_in_body· warnrender.title_missing_raw· warnrender.title_changed· warnrender.description_missing_raw· warnrender.description_changed· inforender.hreflang_changed· warnrender.lang_changed· inforender.js_navigation· warnrender.status_mismatch· warnrender.content_js_dependent· fail below 0.5, otherwise warn below 0.8render.h1_missing_raw· warnrender.h1_changed· warnrender.links_only_rendered· warn from 20%, otherwise inforender.links_removed_by_js· warnrender.non_crawlable_links· warnrender.form_post_navigation· inforender.link_rel_changed· warnrender.structured_data_only_rendered· warnrender.structured_data_removed_by_js· warnrender.structured_data_parse_error· failrender.structured_data_changed· inforender.lazy_images_js_only· warn from 30%, otherwise inforender.images_missing_alt· inforender.resources_blocked_by_robots· fail for script/xhr/fetch, warn for stylesheetrender.failed_requests· warnrender.page_errors· warnrender.console_errors· inforender.request_limit_hit· warnrender.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.