El seguimiento de los cambios en las citas de la descripción general de IA le permite detectar el momento en que sus URL ganan o pierden ubicación en las respuestas generadas por IA de Google. A medida que AI Overviews remodela la distribución de clics, saber qué dominios aparecen en las citas (y cuándo cambia esa lista) es una señal directa para la estrategia de contenido. La API de búsqueda de Scavio devuelve datos estructurados de descripción general de la IA, incluidas las URL citadas, para que pueda sondear las consultas según una programación, diferenciar las listas de citas y activar alertas cuando los competidores ingresan o sus páginas caen. Este tutorial crea un script de Python que almacena instantáneas de citas en JSON e informa los cambios entre ejecuciones.
Requisitos previos
- Python 3.8 o superior instalado
- solicita biblioteca instalada (solicitudes de instalación de pip)
- Una clave API de Scavio de scavio.dev
- Familiaridad básica con la E/S de archivos JSON en Python
Guia paso a paso
Paso 1: Definir consultas y URL de destino para monitorear
Cree un dictado de configuración que asigne consultas de destino a las URL que le interesan. El script comprobará si estas URL aparecen en las citas de la descripción general de AI para cada consulta.
MONITOR_CONFIG = {
"best crm for startups": {
"my_urls": ["https://mysite.com/crm-guide"],
"watch_competitors": True
},
"how to automate lead scoring": {
"my_urls": ["https://mysite.com/lead-scoring"],
"watch_competitors": True
}
}Paso 2: Obtenga citas de descripción general de IA de Scavio
PUBLICAR en el punto final de búsqueda de Scavio para cada consulta. La respuesta incluye un objeto ai_overview con una matriz de citas que contiene las URL a las que se hace referencia en la respuesta generada por IA.
import requests
import os
API_KEY = os.environ.get('SCAVIO_API_KEY', 'your_scavio_api_key')
def fetch_citations(query: str) -> list[str]:
response = requests.post(
'https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'query': query, 'country_code': 'us'}
)
response.raise_for_status()
data = response.json()
ai_overview = data.get('ai_overview', {})
citations = ai_overview.get('citations', [])
return [c.get('url', '') for c in citations]Paso 3: Cargue la instantánea anterior y calcule la diferencia
Lea la última instantánea guardada del disco y compárela con la lista de citas nueva. Identifique qué URL se agregaron y cuáles se eliminaron desde la última verificación.
import json
from pathlib import Path
SNAPSHOT_FILE = Path('citation_snapshots.json')
def load_snapshot() -> dict:
if SNAPSHOT_FILE.exists():
return json.loads(SNAPSHOT_FILE.read_text())
return {}
def diff_citations(old: list[str], new: list[str]) -> dict:
old_set, new_set = set(old), set(new)
return {
'added': list(new_set - old_set),
'removed': list(old_set - new_set),
'unchanged': list(old_set & new_set)
}Paso 4: Ejecute el bucle del monitor y guarde la instantánea actualizada
Repita todas las consultas monitoreadas, obtenga citas actuales, compare con la instantánea anterior e imprima alertas para cualquier cambio. Guarde la nueva instantánea en el disco para la siguiente ejecución.
from datetime import datetime
def run_monitor():
snapshot = load_snapshot()
new_snapshot = {}
for query, config in MONITOR_CONFIG.items():
current = fetch_citations(query)
new_snapshot[query] = current
previous = snapshot.get(query, [])
changes = diff_citations(previous, current)
if changes['added'] or changes['removed']:
print(f'[{datetime.now().isoformat()}] Changes for: {query}')
for url in changes['added']:
label = 'MY URL' if url in config['my_urls'] else 'COMPETITOR'
print(f' + ADDED ({label}): {url}')
for url in changes['removed']:
label = 'MY URL' if url in config['my_urls'] else 'COMPETITOR'
print(f' - REMOVED ({label}): {url}')
SNAPSHOT_FILE.write_text(json.dumps(new_snapshot, indent=2))
print(f'Snapshot saved with {len(new_snapshot)} queries')Ejemplo en Python
import os
import json
import requests
from pathlib import Path
from datetime import datetime
API_KEY = os.environ.get('SCAVIO_API_KEY', 'your_scavio_api_key')
ENDPOINT = 'https://api.scavio.dev/api/v1/search'
SNAPSHOT_FILE = Path('citation_snapshots.json')
MONITOR_CONFIG = {
'best crm for startups': {
'my_urls': ['https://mysite.com/crm-guide'],
'watch_competitors': True
},
'how to automate lead scoring': {
'my_urls': ['https://mysite.com/lead-scoring'],
'watch_competitors': True
}
}
def fetch_citations(query: str) -> list[str]:
response = requests.post(
ENDPOINT,
headers={'x-api-key': API_KEY},
json={'query': query, 'country_code': 'us'}
)
response.raise_for_status()
data = response.json()
ai_overview = data.get('ai_overview', {})
return [c.get('url', '') for c in ai_overview.get('citations', [])]
def load_snapshot() -> dict:
if SNAPSHOT_FILE.exists():
return json.loads(SNAPSHOT_FILE.read_text())
return {}
def diff_citations(old: list[str], new: list[str]) -> dict:
old_set, new_set = set(old), set(new)
return {'added': list(new_set - old_set), 'removed': list(old_set - new_set)}
def run_monitor():
snapshot = load_snapshot()
new_snapshot = {}
for query, config in MONITOR_CONFIG.items():
current = fetch_citations(query)
new_snapshot[query] = current
previous = snapshot.get(query, [])
changes = diff_citations(previous, current)
if changes['added'] or changes['removed']:
print(f'[{datetime.now().isoformat()}] Changes for: {query}')
for url in changes['added']:
label = 'MY URL' if url in config['my_urls'] else 'COMPETITOR'
print(f' + ADDED ({label}): {url}')
for url in changes['removed']:
label = 'MY URL' if url in config['my_urls'] else 'COMPETITOR'
print(f' - REMOVED ({label}): {url}')
SNAPSHOT_FILE.write_text(json.dumps(new_snapshot, indent=2))
print(f'Snapshot saved with {len(new_snapshot)} queries')
if __name__ == '__main__':
run_monitor()Ejemplo en JavaScript
const API_KEY = process.env.SCAVIO_API_KEY || 'your_scavio_api_key';
const ENDPOINT = 'https://api.scavio.dev/api/v1/search';
const fs = require('fs');
const SNAPSHOT_FILE = 'citation_snapshots.json';
const MONITOR_CONFIG = {
'best crm for startups': {
myUrls: ['https://mysite.com/crm-guide'],
watchCompetitors: true
},
'how to automate lead scoring': {
myUrls: ['https://mysite.com/lead-scoring'],
watchCompetitors: true
}
};
async function fetchCitations(query) {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us' })
});
if (!response.ok) throw new Error('HTTP ' + response.status);
const data = await response.json();
const aiOverview = data.ai_overview || {};
return (aiOverview.citations || []).map(c => c.url || '');
}
function loadSnapshot() {
if (fs.existsSync(SNAPSHOT_FILE)) {
return JSON.parse(fs.readFileSync(SNAPSHOT_FILE, 'utf-8'));
}
return {};
}
function diffCitations(oldList, newList) {
const oldSet = new Set(oldList);
const newSet = new Set(newList);
return {
added: [...newSet].filter(u => !oldSet.has(u)),
removed: [...oldSet].filter(u => !newSet.has(u))
};
}
async function main() {
const snapshot = loadSnapshot();
const newSnapshot = {};
for (const [query, config] of Object.entries(MONITOR_CONFIG)) {
const current = await fetchCitations(query);
newSnapshot[query] = current;
const previous = snapshot[query] || [];
const changes = diffCitations(previous, current);
if (changes.added.length || changes.removed.length) {
console.log('[' + new Date().toISOString() + '] Changes for: ' + query);
changes.added.forEach(url => {
const label = config.myUrls.includes(url) ? 'MY URL' : 'COMPETITOR';
console.log(' + ADDED (' + label + '): ' + url);
});
changes.removed.forEach(url => {
const label = config.myUrls.includes(url) ? 'MY URL' : 'COMPETITOR';
console.log(' - REMOVED (' + label + '): ' + url);
});
}
}
fs.writeFileSync(SNAPSHOT_FILE, JSON.stringify(newSnapshot, null, 2));
console.log('Snapshot saved with ' + Object.keys(newSnapshot).length + ' queries');
}
main().catch(console.error);Salida esperada
{
"search_metadata": { "query": "best crm for startups", "country_code": "us" },
"ai_overview": {
"text": "The best CRM for startups depends on team size and budget...",
"citations": [
{ "url": "https://mysite.com/crm-guide", "title": "Top CRM Tools for Startups" },
{ "url": "https://competitor.com/crm-review", "title": "CRM Comparison 2026" }
]
}
}