Overview
Collects new TikTok videos posted under a brand hashtag daily, stores creator and video metadata, and identifies high-engagement UGC for potential reposting or creator outreach.
Trigger
Daily cron at 8 AM
Schedule
Daily at 8 AM (cron: 0 8 * * *)
Workflow Steps
Fetch hashtag video feed
POST to Scavio TikTok hashtag videos endpoint for the brand hashtag. Retrieve latest 50 videos with creator and engagement data.
Filter for new videos
Compare returned video IDs against the seen_videos table. Process only videos not previously collected.
Store video and creator data
Insert video_id, title, author_username, author_sec_uid, view_count, like_count, comment_count, share_count, and created_at into ugc_videos table.
Calculate engagement rate
Compute (likes + comments + shares) / views for each new video. Store as engagement_rate field.
Flag high-engagement UGC
Mark videos with engagement_rate above 5% and view_count above 10,000 as high_priority = 1 for creator outreach or content approval queues.
Generate daily UGC summary
Output daily count of new UGC videos, total new views, and list of high-priority creators for review.
Python Implementation
import sqlite3
import requests
from datetime import date
import time
DB_PATH = "tiktok_ugc.db"
SCRAVIO_KEY = "YOUR_API_KEY"
API_BASE = "https://api.scavio.dev/api/v1/tiktok"
HEADERS = {"Authorization": f"Bearer {SCRAVIO_KEY}"}
BRAND_HASHTAG = "yourbrand"
ENGAGEMENT_THRESHOLD = 0.05
VIEW_THRESHOLD = 10000
def init_db():
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS ugc_videos (
video_id TEXT PRIMARY KEY, hashtag TEXT, author_username TEXT,
author_sec_uid TEXT, title TEXT, view_count INTEGER, like_count INTEGER,
comment_count INTEGER, share_count INTEGER, engagement_rate REAL,
high_priority INTEGER DEFAULT 0, created_at TEXT, collected_date TEXT
)
""")
conn.commit()
return conn
def fetch_hashtag_videos(hashtag: str) -> list:
resp = requests.post(
f"{API_BASE}/hashtag/videos",
headers=HEADERS,
json={"hashtag": hashtag, "count": 50}
)
resp.raise_for_status()
return resp.json().get("data", {}).get("videos", [])
def calc_engagement_rate(video: dict) -> float:
stats = video.get("stats", {})
views = stats.get("playCount", 0)
if not views:
return 0.0
interactions = stats.get("diggCount", 0) + stats.get("commentCount", 0) + stats.get("shareCount", 0)
return round(interactions / views, 4)
def run():
conn = init_db()
today = date.today().isoformat()
videos = fetch_hashtag_videos(BRAND_HASHTAG)
new_count = 0
high_priority = []
for v in videos:
vid_id = v.get("id")
if not vid_id:
continue
existing = conn.execute("SELECT 1 FROM ugc_videos WHERE video_id=?", (vid_id,)).fetchone()
if existing:
continue
stats = v.get("stats", {})
er = calc_engagement_rate(v)
views = stats.get("playCount", 0)
is_priority = 1 if (er >= ENGAGEMENT_THRESHOLD and views >= VIEW_THRESHOLD) else 0
author = v.get("author", {})
conn.execute(
"INSERT OR IGNORE INTO ugc_videos VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
(vid_id, BRAND_HASHTAG, author.get("uniqueId"), author.get("secUid"),
v.get("desc"), views, stats.get("diggCount"), stats.get("commentCount"),
stats.get("shareCount"), er, is_priority, v.get("createTime"), today)
)
new_count += 1
if is_priority:
high_priority.append(author.get("uniqueId"))
conn.commit()
print(f"Today: {new_count} new UGC videos. High-priority creators: {high_priority}")
if __name__ == "__main__":
run()
JavaScript Implementation
const Database = require('better-sqlite3');
const fetch = require('node-fetch');
const DB_PATH = 'tiktok_ugc.db';
const SCRAVIO_KEY = 'YOUR_API_KEY';
const API_BASE = 'https://api.scavio.dev/api/v1/tiktok';
const HEADERS = { 'Authorization': `Bearer ${SCRAVIO_KEY}`, 'Content-Type': 'application/json' };
const BRAND_HASHTAG = 'yourbrand';
const db = new Database(DB_PATH);
db.exec(`CREATE TABLE IF NOT EXISTS ugc_videos (
video_id TEXT PRIMARY KEY, hashtag TEXT, author_username TEXT, author_sec_uid TEXT,
title TEXT, view_count INTEGER, like_count INTEGER, comment_count INTEGER, share_count INTEGER,
engagement_rate REAL, high_priority INTEGER DEFAULT 0, created_at TEXT, collected_date TEXT
)`);
async function fetchHashtagVideos(hashtag) {
const res = await fetch(`${API_BASE}/hashtag/videos`, {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ hashtag, count: 50 })
});
return ((await res.json()).data?.videos) || [];
}
function calcER(v) {
const s = v.stats || {};
const views = s.playCount || 0;
if (!views) return 0;
return ((s.diggCount || 0) + (s.commentCount || 0) + (s.shareCount || 0)) / views;
}
async function run() {
const today = new Date().toISOString().slice(0, 10);
const videos = await fetchHashtagVideos(BRAND_HASHTAG);
let newCount = 0; const highPriority = [];
for (const v of videos) {
if (!v.id || db.prepare('SELECT 1 FROM ugc_videos WHERE video_id=?').get(v.id)) continue;
const er = calcER(v); const views = v.stats?.playCount || 0;
const priority = er >= 0.05 && views >= 10000 ? 1 : 0;
db.prepare('INSERT OR IGNORE INTO ugc_videos VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)').run(
v.id, BRAND_HASHTAG, v.author?.uniqueId, v.author?.secUid, v.desc,
views, v.stats?.diggCount, v.stats?.commentCount, v.stats?.shareCount,
er, priority, v.createTime, today
);
newCount++;
if (priority) highPriority.push(v.author?.uniqueId);
}
console.log(`${newCount} new UGC videos. High-priority: ${highPriority.join(', ')}`);
}
run().catch(console.error);
Platforms Used
TikTok
Trending video, creator, and product discovery