Resumen
Consultas Amazon producto listings diario for tracked competidor ASINs o producto names, almacena price history, y alertas cuando un caidas de precio mas than 10% de el anterior dia.
Desencadenador
Diario cron at 7 AM
Programación
Diario at 7 AM (cron: 0 7 * * *)
Pasos del flujo de trabajo
Cargar tracked productos
Leer producto names o consultas de busqueda de un productos tabla (product_name, asin, last_price, last_checked).
Search Amazon for cada producto
POST to Scavio search API con plataforma: amazon for cada producto consulta. Extraer price, titulo, y URL de el top coincidencia resultado.
Analizar y normalize price
Strip currency symbols y convert price string to float. Handle 'de $X' y rango prices by taking el minimo.
Comparar to anterior price
Cargar last_price de el base de datos. Calcular cambio de precio porcentaje. Marcar if caida exceeds 10%.
Almacenar price history
Insert nuevo fila en price_history tabla con product_name, price, date, y url. Actualizar last_price in productos tabla.
Enviar alerta for significativo drops
For flagged productos, enviar Slack webhook o correo electronico alerta con producto nombre, old price, nuevo price, cambio porcentaje, y Amazon URL.
Implementacion en Python
import sqlite3
import requests
import re
from datetime import date
import time
DB_PATH = "price_monitor.db"
SCRAVIO_KEY = "YOUR_API_KEY"
ALERT_THRESHOLD = 0.10 # 10% drop
ALERT_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK"
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.executescript("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY, product_name TEXT, last_price REAL, last_checked TEXT
);
CREATE TABLE IF NOT EXISTS price_history (
product_name TEXT, price REAL, date TEXT, url TEXT
);
""")
conn.commit()
return conn
def parse_price(price_str: str) -> float | None:
if not price_str:
return None
match = re.search(r'[\d,]+\.?\d*', price_str.replace(',', ''))
return float(match.group()) if match else None
def fetch_amazon_price(query: str) -> dict | None:
resp = requests.post(
"https://api.scavio.dev/api/v1/search",
headers={"x-api-key": SCRAVIO_KEY},
json={"query": query, "platform": "amazon"}
)
resp.raise_for_status()
results = resp.json().get("results", [])
if not results:
return None
top = results[0]
return {"title": top.get("title"), "price": parse_price(top.get("price", "")), "url": top.get("url")}
def run():
conn = init_db()
today = date.today().isoformat()
products = conn.execute("SELECT id, product_name, last_price FROM products").fetchall()
for pid, name, last_price in products:
result = fetch_amazon_price(name)
if not result or result["price"] is None:
time.sleep(0.5)
continue
new_price = result["price"]
conn.execute("INSERT INTO price_history VALUES (?,?,?,?)", (name, new_price, today, result["url"]))
conn.execute("UPDATE products SET last_price=?, last_checked=? WHERE id=?", (new_price, today, pid))
if last_price and last_price > 0:
drop_pct = (last_price - new_price) / last_price
if drop_pct >= ALERT_THRESHOLD:
requests.post(ALERT_WEBHOOK, json={
"text": f"Price drop alert: {name} dropped {drop_pct*100:.1f}% from ${last_price:.2f} to ${new_price:.2f}. {result['url']}"
})
conn.commit()
time.sleep(0.5)
if __name__ == "__main__":
run()
Implementacion en JavaScript
const Database = require('better-sqlite3');
const fetch = require('node-fetch');
const DB_PATH = 'price_monitor.db';
const SCRAVIO_KEY = 'YOUR_API_KEY';
const ALERT_THRESHOLD = 0.10;
const ALERT_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK';
const db = new Database(DB_PATH);
db.exec(`
CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY, product_name TEXT, last_price REAL, last_checked TEXT);
CREATE TABLE IF NOT EXISTS price_history (product_name TEXT, price REAL, date TEXT, url TEXT);
`);
function parsePrice(str) {
if (!str) return null;
const m = str.replace(/,/g, '').match(/[\d]+\.?\d*/);
return m ? parseFloat(m[0]) : null;
}
async function fetchAmazonPrice(query) {
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, platform: 'amazon' })
});
const data = await res.json();
const top = (data.results || [])[0];
if (!top) return null;
return { title: top.title, price: parsePrice(top.price), url: top.url };
}
async function run() {
const today = new Date().toISOString().slice(0, 10);
const products = db.prepare('SELECT id, product_name, last_price FROM products').all();
for (const { id, product_name, last_price } of products) {
const result = await fetchAmazonPrice(product_name);
if (!result || result.price == null) { await new Promise(r => setTimeout(r, 500)); continue; }
db.prepare('INSERT INTO price_history VALUES (?,?,?,?)').run(product_name, result.price, today, result.url);
db.prepare('UPDATE products SET last_price=?, last_checked=? WHERE id=?').run(result.price, today, id);
if (last_price && last_price > 0) {
const drop = (last_price - result.price) / last_price;
if (drop >= ALERT_THRESHOLD) {
await fetch(ALERT_WEBHOOK, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: `Price drop: ${product_name} dropped ${(drop*100).toFixed(1)}% from $${last_price.toFixed(2)} to $${result.price.toFixed(2)}. ${result.url}` })
});
}
}
await new Promise(r => setTimeout(r, 500));
}
}
run().catch(console.error);
Plataformas utilizadas
Amazon
Búsqueda de productos con precios, calificaciones y reseñas