Qué persigue este sistema de auditoría GEO
La lógica del sistema es sencilla: analizar cada URL del sitio y asignarle una serie de métricas que permitan estimar su potencial de reutilización por IA. En lugar de quedarse en un juicio subjetivo, el sistema devuelve columnas concretas que facilitan ordenar, filtrar y priorizar.
En este caso vamos a medir si la URL define pronto el concepto principal, si el contenido tiene claridad conceptual, si la estructura facilita la extracción de información, si existen bloques reutilizables y si el texto responde de forma directa al tema. Después, esas señales se sintetizan en un GEO_SCORE, que sirve como índice operativo.

Por qué Screaming Frog encaja bien en una auditoría GEO
Screaming Frog es una herramienta especialmente útil para este enfoque porque combina tres capacidades muy valiosas en un mismo flujo: rastreo a nivel de URL, renderizado JavaScript y extracción personalizada del HTML resultante. No fue diseñada específicamente para GEO, pero encaja muy bien en este tipo de análisis.
La primera ventaja es evidente: puedes trabajar sobre decenas, cientos o miles de URLs sin depender de revisiones manuales. La segunda es más interesante todavía: el JavaScript personalizado permite ejecutar lógica en cada página durante el crawl. En este caso, esa lógica se utiliza para localizar el contenido principal, limpiarlo, enviarlo a la API, recibir una respuesta estructurada e inyectar el resultado en el DOM renderizado.
La tercera ventaja es operativa. Como los resultados quedan insertados en el HTML renderizado, Screaming Frog puede extraer cada métrica mediante XPath y convertirla en una columna independiente. Esto hace posible transformar una auditoría GEO en un sistema escalable y reproducible.
Resultados reales de una auditoría GEO en 4 URLs analizadas con Screaming Frog
La siguiente tabla muestra un ejemplo real de resultados obtenidos al ejecutar una auditoría GEO con Screaming Frog y un análisis de contenido mediante modelos generativos. Las puntuaciones indican el potencial de reutilización del contenido por sistemas de respuesta generativa.
| URL analizada | GEO Score | Clasificación GEO | Nivel de optimización recomendado |
|---|---|---|---|
| / | 69 | Potencial GEO medio | Mejorar estructura explicativa |
| /estudios-seo/ | 68 | Potencial GEO medio | Refinar bloques citables |
| /consultor-seo/ | 58 | Potencial GEO bajo | Reforzar definición y claridad |
| /presupuesto-seo/ | 57 | Potencial GEO bajo | Reestructurar contenido |
En este ejemplo aparecen dos grupos claros. La home de agencia de SEO y la URL de estudios SEO muestran un potencial GEO medio con margen de mejora, mientras que las páginas de consultor SEO y presupuesto SEO presentan mayor debilidad estructural para sistemas generativos. El patrón más repetido en las URLs con menor puntuación es la falta de definición temprana del tema, menor claridad explicativa y menor número de bloques reutilizables.
Qué métricas devuelve la auditoría GEO
El sistema trabaja con cuatro grupos de métricas: principales, derivadas, accionables y auxiliares.

