Purpose
Collect a small set of relevant, highly surfaced TikTok videos for a topic such as sports fails or winter-sports mishaps. Use TikTok's direct hashtag/search URL and return canonical video URLs, numeric IDs, labels, captions or card text, and any visible engagement information. TikTok page order is treated as the site's best available viral/popularity ordering; do not invent view or like counts that are not visible.
When to Use
Use when the caller wants a limited number of TikTok clips matching a topic, keyword, or hashtag. Prefer a hashtag when one is known, for example {hashtag} without the #; use keyword search when the topic does not map cleanly to a hashtag. If one hashtag does not expose enough relevant videos, repeat the same procedure with additional caller-approved related hashtags and deduplicate by numeric video ID.
Workflow
- Build the direct destination URL, skipping the homepage:
- Hashtag:
https://www.tiktok.com/tag/{hashtag} - Keyword search fallback:
https://www.tiktok.com/search?q={encodeURIComponent(query)}
Navigate once to the selected URL. Dismiss only a blocking consent, login, or modal overlay if present.
Run this evaluator on the loaded page. It accumulates visible video anchors while allowing lazy loading to expose enough results, deduplicates by the numeric
/video/{id}, and returns the first{limit}surfaced results (use6when the caller requests six):
(async () => {
const limit = 6;
const videos = new Map();
const collect = () => {
for (const a of document.querySelectorAll('a[href*="/video/"]')) {
const href = a.href || a.getAttribute("href") || "";
const id = href.match(/\/video\/(\d+)/)?.[1];
if (!id) continue;
const card =
a.closest("article, li") ||
a.parentElement?.parentElement ||
a.parentElement;
videos.set(id, {
id,
url: href.split("?")[0].split("#")[0],
ariaLabel: a.getAttribute("aria-label") || "",
title: a.getAttribute("title") || "",
imageAlt: a.querySelector("img")?.alt || "",
cardText: (card?.innerText || a.innerText || a.textContent || "")
.trim()
.replace(/\s+/g, " ")
.slice(0, 800),
});
}
};
let unchanged = 0,
previousCount = 0,
previousHeight = 0;
for (let i = 0; i < 8 && videos.size < limit && unchanged < 2; i++) {
collect();
window.scrollTo(0, document.documentElement.scrollHeight);
await new Promise((r) => setTimeout(r, 1200));
collect();
const height = document.documentElement.scrollHeight;
unchanged =
videos.size === previousCount && height === previousHeight ? unchanged + 1 : 0;
previousCount = videos.size;
previousHeight = height;
}
collect();
return JSON.stringify({
sourceUrl: location.href.split("?")[0],
hashtag: location.pathname.match(/^\/tag\/([^/]+)/)?.[1] || null,
query: new URL(location.href).searchParams.get("q"),
requested: limit,
count: Math.min(videos.size, limit),
videos: [...videos.values()].slice(0, limit).map((v) => ({
id: v.id,
url: v.url,
caption: v.imageAlt || v.title || "",
ariaLabel: v.ariaLabel,
cardText: v.cardText,
})),
});
})();- If fewer than
{limit}usable videos are returned, optionally navigate to another relevant hashtag URL and run the same evaluator, then merge and deduplicate byid. For sports-fail discovery, useful direct hashtag candidates includehttps://www.tiktok.com/tag/volleyballfail,https://www.tiktok.com/tag/tennisfail,https://www.tiktok.com/tag/basketballfail,https://www.tiktok.com/tag/baseballfail,https://www.tiktok.com/tag/trackfail,https://www.tiktok.com/tag/snowboardfail,https://www.tiktok.com/tag/skateboardfail, andhttps://www.tiktok.com/tag/iceskatingfail; select only those relevant to the caller's requested sport/topic and preserve the first occurrence of each ID. Keep the caller's topic constraint when choosing supplementary hashtags.
Site-Specific Gotchas
- TikTok topic pages are directly addressable at
/tag/{hashtag}; keyword search uses/search?q={query}. A homepage visit and search-box interaction are unnecessary. - Sports-fail topics commonly have sport-specific hashtag destinations such as
/tag/volleyballfail,/tag/tennisfail,/tag/basketballfail,/tag/baseballfail,/tag/trackfail,/tag/snowboardfail,/tag/skateboardfail, and/tag/iceskatingfail; these are optional supplementary sources, not a restriction on other topics. - Video cards are reliably discoverable through
a[href*="/video/"], but CSS classes and card nesting may change. The extractor therefore uses the anchor as the stable selector and gathers metadata from nearby card text. - Hashtag results may lazy-load as the page scrolls and TikTok can virtualize cards. Accumulating by numeric video ID avoids losing cards that leave the DOM.
- The first surfaced cards are not a guaranteed global ranking. Report visible engagement/card text as evidence and describe them as surfaced or viral candidates rather than asserting popularity without a displayed metric.
- Captions, labels, and engagement text can be empty, localized, or mixed together. Preserve the raw fields and do not parse absent counts as zero.
- Consent dialogs, login prompts, rate limits, regional restrictions, or bot checks can reduce the returned set. Report the observed count instead of fabricating six results.
- When combining several hashtags, deduplicate by the numeric video ID and retain the first occurrence's canonical URL and metadata.
Expected Output
Return JSON with sourceUrl, the resolved hashtag or search query, requested, count, and a deduplicated videos array. Each video contains id, canonical url, caption, ariaLabel, and raw cardText. If fewer than requested are accessible, return the available results and state the limitation.