Check the canonical tag
A canonical tag that quietly points to the wrong URL is one of the fastest ways to watch a page vanish from search results while everything else looks fine. This endpoint reads the canonical declared on each submitted page, follows it, and tells you plainly whether it resolves where it should.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
The failure mode nobody notices
A canonical tag doesn't error, redirect, or throw a warning in a browser — it just tells a crawler which URL is the authoritative one, and if that URL is wrong, a perfectly good page can quietly self-cannibalize by declaring another page as its canonical, or worse, point to a URL that no longer exists. Because the page itself renders normally, teams often discover the problem only after rankings drop and someone finally checks the page source.
What gets checked
For each submitted URL, the task extracts the declared canonical link, normalizes both URLs (protocol, trailing slash, query parameters), and reports whether the canonical is self-referencing, points to a different URL, is missing entirely, or creates a chain — a canonical pointing to a page that itself canonicalizes elsewhere, which search engines generally refuse to follow past one hop. It also flags canonicals pointing to a URL that returns a non-200 status, since a canonical to a broken page is functionally the same as no canonical at all.
Why this tag exists at all
The rel=canonical attribute was introduced in 2009 specifically to solve duplicate content: the same product listed under three URL parameters, the same article reachable with and without a trailing slash, print versions sitting alongside the original. It works only when it's accurate — a canonical is a strong hint, not a redirect, so an incorrect one doesn't just fail to help, it can actively steer ranking signal to the wrong page.
Where it tends to go wrong
Faceted navigation and pagination are the two most common sources of canonical mistakes: a filtered product listing canonicalizing to the unfiltered page loses the filtered page's ability to rank for a specific query, while a paginated series that all canonicalizes to page one can cause page two and beyond to drop out of the index entirely. Multi-language sites add another layer, where a canonical accidentally pointing across language versions instead of to the same-language default is a frequent, hard-to-spot bug.
Automating the check
Because the endpoint is async, a full site's canonical map can be verified in one batch call, with the task_id returned immediately and the full report delivered to your webhook once the crawl completes. Running it after every deploy that touches templating logic catches canonical regressions before they cost a ranking cycle, at a cost low enough to run against a large URL set without a second thought.
What you can do with it
Faceted navigation audit
Verify that filtered and sorted product listing pages canonicalize correctly instead of all collapsing onto the base category page.
Post-deploy regression check
Run the checker right after a template change to catch a canonical that started pointing to a staging or old domain by mistake.
Duplicate content cleanup
Confirm that print-friendly, AMP, or parameterized versions of a page all canonicalize back to the single intended URL.
Cross-domain migration validation
During a domain move, check that canonicals on the new domain point to themselves rather than lingering on the old one.
FAQ
What does a canonical tag checker API actually verify?
It extracts the rel=canonical link from each submitted page, resolves and normalizes the target URL, and reports whether it's self-referencing, mismatched, missing, chained, or pointing to a broken URL.
Can it detect canonical chains?
Yes — if a canonical points to a page that itself canonicalizes elsewhere, the report flags the chain, which search engines typically won't follow past one hop.
Is a canonical the same as a redirect?
No. A canonical is a hint to search engines about the preferred URL; the page itself still loads normally, which is exactly why mismatches go unnoticed without a dedicated check.
How much does it cost per request?
$0.002 per request, charged only when the check completes successfully; failed fetches are retried up to three times and never billed.
Is there a free trial?
The tool above runs free in your browser. The API is paid — each call draws from your prepaid ForHosting KIT balance: top up from $10.00 (it never expires), pay each request's published price, and a call with no balance returns HTTP 402. No subscription, no tokens, and a failed task is never charged.
How do results come back?
Via signed webhook, which is recommended for automated pipelines, or a signed link valid for 24 hours.
Can I check canonicals across a whole sitemap in one go?
Yes, submit a batch of URLs in a single request and the task returns a per-URL breakdown once the crawl finishes.
Does it work for multi-language sites?
Yes, and it's particularly useful there, since it will surface a canonical that accidentally points to a different language version instead of the correct same-language URL.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/seo/canonical-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://ejemplo.com"}'const res = await fetch("https://api.kit.forhosting.com/seo/canonical-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"url": "https://ejemplo.com"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/seo/canonical-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"url": "https://ejemplo.com"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/seo/canonical-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"url":"https://ejemplo.com"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"url":"https://ejemplo.com"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/seo/canonical-check", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"url": "https://ejemplo.com"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.canonical_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |