Las caídas en la clasificación pueden pasar desapercibidas durante días si solo verificas manualmente. Para cuando detecte una caída de la posición 3 a la posición 15, ya habrá perdido una cantidad significativa de tráfico. Este tutorial crea un canal de alerta de SEO automatizado que verifica la clasificación de sus palabras clave diariamente a través de la API de Scavio, compara las posiciones actuales con líneas de base anteriores almacenadas en CSV y activa una alerta cuando cualquier palabra clave cae por debajo de un umbral configurado o cae más de un número determinado de posiciones. El proceso cuesta alrededor de $0,005 por palabra clave por día, por lo que monitorear 100 palabras clave cuesta menos de $0,50 por día.
Requisitos previos
- Python 3.8+ instalado
- solicita biblioteca instalada
- Una clave API de Scavio de scavio.dev
- Su dominio y una lista de palabras clave objetivo
Guia paso a paso
Paso 1: Configurar palabras clave y umbrales de alerta
Defina palabras clave para realizar un seguimiento, su dominio objetivo, un umbral de posición sobre el que alertar y un umbral de caída para detectar caídas repentinas.
import os, csv, requests
from datetime import date
API_KEY = os.environ['SCAVIO_API_KEY']
ENDPOINT = 'https://api.scavio.dev/api/v1/search'
HEADERS = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
TARGET_DOMAIN = 'mysite.com'
POSITION_THRESHOLD = 10
DROP_THRESHOLD = 5
KEYWORDS = [
'web scraping api',
'serp api python',
'amazon price api',
'tiktok data api',
]Paso 2: Obtener el rango actual y cargar la línea de base anterior
Consulta el punto final de Scavio Google para cada palabra clave y encuentra la posición de tu dominio. Cargue la última posición registrada desde un archivo histórico CSV para comparar.
def get_rank(keyword):
resp = requests.post(ENDPOINT, headers=HEADERS,
json={'query': keyword, 'country_code': 'us'})
for r in resp.json().get('organic_results', []):
if TARGET_DOMAIN in r.get('link', ''):
return r['position']
return None
def load_baselines(csv_path='rankings_history.csv'):
baselines = {}
try:
with open(csv_path) as f:
rows = list(csv.reader(f))
for row in reversed(rows):
if len(row) >= 3 and row[1] not in baselines and row[2]:
baselines[row[1]] = int(row[2])
except FileNotFoundError:
pass
return baselinesPaso 3: Verificar caídas y generar alertas
Compare las posiciones actuales con las líneas de base. Alerta si la posición excede el umbral o si la caída desde la última verificación excede el umbral de caída.
def run_alert_pipeline():
baselines = load_baselines()
alerts = []
today = date.today().isoformat()
with open('rankings_history.csv', 'a', newline='') as f:
writer = csv.writer(f)
for kw in KEYWORDS:
rank = get_rank(kw)
writer.writerow([today, kw, rank or ''])
prev = baselines.get(kw)
if rank is None:
alerts.append(f'[GONE] {kw}: not found in top results')
elif rank > POSITION_THRESHOLD:
alerts.append(f'[LOW] {kw}: position {rank} (threshold {POSITION_THRESHOLD})')
elif prev and rank - prev >= DROP_THRESHOLD:
alerts.append(f'[DROP] {kw}: {prev} -> {rank} (dropped {rank - prev} positions)')
else:
print(f'[OK] {kw}: position {rank}')
if alerts:
print('\n--- ALERTS ---')
for a in alerts:
print(a)
return alerts
run_alert_pipeline()Ejemplo en Python
import os, csv, requests
from datetime import date
API_KEY = os.environ['SCAVIO_API_KEY']
ENDPOINT = 'https://api.scavio.dev/api/v1/search'
HEADERS = {'x-api-key': API_KEY, 'Content-Type': 'application/json'}
DOMAIN = 'mysite.com'
KEYWORDS = ['web scraping api', 'serp api python', 'amazon price api']
def get_rank(kw):
resp = requests.post(ENDPOINT, headers=HEADERS,
json={'query': kw, 'country_code': 'us'}).json()
for r in resp.get('organic_results', []):
if DOMAIN in r.get('link', ''): return r['position']
return None
def load_prev():
prev = {}
try:
with open('rankings.csv') as f:
for row in reversed(list(csv.reader(f))):
if row[1] not in prev and row[2]: prev[row[1]] = int(row[2])
except FileNotFoundError: pass
return prev
def run():
prev = load_prev()
today = date.today().isoformat()
with open('rankings.csv', 'a', newline='') as f:
w = csv.writer(f)
for kw in KEYWORDS:
rank = get_rank(kw)
w.writerow([today, kw, rank or ''])
p = prev.get(kw)
if rank and p and rank - p >= 5:
print(f'[DROP] {kw}: {p} -> {rank}')
elif rank and rank > 10:
print(f'[LOW] {kw}: position {rank}')
elif rank:
print(f'[OK] {kw}: #{rank}')
else:
print(f'[GONE] {kw}')
if __name__ == '__main__': run()Ejemplo en JavaScript
const fs = require('fs');
const API_KEY = process.env.SCAVIO_API_KEY;
const H = { 'x-api-key': API_KEY, 'Content-Type': 'application/json' };
const DOMAIN = 'mysite.com';
const KEYWORDS = ['web scraping api', 'serp api python'];
async function getRank(kw) {
const data = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: H, body: JSON.stringify({ query: kw, country_code: 'us' })
}).then(r => r.json());
const match = (data.organic_results || []).find(r => r.link.includes(DOMAIN));
return match ? match.position : null;
}
async function run() {
const today = new Date().toISOString().slice(0, 10);
for (const kw of KEYWORDS) {
const rank = await getRank(kw);
fs.appendFileSync('rankings.csv', `${today},${kw},${rank || ''}\n`);
if (!rank) console.log(`[GONE] ${kw}`);
else if (rank > 10) console.log(`[LOW] ${kw}: position ${rank}`);
else console.log(`[OK] ${kw}: #${rank}`);
}
}
run().catch(console.error);Salida esperada
[OK] web scraping api: #3
[OK] serp api python: #5
[DROP] amazon price api: 4 -> 11
--- ALERTS ---
[DROP] amazon price api: 4 -> 11 (dropped 7 positions)