Resumen
Searches Google diario for competidor precios paginas, extrae precios tier informacion de SERP fragmentos, y escribe structured precios datos to un panel de control-ready base de datos tabla.
Desencadenador
Diario cron at 6 AM
Programación
Diario at 6 AM (cron: 0 6 * * *)
Pasos del flujo de trabajo
Cargar competidor lista
Leer competidor names y dominios de un competidores tabla. Estos son el companies to rastrear precios for.
Search for precios paginas
For cada competidor, POST to Scavio search API con consulta '[competidor nombre] precios 2026 sitio:[competidor dominio]'. Extraer top resultado URL y fragmento.
Analizar precios de fragmento
Use regex y palabra clave coincidencia to extraer price menciones de el fragmento: dollar amounts, 'per mes', 'per ano', 'free', 'enterprise'. Structure as tier objetos.
Almacenar structured precios datos
Insert competitor_name, date, raw_snippet, parsed_tiers (JSON), source_url en competitor_pricing tabla.
Marcar precios cambios
Comparar today's parsed_tiers contra yesterday's for cada competidor. Si tiers differ, marcar as changed y almacenar diff.
Actualizar panel de control
El competitor_pricing tabla es queried by el panel de control directamente. Optionally enviar un diario resumen of cualquier precios cambios detectado.
Implementacion en Python
import sqlite3
import requests
import re
import json
from datetime import date, timedelta
import time
DB_PATH = "competitor_pricing.db"
SCRAVIO_KEY = "YOUR_API_KEY"
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.executescript("""
CREATE TABLE IF NOT EXISTS competitors (name TEXT, domain TEXT);
CREATE TABLE IF NOT EXISTS competitor_pricing (
competitor TEXT, date TEXT, raw_snippet TEXT,
parsed_tiers TEXT, source_url TEXT, changed INTEGER DEFAULT 0,
PRIMARY KEY (competitor, date)
);
""")
conn.commit()
return conn
def search_pricing(name: str, domain: str) -> dict | None:
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCRAVIO_KEY},
json={"query": f"{name} pricing 2026 site:{domain}", "platform": "google", "num": 3}
)
resp.raise_for_status()
results = resp.json().get("results", [])
if not results:
return None
r = results[0]
return {"snippet": r.get("snippet", ""), "url": r.get("url", "")}
def parse_pricing_tiers(snippet: str) -> list:
tiers = []
# Find price mentions: $X/mo, $X/month, $X/year, $X per month
price_pattern = r'\$([\d,]+)(?:\.\d+)?(?:/mo|/month|/year|\s+per\s+month|\s+per\s+year)?'
prices = re.findall(price_pattern, snippet, re.IGNORECASE)
if "free" in snippet.lower():
tiers.append({"tier": "free", "price": 0})
for i, price in enumerate(prices):
price_val = float(price.replace(',', ''))
tiers.append({"tier": f"tier_{i+1}", "price": price_val})
if "enterprise" in snippet.lower() or "contact" in snippet.lower():
tiers.append({"tier": "enterprise", "price": None})
return tiers
def run():
conn = init_db()
today = date.today().isoformat()
yesterday = (date.today() - timedelta(days=1)).isoformat()
competitors = conn.execute("SELECT name, domain FROM competitors").fetchall()
changes = []
for name, domain in competitors:
result = search_pricing(name, domain)
if not result:
time.sleep(0.5)
continue
tiers = parse_pricing_tiers(result["snippet"])
tiers_json = json.dumps(tiers)
prev = conn.execute(
"SELECT parsed_tiers FROM competitor_pricing WHERE competitor=? AND date=?",
(name, yesterday)
).fetchone()
changed = 0
if prev and prev[0] != tiers_json:
changed = 1
changes.append(f"{name}: pricing tiers changed")
conn.execute(
"INSERT OR REPLACE INTO competitor_pricing VALUES (?,?,?,?,?,?)",
(name, today, result["snippet"], tiers_json, result["url"], changed)
)
conn.commit()
time.sleep(0.5)
if changes:
print("Pricing changes detected:", changes)
if __name__ == "__main__":
run()
Implementacion en JavaScript
const Database = require('better-sqlite3');
const fetch = require('node-fetch');
const DB_PATH = 'competitor_pricing.db';
const SCRAVIO_KEY = 'YOUR_API_KEY';
const db = new Database(DB_PATH);
db.exec(`
CREATE TABLE IF NOT EXISTS competitors (name TEXT, domain TEXT);
CREATE TABLE IF NOT EXISTS competitor_pricing (competitor TEXT, date TEXT, raw_snippet TEXT, parsed_tiers TEXT, source_url TEXT, changed INTEGER DEFAULT 0, PRIMARY KEY (competitor, date));
`);
async function searchPricing(name, domain) {
const res = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: { 'x-api-key': SCRAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query: `${name} pricing 2026 site:${domain}`, platform: 'google', num: 3 })
});
const results = (await res.json()).results || [];
if (!results.length) return null;
return { snippet: results[0].snippet || '', url: results[0].url || '' };
}
function parseTiers(snippet) {
const tiers = [];
if (/free/i.test(snippet)) tiers.push({ tier: 'free', price: 0 });
const prices = [...snippet.matchAll(/\$([\d,]+)(?:\.\d+)?/g)].map(m => parseFloat(m[1].replace(/,/g,'')));
prices.forEach((p, i) => tiers.push({ tier: `tier_${i+1}`, price: p }));
if (/enterprise|contact/i.test(snippet)) tiers.push({ tier: 'enterprise', price: null });
return tiers;
}
async function run() {
const today = new Date().toISOString().slice(0,10);
const yesterday = new Date(Date.now()-86400000).toISOString().slice(0,10);
const competitors = db.prepare('SELECT name, domain FROM competitors').all();
for (const { name, domain } of competitors) {
const result = await searchPricing(name, domain);
if (!result) { await new Promise(r=>setTimeout(r,500)); continue; }
const tiers = JSON.stringify(parseTiers(result.snippet));
const prev = db.prepare('SELECT parsed_tiers FROM competitor_pricing WHERE competitor=? AND date=?').get(name, yesterday);
const changed = prev && prev.parsed_tiers !== tiers ? 1 : 0;
if (changed) console.log(`${name}: pricing changed`);
db.prepare('INSERT OR REPLACE INTO competitor_pricing VALUES (?,?,?,?,?,?)').run(name, today, result.snippet, tiers, result.url, changed);
await new Promise(r=>setTimeout(r,500));
}
}
run().catch(console.error);
Plataformas utilizadas
Búsqueda web con grafo de conocimiento, PAA y resúmenes de IA