API reference

Two endpoints, both GET, both JSON. The base URL is https://canonicalchecker.com.

Authentication

Integrations authenticate with an API key. Keys start with cc_ and are issued by email: request a key. Treat it like a password.

HeaderDescription
X-API-KeyYour API key, e.g. X-API-Key: cc_…. Preferred.
AuthorizationAlternative: 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_…"
Run a check
GET/api/check?url=…

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

NameDescription
urlstring, requiredThe 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

FieldDescription
inputstringThe url parameter as sent, trimmed.
checkedUrlstringThe normalized URL that was requested (scheme added, fragment removed).
finalUrlstringThe URL after following redirects. All checks run against this page.
httpStatusintegerHTTP status of finalUrl.
redirectChainarrayEvery hop, in order: { url, status, location } for redirects and { url, status } for the final page. One entry means no redirects.
canonical.effectivestring | nullThe 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" | nullWhere effective came from.
canonical.htmlTagsarrayEvery 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.
checksarraySeven check results, always in the same order. See below.
extrasobjectIndexability 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.
durationMsintegerTime 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 }.

FieldDescription
idstringStable identifier, one of the seven below. Use this, not label, in code.
emojistringIcon shown on the website.
labelstringHuman-readable name of the check.
status"pass" | "warn" | "fail" | "na"na means the check didn’t apply, usually because no canonical was declared.
summarystringOne-sentence, human-readable explanation of the result.
detailstring | nullSupporting evidence when there is any: the URL involved, a from → to path, or the conflicting values.

Check IDs

idWhat it tests
declaredA canonical is declared in <head> or a Link header. Fails if missing, or only present outside <head>.
selfRefThe canonical points to the page itself. Warns if it differs only by scheme, www or trailing slash, or points to a different URL.
targetThe canonical target resolves. Fails on 4xx/5xx or a fetch error; warns if the target redirects.
loopFollowing canonicals from the target (up to 3 hops) doesn’t loop. Fails on a loop; warns on a multi-hop chain.
crossDomThe canonical stays on the same registrable domain. Warns on a cross-domain canonical.
multipleExactly one canonical tag. Fails on conflicting tags; warns on duplicates or a tag outside <head>.
conflictThe HTTP Link header and the <link> tag agree. Fails if they disagree.

extras

FieldDescription
robotsTxtBlockedbooleantrue if robots.txt disallows finalUrl for Googlebot (or *).
robotsTxtStatusinteger | nullHTTP status of /robots.txt; null if it couldn’t be fetched.
metaRobotsstring | nullContent of the robots or googlebot meta tag, lowercased.
noindexbooleantrue if the meta robots tag or the X-Robots-Tag header contains noindex.
xRobotsTagstring | nullThe raw X-Robots-Tag response header.
pageStatusintegerHTTP status of finalUrl (same as httpStatus).
pageStatusIssuestring | nullA warning when the page itself returns 4xx/5xx, else null.
Errors

Errors return JSON with a single error string, written to be shown to a user as-is.

StatusWhen
400Bad RequestThe 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.
401UnauthorizedAn API key was sent but is invalid or revoked (/api/check), or no valid key was sent (/api/me).
500Server ErrorSomething 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." }
Test a key
GET/api/me

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.

FieldDescription
okbooleanAlways true on success.
labelstringThe 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" }