Overview
This workflow scans trending TikTok hashtags daily to detect products gaining viral traction. It tracks engagement velocity across product-related hashtags, scores videos by view-to-engagement ratios, and surfaces products showing early viral signals. The output feeds into product research pipelines for dropshippers and DTC brand scouts.
Trigger
Cron schedule (daily at 10 AM UTC)
Schedule
Daily 7 AM
Workflow Steps
Load product hashtag list
Read the list of product-related TikTok hashtags to monitor from configuration.
Fetch trending videos per hashtag
Call the Scavio TikTok API for each hashtag and retrieve trending video data.
Calculate engagement scores
Compute engagement rate, view velocity, and creator authenticity signals for each video.
Filter high-signal products
Keep only videos with engagement above threshold and views suggesting early traction.
Deduplicate and rank
Group by product, deduplicate across hashtags, and rank by combined engagement signal.
Output product opportunities
Save ranked product list with engagement data and send top opportunities to Slack.
Python Implementation
import requests
import json
from datetime import datetime
from pathlib import Path
from collections import defaultdict
API_KEY = "your_scavio_api_key"
BASE_URL = "https://api.scavio.dev/api/v1/tiktok"
PRODUCT_HASHTAGS = [
"tiktokmademebuyit",
"amazonfinds",
"viralproducts",
"dropshipping2026",
"tiktokshop",
"musthave",
"gadgets",
"homefinds",
]
MIN_VIEWS = 50000
MIN_ENGAGEMENT_RATE = 0.04
def fetch_hashtag_videos(hashtag: str) -> list[dict]:
res = requests.post(
f"{BASE_URL}/hashtag",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"hashtag": hashtag},
timeout=15,
)
res.raise_for_status()
return res.json().get("videos", [])
def score_video(video: dict) -> dict | None:
views = video.get("views", 0)
likes = video.get("likes", 0)
shares = video.get("shares", 0)
comments = video.get("comments", 0)
if views < MIN_VIEWS:
return None
engagement_rate = (likes + shares + comments) / views if views > 0 else 0
if engagement_rate < MIN_ENGAGEMENT_RATE:
return None
return {
"video_id": video.get("id", ""),
"description": video.get("description", "")[:200],
"creator": video.get("creator", ""),
"views": views,
"likes": likes,
"shares": shares,
"engagement_rate": round(engagement_rate, 4),
"score": round(engagement_rate * (views / 100000), 2),
}
def run():
opportunities = defaultdict(list)
for hashtag in PRODUCT_HASHTAGS:
videos = fetch_hashtag_videos(hashtag)
for video in videos:
scored = score_video(video)
if scored:
scored["hashtag"] = hashtag
opportunities[scored["creator"]].append(scored)
# Rank by best single video score
ranked = []
for creator, videos in opportunities.items():
best = max(videos, key=lambda x: x["score"])
ranked.append(best)
ranked.sort(key=lambda x: x["score"], reverse=True)
date = datetime.utcnow().strftime("%Y-%m-%d")
report = {
"date": date,
"total_opportunities": len(ranked),
"top_products": ranked[:20],
}
Path(f"tiktok_products_{date}.json").write_text(json.dumps(report, indent=2))
print(f"Found {len(ranked)} product opportunities")
for item in ranked[:5]:
print(f" {item['hashtag']} | {item['views']:,} views | {item['engagement_rate']:.1%} ER | score: {item['score']}")
if __name__ == "__main__":
run()JavaScript Implementation
const API_KEY = "your_scavio_api_key";
const BASE_URL = "https://api.scavio.dev/api/v1/tiktok";
const PRODUCT_HASHTAGS = [
"tiktokmademebuyit", "amazonfinds", "viralproducts", "dropshipping2026",
"tiktokshop", "musthave", "gadgets", "homefinds",
];
const MIN_VIEWS = 10000;
const MIN_ENGAGEMENT = 0.03;
async function fetchHashtagVideos(hashtag) {
const res = await fetch(BASE_URL + "/hashtag/videos", {
method: "POST",
headers: { Authorization: "Bearer " + API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ hashtag }),
});
if (!res.ok) throw new Error("tiktok " + res.status);
const data = await res.json();
return data.data?.videos || [];
}
function scoreVideo(video) {
const views = video.play_count || 0;
if (views < MIN_VIEWS) return null;
const likes = video.digg_count || 0;
const shares = video.share_count || 0;
const comments = video.comment_count || 0;
const er = views > 0 ? (likes + shares + comments) / views : 0;
if (er < MIN_ENGAGEMENT) return null;
return { videoId: video.id, creator: video.creator, views, likes, shares, er: Math.round(er * 10000) / 10000, score: Math.round(er * (views / 100000) * 100) / 100 };
}
async function run() {
const opportunities = {};
for (const hashtag of PRODUCT_HASHTAGS) {
const videos = await fetchHashtagVideos(hashtag);
for (const video of videos) {
const scored = scoreVideo(video);
if (scored) {
scored.hashtag = hashtag;
if (!opportunities[scored.creator]) opportunities[scored.creator] = [];
opportunities[scored.creator].push(scored);
}
}
}
const ranked = Object.values(opportunities).map(vids => vids.sort((a, b) => b.score - a.score)[0]).sort((a, b) => b.score - a.score);
console.log("Found " + ranked.length + " product opportunities");
ranked.slice(0, 5).forEach(item => console.log(" " + item.hashtag + " | " + item.views + " views | score: " + item.score));
}
run();Platforms Used
TikTok
Trending video, creator, and product discovery