Verifichi coerenza e reciprocità degli hreflang
I gruppi hreflang funzionano in modo affidabile solo quando ogni pagina partecipante dichiara lo stesso insieme completo di alternative, compresa l’autoreferenza.
Esegui gratis nel browser
Questo controllo confronta le mappature raccolte da più pagine e rileva i gruppi incompleti senza aprire alcun URL. Costruisce l’insieme atteso da tutti gli URL di origine e destinazione, verifica ogni origine e restituisce i collegamenti precisi che mancano in ciascuna pagina. Il risultato è deterministico e adatto a rilasci, migrazioni e controlli SEO ripetibili.
Perché servono gruppi reciproci completi
Un’annotazione hreflang non è una semplice indicazione unidirezionale. Descrive l’appartenenza a un gruppo di pagine equivalenti per lingua o area geografica, e ogni pagina deve pubblicare lo stesso insieme di destinazioni, inclusa se stessa. Se la pagina inglese indica le alternative francese e tedesca, ma quella francese indica soltanto l’inglese, il gruppo è incompleto anche se diverse singole etichette sembrano corrette. I motori di ricerca possono ignorare le relazioni non confermate, proporre la lingua sbagliata o indebolire il segnale regionale. La revisione manuale diventa inaffidabile quando il gruppo cresce, perché le relazioni aumentano rapidamente. Quattro pagine devono dichiarare quattro destinazioni ciascuna, per sedici relazioni complessive. Il controllo trasforma il confronto visivo in una prova esatta tra insiemi. Conferma la coerenza soltanto se ogni pagina rappresentata dichiara tutte le pagine del gruppo, rendendo esplicite omissioni e autoreferenze mancanti.
Come preparare e leggere le mappature
Invii un record per ogni dichiarazione hreflang rilevata. page_url identifica la pagina con l’etichetta, language ne conserva il valore e target_url indica la destinazione. Includa tutte le pagine previste nel gruppo. Il controllo ricava il gruppo atteso dall’unione degli URL di origine e destinazione. Se una pagina è citata dalle pagine sorelle ma non fornisce dichiarazioni proprie, rimane quindi nell’insieme atteso e risulta priva di tutte le destinazioni. Gli URL vengono confrontati come stringhe esatte dopo la rimozione degli spazi esterni; la raccolta deve usare la forma assoluta canonica prodotta dalle pagine. L’output contiene un riepilogo per pagina e l’elenco dei conflitti. Una pagina incompleta espone missing_urls con le destinazioni precise da aggiungere. consistent è vero solo quando i conflitti sono assenti. I duplicati non creano una falsa completezza, perché le destinazioni vengono confrontate come insieme.
Impiego in rilasci, migrazioni e controlli periodici
Il momento migliore per verificare la reciprocità è prima che i crawler raggiungano un rilascio multilingue. Esporti le dichiarazioni hreflang dalle pagine renderizzate, le converta in record e blocchi il rilascio quando consistent è falso. In questo modo troverà rami del template che omettono la pagina corrente, aperture regionali che aggiornano soltanto il nuovo mercato e migrazioni in cui una lingua produce ancora vecchi URL. Lo stesso controllo è utile dopo l’aggiunta o la rimozione di lingue. Poiché l’algoritmo non effettua richieste di rete, non dimostra che una destinazione risponda, sia canonica o contenga materiale equivalente; servono verifiche di scansione ed editoriali separate. La promessa è circoscritta: stabilire se ogni pagina rappresentata elenca tutte le altre e se stessa. Le richieste API applicano il prezzo base pubblicato di $0.002. Conservi i conflitti con le prove del rilascio per collegare ogni correzione a una precisa origine e destinazione.
Casi d'uso
Validare un rilascio multilingue
Verifichi prima del rilascio che ogni nuova lingua e ogni pagina sorella pubblichino l’insieme completo di alternative.
Controllare una migrazione
Confronti le mappature dei nuovi template e trovi pagine che hanno perso autoreferenze o collegamenti di ritorno.
Proteggere la pipeline SEO
Trasformi un gruppo incoerente in un controllo fallito deterministico con le relazioni mancanti esatte.
Domande frequenti
Quando un gruppo hreflang è coerente?
Ogni URL rappresentato deve dichiarare tutti gli URL del gruppo, incluso se stesso. Il risultato è vero solo se esistono tutte le relazioni.
Il controllo apre le pagine?
No. Confronta solo le mappature fornite e non verifica stato HTTP, canonical o contenuto.
Come rileva le pagine di origine mancanti?
Il gruppo atteso unisce URL di origine e destinazione. Un URL presente solo come destinazione risulta quindi privo di dichiarazioni.
Le dichiarazioni duplicate sono conflitti?
Non cambiano la completezza. Le destinazioni sono un insieme, mentre il conteggio conserva tutti i record inviati.
Le varianti degli URL vengono normalizzate?
No. Sono confrontate esattamente dopo la rimozione degli spazi esterni. Normalizzi schema, host, percorso e barra finale.
Quanto costa una richiesta API?
Ogni richiesta usa il prezzo base pubblicato di $0.002. Il confronto non usa rete né modelli.
Per sviluppatori — accesso via API
Tutto quello che vedi in questa pagina è disponibile anche via API. Questa sezione è per i team che vogliono integrarlo nei propri sistemi; chi non ne ha bisogno può semplicemente usare lo strumento qui sopra.
Endpoint
Autenticazione con Bearer token: un POST mette in coda l'attività e il risultato arriva via webhook o link firmato.
Chiamala dal tuo 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)Esempio di richiesta
{
"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"
}
]
}Esempio di risposta
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.hreflang_conflict_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}L'API è asincrona: ricevi subito un task_id e puoi fare polling fino a 1 richiesta al secondo.
Prezzi
Prezzo pubblicato, senza token né crediti. Se l'attività fallisce, non paghi.
Errori
| HTTP | Codice | Significato |
|---|---|---|
401 | unauthorized | Chiave API mancante o non valida: controlla l'header Authorization. |
402 | insufficient_balance | Credito esaurito: ricarica per continuare a eseguire attività. |
404 | unknown_type | Tipo di attività sconosciuto: controlla il campo type della richiesta. |
429 | rate_limited | Troppe richieste in poco tempo: rallenta e riprova tra qualche secondo. |