API reference
Two endpoints, both GET, both JSON. The base URL is https://canonicalchecker.com.
Integrations authenticate with an API key. Keys start with cc_ and are issued by email: request a key. Treat it like a password.
| Header | Description |
|---|---|
X-API-Key | Your API key, e.g. X-API-Key: cc_…. Preferred. |
Authorization | Alternative: Authorization: Bearer cc_…. Used only when X-API-Key is absent. |
/api/check also answers requests without a key (that’s what the website uses). A key that is sent but invalid or revoked is always rejected with 401. /api/me requires a valid key.
# Either header works
curl https://canonicalchecker.com/api/me \
-H "X-API-Key: cc_…"
curl https://canonicalchecker.com/api/me \
-H "Authorization: Bearer cc_…"
Fetches the page live, follows redirects (up to 5), and runs the 7-point canonical audit plus robots and noindex checks. Responses are never cached (Cache-Control: no-store) and nothing is stored. A check usually takes one to a few seconds; each fetch times out after 10 seconds.
Query parameters
| Name | Description |
|---|---|
urlstring, required | The page to check: a full URL (https://example.com/page) or a bare domain (example.com). Without a scheme, https:// is assumed, with one automatic retry over http:// if the HTTPS connection fails. Only http and https are supported. Private IPs and internal hostnames are rejected. Remember to URL-encode it. |
Headers
Optional X-API-Key or Authorization: Bearer, see Authentication.
# Request
curl -G https://canonicalchecker.com/api/check \
-H "X-API-Key: cc_…" \
--data-urlencode url=example.com
Response 200 OK
| Field | Description |
|---|---|
inputstring | The url parameter as sent, trimmed. |
checkedUrlstring | The normalized URL that was requested (scheme added, fragment removed). |
finalUrlstring | The URL after following redirects. All checks run against this page. |
httpStatusinteger | HTTP status of finalUrl. |
redirectChainarray | Every hop, in order: { url, status, location } for redirects and { url, status } for the final page. One entry means no redirects. |
canonical.effectivestring | null | The canonical Google would use: the first valid <link rel="canonical"> in <head>, else the first Link: rel="canonical" header. null if none. |
canonical.source"html" | "header" | null | Where effective came from. |
canonical.htmlTagsarray | Every canonical <link> tag on the page: { href, inHead, error }. href is resolved to an absolute URL when possible; error is "empty href", "unparseable href", or null. |
canonical.headerValuesstring[] | Canonical URLs from the HTTP Link header, resolved. |
checksarray | Seven check results, always in the same order. See below. |
extrasobject | Indexability signals beyond the canonical. See below. |
verdict"pass" | "warn" | "fail" | Overall result: fail if any check fails, else warn if any check warns, else pass. extras don’t affect it. |
durationMsinteger | Time the check took, in milliseconds. |
# 200 OK (redirectChain and checks trimmed)
{
"input": "example.com",
"checkedUrl": "https://example.com/",
"finalUrl": "https://www.example.com/",
"httpStatus": 200,
"redirectChain": [
{ "url": "https://example.com/", "status": 301,
"location": "https://www.example.com/" },
{ "url": "https://www.example.com/", "status": 200 }
],
"canonical": {
"effective": "https://www.example.com/",
"source": "html",
"htmlTags": [
{ "href": "https://www.example.com/",
"inHead": true, "error": null }
],
"headerValues": []
},
"checks": [
{
"id": "declared",
"emoji": "🏷️",
"label": "Declared Canonical",
"status": "pass",
"summary": "Canonical declared via <link> tag.",
"detail": "https://www.example.com/"
},
{
"id": "selfRef",
"emoji": "🔁",
"label": "Self-Referencing",
"status": "pass",
"summary": "Canonical points to this exact URL.",
"detail": null
},
…
],
"extras": {
"robotsTxtBlocked": false,
"robotsTxtStatus": 200,
"metaRobots": null,
"noindex": false,
"xRobotsTag": null,
"pageStatus": 200,
"pageStatusIssue": null
},
"verdict": "pass",
"durationMs": 812
}
The checks array
Each item is { id, emoji, label, status, summary, detail }.
| Field | Description |
|---|---|
idstring | Stable identifier, one of the seven below. Use this, not label, in code. |
emojistring | Icon shown on the website. |
labelstring | Human-readable name of the check. |
status"pass" | "warn" | "fail" | "na" | na means the check didn’t apply, usually because no canonical was declared. |
summarystring | One-sentence, human-readable explanation of the result. |
detailstring | null | Supporting evidence when there is any: the URL involved, a from → to path, or the conflicting values. |
Check IDs
| id | What it tests |
|---|---|
declared | A canonical is declared in <head> or a Link header. Fails if missing, or only present outside <head>. |
selfRef | The canonical points to the page itself. Warns if it differs only by scheme, www or trailing slash, or points to a different URL. |
target | The canonical target resolves. Fails on 4xx/5xx or a fetch error; warns if the target redirects. |
loop | Following canonicals from the target (up to 3 hops) doesn’t loop. Fails on a loop; warns on a multi-hop chain. |
crossDom | The canonical stays on the same registrable domain. Warns on a cross-domain canonical. |
multiple | Exactly one canonical tag. Fails on conflicting tags; warns on duplicates or a tag outside <head>. |
conflict | The HTTP Link header and the <link> tag agree. Fails if they disagree. |
extras
| Field | Description |
|---|---|
robotsTxtBlockedboolean | true if robots.txt disallows finalUrl for Googlebot (or *). |
robotsTxtStatusinteger | null | HTTP status of /robots.txt; null if it couldn’t be fetched. |
metaRobotsstring | null | Content of the robots or googlebot meta tag, lowercased. |
noindexboolean | true if the meta robots tag or the X-Robots-Tag header contains noindex. |
xRobotsTagstring | null | The raw X-Robots-Tag response header. |
pageStatusinteger | HTTP status of finalUrl (same as httpStatus). |
pageStatusIssuestring | null | A warning when the page itself returns 4xx/5xx, else null. |
Errors return JSON with a single error string, written to be shown to a user as-is.
| Status | When |
|---|---|
400Bad Request | The url is missing or invalid, isn’t http/https, points to a private or internal address, doesn’t resolve, can’t be fetched (connection error or timeout), or redirects more than 5 times. Retrying the same URL won’t help until the site changes. |
401Unauthorized | An API key was sent but is invalid or revoked (/api/check), or no valid key was sent (/api/me). |
500Server Error | Something unexpected went wrong on our side. Safe to retry later. |
# 400
{ "error": "Please enter a full domain, e.g. example.com or https://example.com/page." }
# 401 from /api/check
{ "error": "Invalid API key." }
# 500
{ "error": "Unexpected error while checking. Please try again." }
Confirms that an API key is valid, without running a check. Integrations such as Zapier call it when you connect an account. Requires X-API-Key or Authorization: Bearer. Takes no parameters.
| Field | Description |
|---|---|
okboolean | Always true on success. |
labelstring | The name of the key’s owner, set when the key was issued. It never contains any part of the key. Keys issued without a name return "Canonical Checker API key". |
Without a valid key it returns 401 with { "error": "A valid API key is required." }.
# Request
curl https://canonicalchecker.com/api/me \
-H "X-API-Key: cc_…"
# 200 OK
{ "ok": true, "label": "Acme SEO team" }