Vérifiez la cohérence et la réciprocité des hreflang
Un groupe hreflang n’est fiable que si chaque page participante déclare le même ensemble complet de variantes, avec une autoréférence.
Lancer gratuitement
Ce vérificateur compare les correspondances recueillies sur plusieurs pages et révèle les groupes incomplets sans charger aucune URL. Il construit l’ensemble attendu à partir de toutes les URL sources et cibles, contrôle chaque source, puis indique précisément les liens absents de chaque page. Le résultat déterministe convient aux mises en ligne, aux migrations et aux contrôles SEO reproductibles.
Pourquoi la réciprocité complète est essentielle
Une annotation hreflang n’est pas une simple recommandation à sens unique. Elle décrit l’appartenance à un groupe de pages équivalentes par langue ou région, dont chaque membre doit publier les mêmes destinations, y compris sa propre URL. Si la page anglaise cite les versions française et allemande, mais que la française ne cite que l’anglaise, le groupe reste incomplet malgré des balises isolées apparemment correctes. Les moteurs de recherche peuvent ignorer les relations non confirmées, proposer la mauvaise langue ou affaiblir le ciblage régional. Le contrôle manuel devient vite incertain, car le nombre de relations augmente fortement avec la taille du groupe. Quatre pages doivent chacune déclarer quatre destinations, soit seize relations source-cible. Ce vérificateur remplace la comparaison visuelle par un test d’ensemble exact. Il ne confirme la cohérence que lorsque chaque page représentée déclare toutes les pages du groupe, et rend ainsi visibles les omissions comme les autoréférences manquantes.
Préparer et interpréter les correspondances
Envoyez un enregistrement pour chaque déclaration hreflang observée. page_url désigne la page contenant la balise, language conserve sa valeur et target_url indique sa destination. Incluez toutes les pages censées appartenir au groupe. Le vérificateur déduit le groupe attendu de l’union des URL sources et cibles. Ainsi, une page citée par ses voisines mais dépourvue de déclarations propres reste dans l’ensemble attendu et apparaît comme privée de toutes les destinations. Les URL sont comparées comme des chaînes exactes après suppression des espaces extérieurs ; votre collecte doit donc employer la forme absolue canonique produite par les pages. La sortie contient un résumé par page et une liste de conflits. Toute page incomplète présente missing_urls, avec les destinations précises à ajouter. consistent n’est vrai que lorsque la liste des conflits est vide. Les doublons ne créent pas une fausse complétude, car les destinations sont traitées comme un ensemble.
L’intégrer aux lancements, migrations et audits
Le meilleur moment pour contrôler la réciprocité précède l’arrivée des robots sur une publication multilingue. Exportez les déclarations hreflang des pages rendues, convertissez-les en correspondances et bloquez la mise en ligne lorsque consistent est faux. Vous détecterez les branches de modèle qui omettent la page courante, les lancements régionaux qui ne mettent à jour que le nouveau marché et les migrations où une langue produit encore d’anciennes URL. Le même contrôle convient après l’ajout ou le retrait d’une langue. Comme l’algorithme n’effectue aucune requête réseau, il ne prouve ni la disponibilité d’une cible, ni son caractère canonique, ni l’équivalence de son contenu ; ces points demandent un crawl et une validation éditoriale distincts. Sa promesse est ciblée : vérifier que chaque page représentée cite toutes les autres et elle-même. Les appels API utilisent le prix de base publié de $0.002. Conservez les conflits avec les preuves de déploiement afin de rattacher chaque correction à une source et une cible précises.
Cas d’usage
Valider un lancement multilingue
Vérifiez avant publication que chaque nouvelle langue et chaque page sœur existante proposent l’ensemble complet des variantes.
Auditer une migration de plateforme
Comparez les correspondances des nouveaux modèles et repérez les pages ayant perdu leur autoréférence ou leurs liens retour.
Sécuriser une chaîne SEO
Transformez un groupe incohérent en échec déterministe indiquant chaque relation source-cible absente.
Questions fréquentes
Quand un groupe hreflang est-il cohérent ?
Chaque URL représentée doit déclarer toutes les URL du groupe, elle-même comprise. Le résultat n’est vrai que si toutes ces relations existent.
Le vérificateur charge-t-il les pages ?
Non. Il compare seulement vos correspondances et ne contrôle ni le statut HTTP, ni les balises canoniques, ni le contenu.
Comment détecte-t-il une page source absente ?
Le groupe attendu réunit les URL sources et cibles. Une URL présente uniquement comme cible est donc signalée comme page sans déclarations.
Les déclarations en double sont-elles des conflits ?
Elles ne changent pas la complétude. Les destinations forment un ensemble, tandis que le total conserve tous les enregistrements soumis.
Les variantes d’URL sont-elles normalisées ?
Non. Elles sont comparées exactement après retrait des espaces extérieurs. Normalisez schéma, hôte, chemin et barre finale avant l’envoi.
Quel est le prix d’un appel API ?
Chaque appel applique le prix de base publié de $0.002. La comparaison ne sollicite ni réseau ni modèle.
Pour les développeurs — accès API
Tout sur cette page est disponible par programmation. Cette section s'adresse aux équipes qui veulent l'intégrer à leurs systèmes ; les autres peuvent simplement utiliser l'outil ci-dessus.
Endpoint
Authentification par jeton Bearer : un seul POST met la tâche en file d’attente, et le résultat vous parvient par webhook ou lien signé.
Appeler depuis votre stack
curl -X POST https://api.kit.forhosting.com/seo/hreflang-conflict-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}'const res = await fetch("https://api.kit.forhosting.com/seo/hreflang-conflict-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/seo/hreflang-conflict-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/seo/hreflang-conflict-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"mappings":[{"page_url":"https://example.com/en","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/en","language":"fr","target_url":"https://example.com/fr"},{"page_url":"https://example.com/fr","language":"en","target_url":"https://example.com/en"},{"page_url":"https://example.com/fr","language":"fr","target_url":"https://example.com/fr"}]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/seo/hreflang-conflict-check", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Exemple de requête
{
"mappings": [
{
"page_url": "https://example.com/en",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/en",
"language": "fr",
"target_url": "https://example.com/fr"
},
{
"page_url": "https://example.com/fr",
"language": "en",
"target_url": "https://example.com/en"
},
{
"page_url": "https://example.com/fr",
"language": "fr",
"target_url": "https://example.com/fr"
}
]
}Exemple de réponse
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.hreflang_conflict_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}L’API est asynchrone : chaque appel renvoie un task_id immédiatement, puis vous interrogez l’état à raison d’une requête par seconde.
Tarifs
Le prix est publié, sans tokens ni crédits. Une tâche qui échoue n’est pas facturée.
Erreurs
| HTTP | Code | Signification |
|---|---|---|
401 | unauthorized | Clé API absente ou invalide : vérifiez l’en-tête Authorization. |
402 | insufficient_balance | Solde insuffisant : rechargez votre compte pour lancer cette tâche. |
404 | unknown_type | Type de tâche inconnu : vérifiez le champ type de votre requête. |
429 | rate_limited | Trop de requêtes : ralentissez la cadence, puis réessayez. |