Resumen
Polls tracked campana hashtags cada 6 horas, registros video conteo y engagement totals, calculates crecimiento velocity, y flags viral moments cuando video conteo o engagement grows mas than 50% in un 6-hora window.
Desencadenador
Every 6 horas (cron: 0 */6 * * *)
Programación
Every 6 horas (cron: 0 */6 * * *)
Pasos del flujo de trabajo
Obtener hashtag stats
POST to Scavio TikTok hashtag endpoint for cada tracked hashtag. Extraer video_count y total_view_count de el respuesta.
Almacenar snapshot
Escribir hashtag, video_count, view_count, y marca de tiempo to un snapshots tabla.
Calcular crecimiento velocity
Comparar actual video_count y view_count contra el snapshot de 6 horas ago. Calcular new_videos y new_views in el window.
Detectar viral umbral
Marcar if new_videos in el window exceeds un umbral (e.g., 200 nuevo videos in 6 horas for un mid-size campana). Thresholds son configurable per hashtag.
Alert on viral deteccion
Enviar Slack o webhook alerta con hashtag nombre, nuevo video conteo, view velocity (views per hora), y un TikTok enlace to el hashtag pagina.
Implementacion en Python
import sqlite3
import requests
from datetime import datetime, timedelta
import time
DB_PATH = "tiktok_campaigns.db"
SCRAVIO_KEY = "YOUR_API_KEY"
API_BASE = "https://api.scavio.dev/api/v1/tiktok"
HEADERS = {"Authorization": f"Bearer {SCRAVIO_KEY}"}
ALERT_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK"
VIRAL_VIDEO_THRESHOLD = 200 # new videos per 6h window
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.executescript("""
CREATE TABLE IF NOT EXISTS hashtags (hashtag TEXT PRIMARY KEY, viral_threshold INTEGER DEFAULT 200);
CREATE TABLE IF NOT EXISTS snapshots (
hashtag TEXT, video_count INTEGER, view_count INTEGER, ts TEXT
);
""")
conn.commit()
return conn
def fetch_hashtag_stats(hashtag: str) -> dict:
resp = requests.post(
f"{API_BASE}/hashtag/info",
headers=HEADERS,
json={"hashtag": hashtag}
)
resp.raise_for_status()
data = resp.json().get("data", {})
return {
"video_count": data.get("videoCount", 0),
"view_count": data.get("viewCount", 0)
}
def run():
conn = init_db()
now = datetime.utcnow().isoformat()
six_hours_ago = (datetime.utcnow() - timedelta(hours=6)).isoformat()
hashtags = conn.execute("SELECT hashtag, viral_threshold FROM hashtags").fetchall()
for hashtag, threshold in hashtags:
stats = fetch_hashtag_stats(hashtag)
conn.execute("INSERT INTO snapshots VALUES (?,?,?,?)",
(hashtag, stats["video_count"], stats["view_count"], now))
prev = conn.execute(
"SELECT video_count, view_count FROM snapshots "
"WHERE hashtag=? AND ts <= ? ORDER BY ts DESC LIMIT 1",
(hashtag, six_hours_ago)
).fetchone()
if prev:
new_videos = stats["video_count"] - prev[0]
new_views = stats["view_count"] - prev[1]
views_per_hour = new_views // 6
if new_videos >= (threshold or VIRAL_VIDEO_THRESHOLD):
requests.post(ALERT_WEBHOOK, json={
"text": f"Viral alert #{hashtag}: +{new_videos} videos in 6h, +{views_per_hour:,} views/hr. https://tiktok.com/tag/{hashtag}"
})
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 = 'tiktok_campaigns.db';
const API_BASE = 'https://api.scavio.dev/api/v1/tiktok';
const SCRAVIO_KEY = 'YOUR_API_KEY';
const HEADERS = { 'Authorization': `Bearer ${SCRAVIO_KEY}`, 'Content-Type': 'application/json' };
const ALERT_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK';
const db = new Database(DB_PATH);
db.exec(`
CREATE TABLE IF NOT EXISTS hashtags (hashtag TEXT PRIMARY KEY, viral_threshold INTEGER DEFAULT 200);
CREATE TABLE IF NOT EXISTS snapshots (hashtag TEXT, video_count INTEGER, view_count INTEGER, ts TEXT);
`);
async function fetchHashtagStats(hashtag) {
const res = await fetch(`${API_BASE}/hashtag/info`, {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ hashtag })
});
const data = (await res.json()).data || {};
return { video_count: data.videoCount || 0, view_count: data.viewCount || 0 };
}
async function run() {
const now = new Date().toISOString();
const sixHoursAgo = new Date(Date.now() - 6 * 3600000).toISOString();
const hashtags = db.prepare('SELECT hashtag, viral_threshold FROM hashtags').all();
for (const { hashtag, viral_threshold } of hashtags) {
const stats = await fetchHashtagStats(hashtag);
db.prepare('INSERT INTO snapshots VALUES (?,?,?,?)').run(hashtag, stats.video_count, stats.view_count, now);
const prev = db.prepare(
'SELECT video_count, view_count FROM snapshots WHERE hashtag=? AND ts <= ? ORDER BY ts DESC LIMIT 1'
).get(hashtag, sixHoursAgo);
if (prev) {
const newVideos = stats.video_count - prev.video_count;
const newViews = stats.view_count - prev.view_count;
if (newVideos >= (viral_threshold || 200)) {
await fetch(ALERT_WEBHOOK, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: `Viral alert #${hashtag}: +${newVideos} videos, +${Math.round(newViews/6).toLocaleString()} views/hr. https://tiktok.com/tag/${hashtag}` })
});
}
}
await new Promise(r => setTimeout(r, 500));
}
}
run().catch(console.error);
Plataformas utilizadas
TikTok
Descubrimiento de videos, creadores y productos en tendencia