Métricas principales
- IA_REUSE_SCORE: probabilidad general de reutilización por IA.
- DEFINITION_SCORE: calidad y claridad de la definición principal.
- CLARITY_SCORE: claridad conceptual y facilidad de síntesis.
- STRUCTURE_SCORE: calidad estructural del contenido.
- CITABLE_BLOCKS: número estimado de fragmentos reutilizables o citables.
Métricas derivadas
- GEO_SCORE: índice global de potencial GEO.
- GEO_PRIORITY: prioridad operativa de optimización.
- GEO_STATUS: estado general del contenido desde una perspectiva GEO.
- LLM_RISK: riesgo principal de ser ignorado o mal aprovechado por sistemas generativos.
Campos accionables
- MAIN_ISSUES: principales problemas detectados.
- ACTIONABLE_ADVICE: recomendaciones aplicables sobre esa URL.
- TOP_PRIORITY_FIX: la mejora más importante que conviene aplicar primero.
Señales auxiliares
- CONTENT_WORDS: número de palabras del contenido extraído.
- H2_COUNT: número de H2.
- LIST_COUNT: número de elementos de lista.
- EXTRACTED_CHARS: caracteres totales enviados al análisis.
Qué significa cada métrica
IA_REUSE_SCORE
Es la puntuación general de 0 a 100 sobre probabilidad de reutilización por IA. Cuanto más alto es el valor, más probable es que el contenido tenga rasgos útiles para sistemas generativos.
DEFINITION_SCORE
Mide si la URL define de forma clara y suficientemente temprana el tema principal. Un contenido que tarda demasiado en explicar de qué trata suele rendir peor en este punto.
CLARITY_SCORE
Evalúa la claridad conceptual, la separación de ideas y la facilidad con la que un modelo puede sintetizar el contenido sin confusión.
STRUCTURE_SCORE
Valora jerarquía, organización semántica, presencia de listas, bloques reutilizables y facilidad de extracción.
CITABLE_BLOCKS
Es el número estimado de bloques que una IA podría reutilizar. Aquí suelen contar definiciones, clasificaciones, comparativas, listas de factores o fragmentos explicativos autónomos.
GEO_SCORE
Es una puntuación agregada que permite priorizar URLs. No pretende ser una verdad absoluta, sino una forma operativa de ordenar qué páginas conviene revisar antes.
GEO_PRIORITY
Clasifica la URL en cuatro niveles de actuación:
- Tier 1 – AI Ready
- Tier 2 – Improve
- Tier 3 – Weak
- Tier 4 – Rebuild
GEO_STATUS
Resume el estado general del contenido:
- High GEO Potential
- Medium GEO Potential
- Low GEO Potential
- Very Low GEO Potential
LLM_RISK
Indica el principal riesgo detectado. Puede señalar problemas como weak_definition, poor_structure, low_clarity o few_citable_blocks.
Arquitectura del sistema
El sistema se apoya en un flujo de ocho pasos:
- Screaming Frog rastrea una URL.
- El script localiza el contenido principal.
- Limpia ruido y extrae señales estructurales.
- Envía el contenido a OpenAI.
- OpenAI devuelve un JSON estructurado con las métricas.
- El script calcula GEO_SCORE y GEO_PRIORITY.
- Los resultados se insertan en un nodo oculto del DOM.
- Screaming Frog extrae esos valores con XPath y los convierte en columnas.
La clave está en que solo se realiza una llamada por URL y luego se reutiliza esa respuesta para poblar todas las columnas que necesitas.
Qué información se envía al modelo
No se envía el HTML completo. Eso sería menos eficiente, más caro y más ruidoso. Lo que se hace es construir un bloque de contenido con las partes más útiles de la página:
- Title
- Meta description
- H1
- H2
- H3
- Párrafos
- Listas
- Blockquotes
Además, se añaden señales cuantitativas:
- Número de H2
- Número de listas
- Número de párrafos
- Número de palabras
- Número de caracteres
Con eso se consigue un equilibrio razonable entre contexto, coste y limpieza del input.
Recomendaciones previas antes de montarlo
Usa una API key nueva
No conviene reutilizar claves expuestas o usadas en otros entornos. Lo razonable es trabajar con una clave específica para este tipo de pruebas.
Empieza con pocas URLs
Antes de lanzar el sistema a cientos de páginas, valida el flujo con una muestra pequeña. Eso te permite comprobar que las columnas se llenan bien y que los resultados que nos da tienen sentido.
Ajusta el renderizado JavaScript
Si el crawl va demasiado rápido, puede que algunos apartados no lleguen a completarse. En ese caso, conviene aumentar el tiempo de espera y reducir la velocidad.
Controla el coste
Cada URL genera una llamada a la API. Si vas a analizar cientos o miles de páginas, es mejor estimar antes el volumen, tokens y presupuesto.
Script JavaScript completo para Screaming Frog
Este es el script completo que se integra en JavaScript personalizado dentro de Screaming Frog. Su función es localizar el contenido principal, limpiarlo, enviarlo a OpenAI, calcular métricas derivadas e insertar los resultados en el DOM renderizado.
/*
GEO Audit Injector for Screaming Frog
1 API call per URL
Injects values into rendered DOM as data attributes
Then extract each metric with Custom Extraction
BEFORE USE:
- Replace YOUR_NEW_OPENAI_API_KEY
- Enable Config > Spider > Rendering > JavaScript
*/
const OPENAI_API_KEY = "YOUR_NEW_OPENAI_API_KEY";
const MODEL = "gpt-5-mini";
const OPENAI_URL = "https://api.openai.com/v1/responses";
const MAX_CHARS = 12000;
const MIN_CONTENT_CHARS = 400;
const FETCH_TIMEOUT_MS = 45000;
function cleanText(text) {
return (text || "")
.replace(/\s+/g, " ")
.replace(/\u00A0/g, " ")
.trim();
}
function safeSlice(text, maxChars) {
if (!text || text.length <= maxChars) return text || "";
return text.slice(0, maxChars);
}
function countWords(text) {
const t = cleanText(text);
if (!t) return 0;
return t.split(/\s+/).filter(Boolean).length;
}
function uniqueTextFromNodes(nodes) {
const seen = new Set();
const parts = [];
nodes.forEach((node) => {
const text = cleanText(node.innerText || node.textContent || "");
if (text && !seen.has(text)) {
seen.add(text);
parts.push(text);
}
});
return parts.join(" ");
}
function getMetaDescription() {
const el = document.querySelector('meta[name="description"]');
return el ? cleanText(el.getAttribute("content")) : "";
}
function getTitleText() {
return cleanText(document.title || "");
}
function getHeadings(selector, scope = document) {
return Array.from(scope.querySelectorAll(selector))
.map((el) => cleanText(el.innerText || el.textContent || ""))
.filter(Boolean);
}
function getMainContentNode() {
const selectors = [
"main article",
"article",
"main",
'[role="main"]',
".post-content",
".entry-content",
".article-content",
".content",
".main-content",
"#main-content",
"#content",
".post",
".entry"
];
for (const selector of selectors) {
const node = document.querySelector(selector);
if (node && cleanText(node.innerText || "").length >= MIN_CONTENT_CHARS) {
return node;
}
}
const candidates = Array.from(document.querySelectorAll("main, article, section, div"))
.map((el) => ({
el,
len: cleanText(el.innerText || "").length
}))
.filter((x) => x.len >= MIN_CONTENT_CHARS)
.sort((a, b) => b.len - a.len);
return candidates.length ? candidates[0].el : null;
}
function extractContentData() {
const mainNode = getMainContentNode();
if (!mainNode) {
return { error: "No main content found." };
}
const clone = mainNode.cloneNode(true);
const removeSelectors = [
"script",
"style",
"noscript",
"form",
"iframe",
"nav",
"aside",
"footer",
".sidebar",
".related",
".related-posts",
".share",
".social",
".newsletter",
".comments",
".comment-respond",
".ads",
".ad",
".advertisement",
".breadcrumbs",
"#cookie-law-info-bar",
".cookie",
".cookies",
".cky-consent-container",
".cli-bar-container"
];
removeSelectors.forEach((selector) => {
clone.querySelectorAll(selector).forEach((el) => el.remove());
});
const title = getTitleText();
const metaDescription = getMetaDescription();
const h1 = getHeadings("h1", clone);
const h2 = getHeadings("h2", clone);
const h3 = getHeadings("h3", clone);
const paragraphs = uniqueTextFromNodes(clone.querySelectorAll("p"));
const listItems = uniqueTextFromNodes(clone.querySelectorAll("li"));
const blockquotes = uniqueTextFromNodes(clone.querySelectorAll("blockquote"));
let bodyText = [
title ? `TITLE: ${title}` : "",
metaDescription ? `META_DESCRIPTION: ${metaDescription}` : "",
h1.length ? `H1: ${h1.join(" | ")}` : "",
h2.length ? `H2: ${h2.join(" | ")}` : "",
h3.length ? `H3: ${h3.join(" | ")}` : "",
paragraphs ? `PARAGRAPHS: ${paragraphs}` : "",
listItems ? `LIST_ITEMS: ${listItems}` : "",
blockquotes ? `QUOTES: ${blockquotes}` : ""
]
.filter(Boolean)
.join("\n\n");
bodyText = safeSlice(cleanText(bodyText), MAX_CHARS);
return {
content: bodyText,
features: {
url: window.location.href,
title,
metaDescription,
h1_count: h1.length,
h2_count: h2.length,
h3_count: h3.length,
list_count: clone.querySelectorAll("ul li, ol li").length,
paragraph_count: clone.querySelectorAll("p").length,
words: countWords(bodyText),
chars: bodyText.length
}
};
}
function fetchWithTimeout(url, options, timeoutMs) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs)
)
]);
}
function extractTextOutput(data) {
if (data.output_text && typeof data.output_text === "string") {
return data.output_text.trim();
}
if (Array.isArray(data.output)) {
const parts = [];
data.output.forEach((item) => {
if (Array.isArray(item.content)) {
item.content.forEach((c) => {
if (typeof c.text === "string") parts.push(c.text);
});
}
});
if (parts.length) return parts.join("\n").trim();
}
return "";
}
function clampScore(n) {
const v = Number(n);
if (isNaN(v)) return 0;
if (v < 0) return 0;
if (v > 100) return 100;
return Math.round(v);
}
function normalizeString(v) {
return cleanText((v || "").toString());
}
function buildGeoStatus(score) {
if (score >= 80) return "High GEO Potential";
if (score >= 60) return "Medium GEO Potential";
if (score >= 40) return "Low GEO Potential";
return "Very Low GEO Potential";
}
function buildLlmRisk(definitionScore, structureScore, clarityScore, citableBlocks) {
const risks = [];
if (definitionScore < 50) risks.push("weak_definition");
if (structureScore < 50) risks.push("poor_structure");
if (clarityScore < 50) risks.push("low_clarity");
if (citableBlocks < 2) risks.push("few_citable_blocks");
if (!risks.length) return "low_risk";
if (risks.length <= 2) return risks.join("|");
return "high_risk:" + risks.join("|");
}
function buildGeoPriority(geoScore) {
if (geoScore >= 80) return "Tier 1 - AI Ready";
if (geoScore >= 60) return "Tier 2 - Improve";
if (geoScore >= 40) return "Tier 3 - Weak";
return "Tier 4 - Rebuild";
}
function upsertGeoNode(data) {
let node = document.querySelector("#sf-geo-audit");
if (!node) {
node = document.createElement("div");
node.id = "sf-geo-audit";
node.style.display = "none";
document.body.appendChild(node);
}
Object.entries(data).forEach(([key, value]) => {
node.setAttribute(`data-${key}`, String(value ?? ""));
});
node.textContent = JSON.stringify(data);
}
async function callOpenAI(content, features) {
const schema = {
type: "object",
additionalProperties: false,
properties: {
IA_REUSE_SCORE: { type: "number" },
DEFINITION_SCORE: { type: "number" },
CLARITY_SCORE: { type: "number" },
STRUCTURE_SCORE: { type: "number" },
CITABLE_BLOCKS: { type: "number" },
MAIN_ISSUES: { type: "string" },
ACTIONABLE_ADVICE: { type: "string" },
TOP_PRIORITY_FIX: { type: "string" }
},
required: [
"IA_REUSE_SCORE",
"DEFINITION_SCORE",
"CLARITY_SCORE",
"STRUCTURE_SCORE",
"CITABLE_BLOCKS",
"MAIN_ISSUES",
"ACTIONABLE_ADVICE",
"TOP_PRIORITY_FIX"
]
};
const systemPrompt = `
You are an expert GEO and LLM content auditor.
Evaluate how likely the content is to be reused, synthesized or cited by generative systems such as ChatGPT, Gemini, Perplexity or Copilot.
Score strictly using only the provided content.
Criteria:
1. IA_REUSE_SCORE (0-100)
2. DEFINITION_SCORE (0-100)
3. CLARITY_SCORE (0-100)
4. STRUCTURE_SCORE (0-100)
5. CITABLE_BLOCKS (integer)
6. MAIN_ISSUES (brief, specific)
7. ACTIONABLE_ADVICE (brief, actionable)
8. TOP_PRIORITY_FIX (single top action)
Rules:
- No extra text outside JSON.
- Penalize weak or late definitions, poor structure, long repetitive paragraphs, weak information density, promotional language, lack of lists/comparisons.
- Reward early definitions, answer-first structure, reusable blocks, lists, comparisons and direct explanations.
`.trim();
const userPrompt = `
URL: ${features.url}
EXTRACTED SIGNALS:
- title: ${features.title || ""}
- meta_description: ${features.metaDescription || ""}
- h1_count: ${features.h1_count}
- h2_count: ${features.h2_count}
- h3_count: ${features.h3_count}
- list_count: ${features.list_count}
- paragraph_count: ${features.paragraph_count}
- word_count: ${features.words}
- char_count: ${features.chars}
CONTENT:
${content}
`.trim();
const payload = {
model: MODEL,
input: [
{
role: "system",
content: [{ type: "input_text", text: systemPrompt }]
},
{
role: "user",
content: [{ type: "input_text", text: userPrompt }]
}
],
text: {
format: {
type: "json_schema",
name: "geo_audit",
strict: true,
schema
}
}
};
const response = await fetchWithTimeout(
OPENAI_URL,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${OPENAI_API_KEY}`
},
body: JSON.stringify(payload)
},
FETCH_TIMEOUT_MS
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`OpenAI API ${response.status}: ${errorText}`);
}
const data = await response.json();
const rawText = extractTextOutput(data);
if (!rawText) {
throw new Error("No structured response returned.");
}
const parsed = JSON.parse(rawText);
const iaReuse = clampScore(parsed.IA_REUSE_SCORE);
const definition = clampScore(parsed.DEFINITION_SCORE);
const clarity = clampScore(parsed.CLARITY_SCORE);
const structure = clampScore(parsed.STRUCTURE_SCORE);
const citableBlocks = Math.max(0, parseInt(parsed.CITABLE_BLOCKS || 0, 10) || 0);
const mainIssues = normalizeString(parsed.MAIN_ISSUES);
const advice = normalizeString(parsed.ACTIONABLE_ADVICE);
const topFix = normalizeString(parsed.TOP_PRIORITY_FIX);
const geoScore = Math.round(
iaReuse * 0.40 +
definition * 0.20 +
clarity * 0.15 +
structure * 0.15 +
citableBlocks * 2
);
const geoStatus = buildGeoStatus(iaReuse);
const geoPriority = buildGeoPriority(geoScore);
const llmRisk = buildLlmRisk(definition, structure, clarity, citableBlocks);
return {
ia_reuse_score: iaReuse,
definition_score: definition,
clarity_score: clarity,
structure_score: structure,
citable_blocks: citableBlocks,
geo_score: geoScore,
geo_priority: geoPriority,
geo_status: geoStatus,
llm_risk: llmRisk,
main_issues: mainIssues,
actionable_advice: advice,
top_priority_fix: topFix,
content_words: features.words,
h2_count: features.h2_count,
list_count: features.list_count,
extracted_chars: features.chars
};
}
return (async () => {
try {
if (!OPENAI_API_KEY || OPENAI_API_KEY === "YOUR_NEW_OPENAI_API_KEY") {
return seoSpider.error("Add a valid OpenAI API key.");
}
const extracted = extractContentData();
if (extracted.error) {
upsertGeoNode({
ia_reuse_score: "",
definition_score: "",
clarity_score: "",
structure_score: "",
citable_blocks: "",
geo_score: "",
geo_priority: "Error",
geo_status: "Error",
llm_risk: "Error",
main_issues: extracted.error,
actionable_advice: "",
top_priority_fix: "",
content_words: 0,
h2_count: 0,
list_count: 0,
extracted_chars: 0
});
return seoSpider.data(extracted.error);
}
const { content, features } = extracted;
if (!content || content.length < MIN_CONTENT_CHARS) {
const msg = "Content too short for reliable GEO audit.";
upsertGeoNode({
ia_reuse_score: "",
definition_score: "",
clarity_score: "",
structure_score: "",
citable_blocks: "",
geo_score: "",
geo_priority: "Error",
geo_status: "Error",
llm_risk: "Error",
main_issues: msg,
actionable_advice: "",
top_priority_fix: "",
content_words: features.words || 0,
h2_count: features.h2_count || 0,
list_count: features.list_count || 0,
extracted_chars: features.chars || 0
});
return seoSpider.data(msg);
}
const audit = await callOpenAI(content, features);
upsertGeoNode(audit);
return seoSpider.data(
`GEO=${audit.geo_score} | PRIORITY=${audit.geo_priority} | IA=${audit.ia_reuse_score} | DEF=${audit.definition_score} | CLA=${audit.clarity_score} | STR=${audit.structure_score} | RISK=${audit.llm_risk}`
);
} catch (error) {
upsertGeoNode({
ia_reuse_score: "",
definition_score: "",
clarity_score: "",
structure_score: "",
citable_blocks: "",
geo_score: "",
geo_priority: "Error",
geo_status: "Error",
llm_risk: "Error",
main_issues: cleanText(error.message),
actionable_advice: "",
top_priority_fix: "",
content_words: 0,
h2_count: 0,
list_count: 0,
extracted_chars: 0
});
return seoSpider.error(`GEO audit error: ${error.message}`);
}
})();Explicación del JavaScript paso a paso
Definición de constantes
El script comienza definiendo la API key, el modelo, el endpoint, el número máximo de caracteres y el tiempo de espera. Esto permite controlar el coste, la longitud y la estabilidad.
Limpieza del texto
La función cleanText() normaliza los espacios y deja el texto en un formato más limpio para el análisis.
Recorte del contenido
safeSlice() evita enviar textos demasiado largos, lo que ayuda a controlar el coste.
Conteo de palabras
countWords() se utiliza para registrar una señal auxiliar que luego puede ser útil al interpretar resultados.
Extracción de contenido único
uniqueTextFromNodes() recorre distintos nodos y evita duplicados exactos.
Lectura de title y meta description
El script añade estos elementos porque aportan contexto valioso al modelo.
Detección del contenido principal
getMainContentNode() busca el contenido usando varios selectores habituales. Si no encuentra un bloque claro, se queda con el contenedor que tenga más texto útil.
Limpieza del bloque principal
Se eliminan scripts, estilos, formularios, navegación, sidebars, comentarios, banners de cookies y otros elementos con poco valor para la auditoría.
Construcción del contenido a analizar
Después se unen title, meta description, H1, H2, H3, párrafos, listas y citas en un solo bloque de texto.
Llamada a la API
callOpenAI() envía el contenido y las señales estructurales junto con un JSON Schema estricto para forzar una respuesta estructurada.
Normalización de métricas
Las puntuaciones se convierten en números consistentes y se limpian los campos textuales.
Cálculo de GEO_SCORE
La fórmula utilizada en este ejemplo es:
- IA_REUSE_SCORE × 0,40
- DEFINITION_SCORE × 0,20
- CLARITY_SCORE × 0,15
- STRUCTURE_SCORE × 0,15
- CITABLE_BLOCKS × 2
Es un punto de partida razonable, no una fórmula cerrada. Puede ajustarse según el tipo de proyecto.
Clasificación y riesgos
El script genera GEO_STATUS, GEO_PRIORITY y LLM_RISK para convertir el análisis en algo operativo.
Inyección en el DOM
Por último, inserta todos los resultados en un nodo oculto con id sf-geo-audit. Ese es el punto del que luego tiran los XPath.
Configuración exacta de la auditoría GEO en Screaming Frog
1. Activar JavaScript rendering
Ve a Config > Spider > Rendering > JavaScript.
2. Añadir el script
Ve a Configuration > Custom > JavaScript y pega el script completo.
3. Ajustar el tiempo de espera
Si algunas URLs aparecen vacías porque el script no termina a tiempo, aumenta el wait time del renderizado.
4. Reducir velocidad de crawl si hace falta
Cuando trabajas con llamadas a API dentro del rastreo no conviene forzar demasiada velocidad.
5. Crear las extracciones XPath
Una vez añadido el script, el siguiente paso es crear una extracción por cada métrica.
XPath completos para extraer todas las columnas

Para todas las extracciones usa:
- Tipo: XPath
- Modo: Extraer HTML interna
IA_REUSE_SCORE
//*[@id='sf-geo-audit']/@data-ia_reuse_score
DEFINITION_SCORE
//*[@id='sf-geo-audit']/@data-definition_score
CLARITY_SCORE
//*[@id='sf-geo-audit']/@data-clarity_score
STRUCTURE_SCORE
//*[@id='sf-geo-audit']/@data-structure_score
CITABLE_BLOCKS
//*[@id='sf-geo-audit']/@data-citable_blocks
GEO_SCORE
//*[@id='sf-geo-audit']/@data-geo_score
GEO_PRIORITY
//*[@id='sf-geo-audit']/@data-geo_priority
GEO_STATUS
//*[@id='sf-geo-audit']/@data-geo_status
LLM_RISK
//*[@id='sf-geo-audit']/@data-llm_risk
MAIN_ISSUES
//*[@id='sf-geo-audit']/@data-main_issues
ACTIONABLE_ADVICE
//*[@id='sf-geo-audit']/@data-actionable_advice
TOP_PRIORITY_FIX
//*[@id='sf-geo-audit']/@data-top_priority_fix
CONTENT_WORDS
//*[@id='sf-geo-audit']/@data-content_words
H2_COUNT
//*[@id='sf-geo-audit']/@data-h2_count
LIST_COUNT
//*[@id='sf-geo-audit']/@data-list_count
EXTRACTED_CHARS
//*[@id='sf-geo-audit']/@data-extracted_charsCómo validar que todo funciona
- Prueba primero con un grupo pequeño de URLs.
- Verifica que se rellenan al menos IA_REUSE_SCORE, GEO_SCORE, GEO_PRIORITY y LLM_RISK.
- Ordena por GEO_SCORE ascendente.
- Revisa manualmente una URL alta, una media y una baja para comprobar si el diagnóstico encaja con la realidad del contenido.
Si el sistema funciona bien en una muestra pequeña, ya puedes escalarlo.
Cómo interpretar los resultados
Cuando GEO_SCORE es alto
Suele indicar una definición clara, buena organización semántica, bloques reutilizables y contenido fácil de sintetizar.
Cuando GEO_SCORE es medio
Lo habitual es que la URL sea útil, pero tenga puntos de mejora, como que el concepto se explica demasiado tarde, exceso de narrativa o falta de listas y bloques comparativos.
Cuando GEO_SCORE es bajo
Suele coincidir con páginas poco claras, excesivamente promocionales, mal estructuradas o con escasa densidad informativa.
Cómo usar esto en una auditoría real
Detectar páginas prioritarias
Ordena por GEO_SCORE ascendente.
Detectar falta de definición
Filtra por URLs con DEFINITION_SCORE por debajo de 50.
Detectar problemas estructurales
Filtra por LLM_RISK cuando incluya poor_structure.
Detectar contenido poco reutilizable
Filtra por CITABLE_BLOCKS inferiores a 2.
Montar un backlog editorial
Usa MAIN_ISSUES, ACTIONABLE_ADVICE y TOP_PRIORITY_FIX para convertir el crawl en una lista priorizada de acciones.
Casos en los que este sistema aporta más valor
Blogs con contenido informativo
Permite detectar qué piezas explican bien un tema y cuáles no.
Sitios B2B
Muchas páginas de servicios están escritas para vender, pero no para ser reutilizadas por IA. Esta auditoría ayuda a detectar ese desequilibrio.
Proyectos con estrategia GEO
Es especialmente útil cuando quieres priorizar qué URLs merecen optimización específica para buscadores generativos.
Auditorías de contenido heredado
Sirve para revisar de forma rápida URLs antiguas cuyo valor explicativo es bajo o irregular.
Limitaciones del sistema
No mide citación real
Este sistema estima probabilidad de reutilización. No confirma que una URL vaya a ser citada en un entorno real.
Depende de la extracción del contenido principal
Si la página tiene una estructura poco convencional y el contenido no se localiza bien, la evaluación pierde precisión.
Los pesos del GEO_SCORE son ajustables
La fórmula no es universal. Puede afinarse según vertical, intención del contenido y objetivos del proyecto.
Tiene coste por URL
Cada página genera una llamada a la API, así que conviene tener esto en cuenta al escalar.
No sustituye una auditoría SEO
La capa GEO suma valor, pero no reemplaza análisis de indexación, enlazado, intención, autoridad o arquitectura.
Recomendaciones prácticas para usarlo bien
- Empieza por un muestreo reducido.
- Ajusta los pesos de GEO_SCORE cuando tengas datos propios.
- Segmenta por tipo de URL: blog, servicios, landings, recursos o categorías.
- No uses esta auditoría como sustituto del SEO clásico, sino como capa adicional.
- Valida manualmente una muestra antes de escalar a todo el sitio.
Qué mejoras puedes añadir después
Una vez validado el sistema, puedes ampliarlo con nuevas métricas como AI_SNIPPET_SCORE, ENTITY_CLARITY_SCORE o ANSWERABILITY_SCORE.
También puedes crear una segunda capa que no solo audite, sino que proponga una reescritura parcial del primer bloque, nuevas estructuras de H2 o recomendaciones de bloques citables. Ahí es donde una auditoría GEO empieza a convertirse en una herramienta de optimización editorial real.
Ten esto en cuenta
La visibilidad ya no se explica solo desde el ranking. Cada vez tiene más sentido medir si una URL puede servir como materia prima para una respuesta generativa. Eso obliga a incorporar nuevas preguntas en la auditoría de contenidos:
- ¿Define pronto el tema?
- ¿Es fácil de sintetizar?
- ¿Tiene bloques reutilizables?
- ¿Puede una IA usar esta URL para responder?
Montar una auditoría GEO de URLs con Screaming Frog permite empezar a responder esas preguntas de forma operativa, escalable y accionable.
Y ese cambio, bien trabajado, puede convertirse en una ventaja real para cualquier proyecto que quiera entender cómo ganar presencia en entornos generativos más allá del SEO tradicional.

SEO desde 2004 – CEO en Indexando Marketing desde 2016 – IA lover y automatizaciones desde 2024.
