Para detectar tendencias virales de TikTok de forma programática, descargas los vídeos recientes de un hashtag mediante una API, lees el play_count y el create_time de cada vídeo y los ordenas por una puntuación de velocidad (reproducciones divididas por las horas transcurridas desde la publicación), de modo que los que más rápido suben quedan arriba antes de llegar a su pico. Ese es todo el truco: una tendencia no es qué vídeo tiene más reproducciones ahora mismo, sino cuál las gana más rápido. Un clip con 80.000 reproducciones y cuatro horas de vida está más caliente que otro estancado en 2 millones tras tres semanas. Este tutorial monta esa canalización con tres endpoints de TikTok de Scavio: /hashtag devuelve el id de un challenge y sus estadísticas agregadas, /hashtag/videos devuelve las subidas recientes con métricas por vídeo, y /search/videos contrasta los clips que despegan por palabra clave. Cada llamada cuesta 1 crédito (0,005 $), usa autenticación Bearer y devuelve JSON estructurado sin proxies ni CAPTCHA que gestionar. Calcularás la velocidad en pocas líneas, paginarás correctamente (cursor numérico para los feeds de hashtag y de búsqueda) y terminarás con una lista ordenada de clips al alza que puedes consultar de forma periódica. La misma clave API también llega a Google, Amazon y YouTube, así que un sonido que sube en TikTok puede convertirse en una consulta de producto en la llamada siguiente.
Requisitos previos
- Una clave API de Scavio (el registro gratuito incluye 50 créditos de un solo uso)
- Python 3.9+ con la librería requests, o Node 18+ para la versión en JS
- Tu clave exportada como SCAVIO_API_KEY en el entorno
- Un hashtag o palabra clave que quieras vigilar (por ejemplo, un nicho donde encaje tu producto)
Guia paso a paso
Paso 1: Resuelve el hashtag a un id de challenge y sus estadísticas base
Llama a /hashtag con el hashtag a secas (sin el símbolo #) para obtener su challenge_id junto con los recuentos agregados de visualizaciones y vídeos. Esos números agregados son tu línea base: un hashtag que sumó millones de vídeos esta semana ya está saturado, mientras que uno más pequeño que sube rápido es donde encuentras a los que despuntan pronto. Guarda el challenge_id, identifica el mismo challenge entre peticiones.
import os, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
r = requests.post("https://api.scavio.dev/api/v1/tiktok/hashtag", headers=H,
json={"hashtag": "quietluxury"})
tag = r.json()
print(tag["challenge_id"], tag.get("view_count"), tag.get("video_count"))Paso 2: Descarga los vídeos recientes del hashtag con paginación
Llama a /hashtag/videos con el hashtag y un count. La respuesta te da las subidas recientes, cada una con play_count, digg_count (me gusta) y create_time (una marca de tiempo Unix). Para profundizar, pasa el cursor numérico que devolvió la página anterior a la siguiente petición. Este feed es la materia prima para el ranking de velocidad. No uses max_cursor aquí: ese campo es solo de /user/posts.
videos = []
cursor = 0
for _ in range(3): # three pages
resp = requests.post("https://api.scavio.dev/api/v1/tiktok/hashtag/videos",
headers=H, json={"hashtag": "quietluxury", "count": 30, "cursor": cursor}).json()
videos.extend(resp["videos"])
cursor = resp.get("cursor", 0)
if not resp.get("has_more"):
break
print(len(videos), "videos collected")Paso 3: Calcula una puntuación de velocidad y ordena a los que suben
La velocidad es el play_count dividido por las horas transcurridas desde que se publicó el vídeo. Convierte create_time a horas-atrás con el tiempo Unix actual, protege la división contra cero para clips recién subidos y ordena de mayor a menor. Lo más alto de esta lista es lo que se acelera ahora, no lo que ya ganó. Afina la fórmula después (pondera el digg_count, penaliza clips antiguos), pero las reproducciones por hora son una primera señal fuerte y barata.
import time
now = time.time()
def velocity(v):
age_hours = max((now - v["create_time"]) / 3600, 0.5)
return v["play_count"] / age_hours
ranked = sorted(videos, key=velocity, reverse=True)
for v in ranked[:5]:
print(round(velocity(v)), "plays/hr", v["video_id"], v["play_count"])Paso 4: Contrasta por palabra clave los clips que despegan
Un feed de hashtag puede pasar por alto vídeos que se hacen virales bajo un texto o un sonido en lugar de la etiqueta. Llama a /search/videos con una palabra clave (el nombre del sonido, una frase, un producto) y el mismo patrón de cursor numérico para capturarlos. Fusiona ambas listas de vídeos, deduplica por video_id y vuelve a ordenar. Ejecuta toda la canalización de forma periódica (cada pocas horas) y compara las listas ordenadas para ver qué empieza a subir.
search = requests.post("https://api.scavio.dev/api/v1/tiktok/search/videos",
headers=H, json={"query": "quiet luxury outfit", "count": 30, "cursor": 0}).json()
seen = {v["video_id"] for v in videos}
for v in search["videos"]:
if v["video_id"] not in seen:
videos.append(v)
seen.add(v["video_id"])
print(len(videos), "unique videos after cross-check")Ejemplo en Python
import os, time, requests
H = {"Authorization": f"Bearer {os.environ['SCAVIO_API_KEY']}", "Content-Type": "application/json"}
BASE = "https://api.scavio.dev/api/v1/tiktok"
def hashtag_videos(hashtag, pages=3, count=30):
out, cursor = [], 0
for _ in range(pages):
resp = requests.post(f"{BASE}/hashtag/videos", headers=H,
json={"hashtag": hashtag, "count": count, "cursor": cursor}).json()
out.extend(resp.get("videos", []))
cursor = resp.get("cursor", 0)
if not resp.get("has_more"):
break
return out
def search_videos(query, count=30):
resp = requests.post(f"{BASE}/search/videos", headers=H,
json={"query": query, "count": count, "cursor": 0}).json()
return resp.get("videos", [])
def velocity(v, now):
age_hours = max((now - v["create_time"]) / 3600, 0.5)
return v["play_count"] / age_hours
def detect_trends(hashtag, keyword):
tag = requests.post(f"{BASE}/hashtag", headers=H, json={"hashtag": hashtag}).json()
print("challenge", tag["challenge_id"], "views", tag.get("view_count"))
videos = hashtag_videos(hashtag)
seen = {v["video_id"] for v in videos}
for v in search_videos(keyword):
if v["video_id"] not in seen:
videos.append(v); seen.add(v["video_id"])
now = time.time()
ranked = sorted(videos, key=lambda v: velocity(v, now), reverse=True)
for v in ranked[:5]:
print(round(velocity(v, now)), "plays/hr", v["video_id"], v["play_count"], v["digg_count"])
return ranked
if __name__ == "__main__":
detect_trends("quietluxury", "quiet luxury outfit")Ejemplo en JavaScript
const H = {
Authorization: `Bearer ${process.env.SCAVIO_API_KEY}`,
"Content-Type": "application/json",
};
const BASE = "https://api.scavio.dev/api/v1/tiktok";
async function post(path, body) {
const r = await fetch(`${BASE}${path}`, {
method: "POST", headers: H, body: JSON.stringify(body),
});
return r.json();
}
async function hashtagVideos(hashtag, pages = 3, count = 30) {
const out = [];
let cursor = 0;
for (let i = 0; i < pages; i++) {
const resp = await post("/hashtag/videos", { hashtag, count, cursor });
out.push(...(resp.videos || []));
cursor = resp.cursor || 0;
if (!resp.has_more) break;
}
return out;
}
function velocity(v, now) {
const ageHours = Math.max((now - v.create_time) / 3600, 0.5);
return v.play_count / ageHours;
}
async function detectTrends(hashtag, keyword) {
const tag = await post("/hashtag", { hashtag });
console.log("challenge", tag.challenge_id, "views", tag.view_count);
const videos = await hashtagVideos(hashtag);
const seen = new Set(videos.map((v) => v.video_id));
const search = await post("/search/videos", { query: keyword, count: 30, cursor: 0 });
for (const v of search.videos || []) {
if (!seen.has(v.video_id)) { videos.push(v); seen.add(v.video_id); }
}
const now = Date.now() / 1000;
const ranked = videos.sort((a, b) => velocity(b, now) - velocity(a, now));
for (const v of ranked.slice(0, 5)) {
console.log(Math.round(velocity(v, now)), "plays/hr", v.video_id, v.play_count, v.digg_count);
}
return ranked;
}
detectTrends("quietluxury", "quiet luxury outfit");Salida esperada
challenge 7012XXXXXXXXXXXXX views 4XX,XXX,XXX
45 videos collected
52 unique videos after cross-check
Top movers by velocity (plays per hour):
~9,800 plays/hr video_id 73210... play_count 88,000 digg_count 9,400 (posted ~9h ago)
~6,100 plays/hr video_id 73208... play_count 73,000 digg_count 7,100 (posted ~12h ago)
~3,400 plays/hr video_id 73199... play_count 61,000 digg_count 5,200 (posted ~18h ago)
~1,900 plays/hr video_id 73185... play_count 410,000 digg_count 38,000 (posted ~9d ago)
~1,200 plays/hr video_id 73176... play_count 27,000 digg_count 2,300 (posted ~22h ago)
Nota: el clip de 88 mil reproducciones supera al de 410 mil porque se acelera mucho más rápido. Los números de arriba son valores ilustrativos, no métricas reales.