Encontrar objetivos de guest posting a escala es un problema de footprint de búsqueda: lanzas decenas de queries de "write for us" e intitle: por tu nicho, deduplicas los dominios y entregas los supervivientes a una persona para que juzgue la calidad. Un post popular de r/SEO clavó la restricción real: revisar a mano 1.000+ sitios porque la propia UI de Google agota un footprint rápido y rasparla exige una API de pago. Este tutorial automatiza la mitad de descubrimiento con el endpoint de Google de Scavio y es honesto sobre la mitad que no puedes automatizar: la decisión de calidad sigue necesitando a una persona. Cinco pasos, código ejecutable.
Requisitos previos
- Python 3.8+ o Node 18+ instalado
- Una clave de API de Scavio desde scavio.dev (50 créditos gratis al registrarte)
- requests instalado (pip install requests) para Python
- Un nicho objetivo y 2-3 términos semilla (p. ej. "viajes", "fintech", "decoración del hogar")
Guia paso a paso
Paso 1: Construye el conjunto de queries de footprint
Las páginas de guest posting comparten footprints predecibles. Combina tu nicho con patrones como 'write for us', 'guest post guidelines', 'contribute', 'become a contributor' y variantes intitle:. Cuanto más amplio sea el conjunto de footprints, menos agotas cada query individual, que era justo el dolor del hilo de r/SEO.
NICHE = "home decor"
FOOTPRINTS = [
f'{NICHE} "write for us"',
f'{NICHE} "guest post guidelines"',
f'{NICHE} intitle:"write for us"',
f'{NICHE} "become a contributor"',
f'{NICHE} inurl:guest-post',
]Paso 2: Lanza cada query a través del endpoint de SERP
Haz un POST de cada footprint a https://api.scavio.dev/api/v1/google. Aquí basta una petición ligera (1 crédito): solo necesitas las URLs orgánicas, no el knowledge graph. Pagina los resultados para ampliar la cobertura. Auth Bearer, JSON estructurado de vuelta, sin proxies.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
def search(q):
r = requests.post("https://api.scavio.dev/api/v1/google",
headers=H, json={"query": q})
r.raise_for_status()
return r.json().get("data", {}).get("organic", [])Paso 3: Extrae y deduplica por dominio registrado
Distintos footprints devuelven los mismos sitios, así que deduplica por dominio, no por URL. Reduce al dominio registrable y guárdalo en un set. Aquí es donde una sesión de la UI de Google se desmorona y un script brilla: colapsas cientos de resultados solapados en una lista limpia de prospectos.
from urllib.parse import urlparse
def domain(url):
host = urlparse(url).netloc.lower()
return host[4:] if host.startswith("www.") else host
def collect(footprints):
seen = set()
for q in footprints:
for res in search(q):
d = domain(res.get("link", ""))
if d:
seen.add(d)
return sorted(seen)Paso 4: Filtra la basura evidente de forma automática
Descarta dominios que nunca quieres: los blogs de tus competidores, granjas de enlaces conocidas y plataformas gigantes (medium.com, linkedin.com). Una pequeña blocklist más una comprobación de que el título de la página contiene de verdad una frase de colaborador elimina la mayor parte del ruido antes de que una persona lo mire. Automatiza el filtrado, no el juicio.
BLOCKLIST = {"medium.com", "linkedin.com", "quora.com", "reddit.com"}
def prospects(footprints):
domains = collect(footprints)
return [d for d in domains if d not in BLOCKLIST and "." in d]Paso 5: Entrega los supervivientes a una persona: este es el límite honesto
El script te da una lista de prospectos deduplicada y filtrada en minutos en lugar de días. No puede juzgar si la audiencia, la autoridad y el 'aspecto y sensación' de un sitio merecen el pitch: eso es trabajo humano, exactamente como argumentaba quien posteó en r/SEO. Exporta a CSV y luego que una persona puntúe cada dominio antes del outreach. Trata la API como un acelerador de descubrimiento, no como un sustituto del juicio.
import csv
rows = prospects(FOOTPRINTS)
with open("guest_post_prospects.csv", "w", newline="") as f:
w = csv.writer(f)
w.writerow(["domain", "human_score_0_5", "notes"])
for d in rows:
w.writerow([d, "", ""])
print(f"{len(rows)} prospects exported for manual review")Ejemplo en Python
import os, requests
from urllib.parse import urlparse
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
NICHE = "home decor"
FOOTPRINTS = [f'{NICHE} "write for us"', f'{NICHE} intitle:"write for us"', f'{NICHE} "become a contributor"']
BLOCKLIST = {"medium.com", "linkedin.com", "reddit.com"}
def domain(u):
h = urlparse(u).netloc.lower()
return h[4:] if h.startswith("www.") else h
seen = set()
for q in FOOTPRINTS:
r = requests.post("https://api.scavio.dev/api/v1/google", headers=H, json={"query": q})
for res in r.json().get("data", {}).get("organic", []):
d = domain(res.get("link", ""))
if d and d not in BLOCKLIST:
seen.add(d)
print(sorted(seen))Ejemplo en JavaScript
const H = { Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`, 'Content-Type': 'application/json' };
const NICHE = 'home decor';
const FOOTPRINTS = [`${NICHE} "write for us"`, `${NICHE} intitle:"write for us"`, `${NICHE} "become a contributor"`];
const BLOCKLIST = new Set(['medium.com', 'linkedin.com', 'reddit.com']);
const domain = u => { const h = new URL(u).hostname.toLowerCase(); return h.startsWith('www.') ? h.slice(4) : h; };
const seen = new Set();
for (const q of FOOTPRINTS) {
const r = await fetch('https://api.scavio.dev/api/v1/google', { method: 'POST', headers: H, body: JSON.stringify({ query: q }) }).then(x => x.json());
for (const res of (r.data?.organic || [])) {
try { const d = domain(res.link); if (d && !BLOCKLIST.has(d)) seen.add(d); } catch {}
}
}
console.log([...seen].sort());Salida esperada
['decorhubblog.com', 'homestylejournal.com', 'interiorinsider.net', ...]
# A deduped, junk-filtered list of guest-post prospect domains for your niche,
# ready for the part no API can do: a human scoring each site's authority and fit.