Un video de 60.000 visitas con una sección de comentarios plana es una victoria de alcance y un fracaso de intención, y puedes cuantificar esa brecha con la API de TikTok de Scavio en vez de adivinar. Las visitas miden a cuántos teléfonos empujó el algoritmo tu clip. No dicen nada sobre si alguien quería lo que vendes. La intención vive en los comentarios: cuánta gente se molestó en escribir, cuántos preguntaron dónde comprar o cómo funciona, cuántos lo compartieron. Esas señales están en la API. Esta guía descarga las estadísticas del video, baja los comentarios y calcula tres números que te dicen si tu contenido generó demanda o solo pasó de largo. Cada llamada a Scavio cuesta 1 crédito (0,005 $). Lo honesto viene al final: la API mide si hubo intención; no te dice quién se registró ni arregla un embudo roto.
Requisitos previos
- Una API key de Scavio (sk_live_...). 50 créditos gratis al registrarte, sin tarjeta.
- El video_id de la publicación que se hizo viral.
- Python 3 o Node instalado.
Guia paso a paso
Paso 1: Paso 1: Obtén tu perfil y las estadísticas de la publicación
Los endpoints de TikTok funcionan en dos saltos. Primero llama a profile con tu username para conseguir un sec_user_id; luego pásalo a user/posts para listar tus publicaciones recientes con estadísticas: play_count, digg_count (likes), comment_count, share_count. Busca tu video viral en la lista y anota sus cifras crudas. Ese es el denominador de cada ratio de abajo. Dos llamadas, 2 créditos.
import requests
KEY = "sk_live_..."
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
BASE = "https://api.scavio.dev/api/v1/tiktok"
prof = requests.post(f"{BASE}/profile", headers=H, json={"username": "yourhandle"}).json()
sec_user_id = prof["sec_user_id"]
posts = requests.post(f"{BASE}/user/posts", headers=H,
json={"sec_user_id": sec_user_id, "count": 20}).json()
for p in posts["posts"]:
s = p["statistics"]
print(p["video_id"], s["play_count"], s["comment_count"], s["share_count"])Paso 2: Paso 2: Descarga los comentarios del video viral
Ahora trae los comentarios reales. Pasa el video_id y un count (con 100 basta para tomar el pulso). Cada comentario lleva text, digg_count (cuántos le dieron like al comentario) y reply_comment_total (cuántas respuestas generó). Los comentarios que atraen likes y respuestas son las conversaciones que vale la pena leer. 1 crédito.
video_id = "7300000000000000000"
cm = requests.post(f"{BASE}/video/comments", headers=H,
json={"video_id": video_id, "count": 100}).json()
comments = cm["comments"]
print(len(comments), "comments pulled")
for c in comments[:5]:
print(c["digg_count"], c["reply_comment_total"], c["text"])Paso 3: Paso 3: Calcula las tres señales de intención
El alcance son las reproducciones. La intención es lo que la gente hizo más allá de mirar. Calcula el ratio comentario-visita (comentarios / reproducciones) y el ratio compartido-visita (compartidos / reproducciones), y luego la densidad de intención: la proporción de comentarios con un signo de interrogación o una palabra de compra como how, where, link, price o sign up. Usa también los propios comentarios como proxy de guardado y compartido: un clip que la gente envía a un amigo recibe respuestas etiquetando a alguien, y eso aparece como texto. Un patrón claro de alcance sin intención se ve así: muchas visitas, un ratio comentario-visita muy por debajo del 0,3 % y casi ningún comentario que pregunte por tu producto. Significa que al algoritmo le gustó tu gancho pero nadie quería el producto. Si el ratio es sano pero la densidad ronda cero, la gente se enganchó con el entretenimiento e ignoró la oferta. Aquí va lo honesto. La API te da comentarios, conteos y estos proxies; no atribuye registros, porque no lleva seguimiento de embudo, y no puede arreglar la regla de TikTok de que las cuentas con menos de 1.000 seguidores no tienen enlace clicable en la bio, que es un problema de embudo que ninguna llamada de datos resuelve. Así que la API responde a una sola pregunta: ¿tu contenido generó intención? Tu bio y tu landing son un arreglo aparte. La regla de decisión: si la densidad de intención es alta pero los registros están planos, tu contenido funciona y tu embudo está roto, así que arregla primero el enlace y la landing. Si la densidad de intención ronda cero, más alcance no te salvará, así que cambia el contenido y la oferta antes de perseguir otro viral.
INTENT_WORDS = ["how", "where", "link", "price", "cost", "buy", "sign up", "sign-up"]
def has_intent(text):
t = text.lower()
if "?" in t:
return True
return any(w in t for w in INTENT_WORDS)
plays = posts["posts"][0]["statistics"]["play_count"]
shares = posts["posts"][0]["statistics"]["share_count"]
n = len(comments)
intent_n = sum(1 for c in comments if has_intent(c["text"]))
print("comment-to-view: %.3f%%" % (100 * n / plays))
print("share-to-view: %.3f%%" % (100 * shares / plays))
print("intent density: %.1f%%" % (100 * intent_n / n))Ejemplo en Python
import requests
KEY = "sk_live_..."
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
BASE = "https://api.scavio.dev/api/v1/tiktok"
VIDEO_ID = "7300000000000000000"
INTENT_WORDS = ["how", "where", "link", "price", "cost", "buy", "sign up", "sign-up"]
def has_intent(text):
t = text.lower()
return "?" in t or any(w in t for w in INTENT_WORDS)
prof = requests.post(f"{BASE}/profile", headers=H, json={"username": "yourhandle"}).json()
sec_user_id = prof["sec_user_id"]
posts = requests.post(f"{BASE}/user/posts", headers=H,
json={"sec_user_id": sec_user_id, "count": 20}).json()
vid = next(p for p in posts["posts"] if p["video_id"] == VIDEO_ID)
plays = vid["statistics"]["play_count"]
shares = vid["statistics"]["share_count"]
cm = requests.post(f"{BASE}/video/comments", headers=H,
json={"video_id": VIDEO_ID, "count": 100}).json()
comments = cm["comments"]
n = len(comments)
intent_n = sum(1 for c in comments if has_intent(c["text"]))
print("plays: %d" % plays)
print("comment-to-view: %.3f%%" % (100 * n / plays))
print("share-to-view: %.3f%%" % (100 * shares / plays))
print("intent density: %.1f%% (%d of %d)" % (100 * intent_n / n, intent_n, n))Ejemplo en JavaScript
const KEY = "sk_live_...";
const H = { "Authorization": `Bearer ${KEY}`, "Content-Type": "application/json" };
const BASE = "https://api.scavio.dev/api/v1/tiktok";
const VIDEO_ID = "7300000000000000000";
const INTENT_WORDS = ["how", "where", "link", "price", "cost", "buy", "sign up", "sign-up"];
const hasIntent = (t) => {
const s = t.toLowerCase();
return s.includes("?") || INTENT_WORDS.some((w) => s.includes(w));
};
const post = (path, body) =>
fetch(`${BASE}/${path}`, { method: "POST", headers: H, body: JSON.stringify(body) }).then((r) => r.json());
const prof = await post("profile", { username: "yourhandle" });
const posts = await post("user/posts", { sec_user_id: prof.sec_user_id, count: 20 });
const vid = posts.posts.find((p) => p.video_id === VIDEO_ID);
const plays = vid.statistics.play_count;
const shares = vid.statistics.share_count;
const cm = await post("video/comments", { video_id: VIDEO_ID, count: 100 });
const comments = cm.comments;
const n = comments.length;
const intentN = comments.filter((c) => hasIntent(c.text)).length;
console.log(`plays: ${plays}`);
console.log(`comment-to-view: ${(100 * n / plays).toFixed(3)}%`);
console.log(`share-to-view: ${(100 * shares / plays).toFixed(3)}%`);
console.log(`intent density: ${(100 * intentN / n).toFixed(1)}% (${intentN} of ${n})`);Salida esperada
plays: 60214
comment-to-view: 0.142%
share-to-view: 0.061%
intent density: 3.5% (3 of 86)