La detección de robo de contenido con una API SERP funciona buscando frases distintivas de su propio contenido y verificando si otros dominios las han publicado sin permiso. Este tutorial le muestra cómo extraer oraciones únicas de sus artículos, consultar una API de búsqueda para obtener resultados de coincidencia exacta y marcar dominios que vuelven a publicar su trabajo para que pueda presentar solicitudes de eliminación o ajustar su estrategia de SEO.
Requisitos previos
- Clave API de Scavio (250 créditos gratuitos al mes en scavio.dev)
- Python 3.9+ o Node.js 18+
- Una lista de URL o contenido de texto que desea monitorear
Guia paso a paso
Paso 1: Extrae frases únicas de tu contenido
Seleccione de 3 a 5 oraciones distintivas de cada artículo que desee monitorear. Evite frases genéricas que aparecerían en muchos sitios. Elija oraciones con puntos de datos específicos, frases únicas o terminología de marca que solo aparecerían en otros lugares si se copiaran.
content_fingerprints = {
'https://yourblog.com/api-comparison-2026': [
'"The effective cost per query drops 38% when you factor in structured data extraction"',
'"Batch endpoints amortize cold-start latency across 50-query chunks"',
'"Three providers silently inject affiliate links into knowledge panel results"',
],
'https://yourblog.com/serp-monitoring-guide': [
'"Position tracking without render-side JS execution misses 23% of dynamic SERP features"',
'"Weekly crawl cadence catches ranking drops 4.2 days late on average"',
],
}Paso 2: Busque cada huella digital a través de la API
Consulte la API de búsqueda de Scavio con cada frase de huellas dactilares entre comillas dobles para obtener una coincidencia exacta. Cualquier resultado que no sea de tu propio dominio es un potencial ladrón de contenidos.
import requests
API_KEY = 'your_scavio_api_key'
matches = []
for source_url, phrases in content_fingerprints.items():
for phrase in phrases:
resp = requests.post(
'https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'query': phrase, 'num': 10}
)
data = resp.json()
for item in data.get('organic', []):
if 'yourblog.com' not in item.get('url', ''):
matches.append({
'source': source_url,
'phrase': phrase,
'thief_url': item['url'],
'thief_title': item.get('title', ''),
})Paso 3: Deduplicar y anotar coincidencias
Agrupe las coincidencias por dominio y cuente cuántas frases de huellas dactilares coincidió con cada dominio. Es casi seguro que un dominio que coincida con varias frases únicas esté eliminando su contenido en lugar de utilizar un lenguaje similar por coincidencia.
from collections import Counter
from urllib.parse import urlparse
domain_counts = Counter()
for m in matches:
domain = urlparse(m['thief_url']).netloc
domain_counts[domain] += 1
print('Domains with most matches:')
for domain, count in domain_counts.most_common(10):
print(f' {domain}: {count} phrase matches')Paso 4: Generar un reporte de robo
Exporte los resultados a un informe estructurado con la URL de origen, la frase coincidente, la URL ofensiva y el recuento de coincidencias. Este informe es lo que necesita para solicitudes de eliminación de DMCA o para enviarlo a su equipo legal.
import json
report = {
'scan_date': '2026-05-12',
'total_matches': len(matches),
'unique_domains': len(domain_counts),
'top_offenders': [
{'domain': d, 'phrase_matches': c}
for d, c in domain_counts.most_common(10)
],
'all_matches': matches,
}
with open('content_theft_report.json', 'w') as f:
json.dump(report, f, indent=2)
print(f'Report saved: {len(matches)} matches across {len(domain_counts)} domains')Ejemplo en Python
import requests
import json
from collections import Counter
from urllib.parse import urlparse
API_KEY = 'your_scavio_api_key'
YOUR_DOMAIN = 'yourblog.com'
content_fingerprints = {
'https://yourblog.com/api-comparison-2026': [
'"The effective cost per query drops 38% when you factor in structured data extraction"',
'"Batch endpoints amortize cold-start latency across 50-query chunks"',
],
'https://yourblog.com/serp-monitoring-guide': [
'"Position tracking without render-side JS execution misses 23% of dynamic SERP features"',
],
}
matches = []
for source_url, phrases in content_fingerprints.items():
for phrase in phrases:
resp = requests.post(
'https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'query': phrase, 'num': 10}
)
data = resp.json()
for item in data.get('organic', []):
if YOUR_DOMAIN not in item.get('url', ''):
matches.append({
'source': source_url,
'phrase': phrase,
'thief_url': item['url'],
'thief_title': item.get('title', ''),
})
domain_counts = Counter(urlparse(m['thief_url']).netloc for m in matches)
report = {
'scan_date': '2026-05-12',
'total_matches': len(matches),
'unique_domains': len(domain_counts),
'top_offenders': [
{'domain': d, 'phrase_matches': c}
for d, c in domain_counts.most_common(10)
],
'all_matches': matches,
}
with open('content_theft_report.json', 'w') as f:
json.dump(report, f, indent=2)
print(f'Scan complete: {len(matches)} matches across {len(domain_counts)} domains')Ejemplo en JavaScript
const fs = require('fs');
const API_KEY = 'your_scavio_api_key';
const YOUR_DOMAIN = 'yourblog.com';
const contentFingerprints = {
'https://yourblog.com/api-comparison-2026': [
'"The effective cost per query drops 38% when you factor in structured data extraction"',
'"Batch endpoints amortize cold-start latency across 50-query chunks"',
],
'https://yourblog.com/serp-monitoring-guide': [
'"Position tracking without render-side JS execution misses 23% of dynamic SERP features"',
],
};
async function main() {
const matches = [];
for (const [sourceUrl, phrases] of Object.entries(contentFingerprints)) {
for (const phrase of phrases) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: phrase, num: 10 }),
});
const data = await resp.json();
for (const item of data.organic || []) {
if (!item.url?.includes(YOUR_DOMAIN)) {
matches.push({
source: sourceUrl,
phrase,
thief_url: item.url,
thief_title: item.title || '',
});
}
}
}
}
const domainCounts = {};
for (const m of matches) {
const domain = new URL(m.thief_url).hostname;
domainCounts[domain] = (domainCounts[domain] || 0) + 1;
}
const sorted = Object.entries(domainCounts).sort((a, b) => b[1] - a[1]);
const report = {
scan_date: '2026-05-12',
total_matches: matches.length,
unique_domains: sorted.length,
top_offenders: sorted.slice(0, 10).map(([domain, count]) => ({ domain, phrase_matches: count })),
all_matches: matches,
};
fs.writeFileSync('content_theft_report.json', JSON.stringify(report, null, 2));
console.log(`Scan complete: ${matches.length} matches across ${sorted.length} domains`);
}
main();Salida esperada
Scan complete: 7 matches across 4 domains