Analyze TikTok Post Performance by Views

Site tiktok.comTask analyze-tiktok-post-performanceVersion v2Updated Aug 10, 2026Category social-media

Collect TikTok posts from an account for a date range, resolve each post's published time and view count from TikTok page state or its same-origin item-detail endpoint, and rank results from highest to lowest views. This skill was captured from a live agent session on tiktok.com and publishes here verbatim, exactly as an agent receives it.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Purpose

Produce a performance report for a TikTok account over a requested date range. Return each qualifying post's canonical TikTok URL, post type, published timestamp, view count, and rank ordered from highest to lowest views. Do not infer missing metrics or include unrelated platform names in the report.

When to Use

Use when the caller provides a TikTok username and asks for post analytics, view totals, or performance ranking over a period such as the previous two months. “All posts” means all posts TikTok exposes through the public profile and individual post pages; private, deleted, restricted, or unrendered posts cannot be included.

Workflow

  1. Normalize {username} by removing a leading @, then navigate directly to https://www.tiktok.com/@{username}. Do not visit the homepage.
  2. On the profile, progressively scroll and accumulate canonical links matching /@{username}/video/{numeric-id} or /@{username}/photo/{numeric-id} until two consecutive scroll cycles produce no new IDs and the document height is stable. Use this evaluator in one call; it retains IDs across virtualization:
(async () => {
  const posts = new Map();
  const collect = () => {
    for (const a of document.querySelectorAll(
      'a[href*="/video/"],a[href*="/photo/"]',
    )) {
      const href = a.href || a.getAttribute("href") || "";
      if (
        !href.includes("/@") ||
        (!href.includes("/video/") && !href.includes("/photo/"))
      )
        continue;
      const m = href.match(/\/(?:video|photo)\/(\d+)/);
      if (!m) continue;
      const id = m[1];
      posts.set(id, {
        id,
        url: href.split("?")[0].split("#")[0],
        type: href.includes("/photo/") ? "photo" : "video",
      });
    }
  };
  let unchanged = 0,
    previousCount = 0,
    previousHeight = 0;
  for (let i = 0; i < 120 && unchanged < 4; i++) {
    collect();
    window.scrollTo(0, document.documentElement.scrollHeight);
    await new Promise((r) => setTimeout(r, 1400));
    collect();
    const height = document.documentElement.scrollHeight,
      count = posts.size;
    unchanged =
      count === previousCount && height === previousHeight ? unchanged + 1 : 0;
    previousCount = count;
    previousHeight = height;
  }
  collect();
  return JSON.stringify({
    profileUrl: location.href.split("?")[0],
    posts: [...posts.values()],
  });
})();
  1. For each collected URL, navigate directly to that canonical URL. Prefer the observed same-origin endpoint https://www.tiktok.com/api/item/detail/?itemId={post-id} for metric resolution. Run this evaluator on the loaded TikTok post page; it fetches only TikTok's endpoint and falls back to the embedded page state when the endpoint is unavailable or does not expose a matching object:
(async () => {
  const url = location.href.split("?")[0].split("#")[0];
  const target = (url.match(/\/(?:video|photo)\/(\d+)/) || [])[1] || null;
  const roots = [];
  try {
    const r = await fetch("/api/item/detail/?itemId=" + encodeURIComponent(target), {
      credentials: "include",
    });
    if (r.ok) {
      const j = await r.json();
      roots.push({ id: "item-detail", root: j });
    }
  } catch {}
  for (const id of ["__UNIVERSAL_DATA_FOR_REHYDRATION__", "SIGI_STATE"]) {
    const el = document.getElementById(id);
    if (!el) continue;
    try {
      roots.push({ id, root: JSON.parse(el.textContent) });
    } catch {}
  }
  const seen = new WeakSet(),
    hits = [];
  function walk(v, path) {
    if (!v || typeof v !== "object" || seen.has(v)) return;
    seen.add(v);
    const value = String(v.id ?? v.awemeId ?? v.itemId ?? "");
    if (target && value === target) {
      const s = v.stats || v.statistics || v.statsV2 || {};
      hits.push({
        path,
        id: target,
        createTime: v.createTime ?? v.create_time ?? null,
        stats: {
          views:
            s.playCount ??
            s.play_count ??
            s.viewCount ??
            s.view_count ??
            s.views ??
            null,
          likes: s.diggCount ?? s.digg_count ?? s.likeCount ?? null,
          comments: s.commentCount ?? s.comment_count ?? null,
          shares: s.shareCount ?? s.share_count ?? null,
        },
        author:
          v.author?.uniqueId ??
          v.author?.unique_id ??
          v.authorInfo?.uniqueId ??
          null,
      });
    }
    for (const [k, x] of Object.entries(v)) walk(x, path + "." + k);
  }
  for (const x of roots) walk(x.root, x.id);
  return JSON.stringify({
    url,
    postId: target,
    postType: url.includes("/photo/") ? "photo" : "video",
    source: roots[0]?.id || null,
    match: hits[0] || null,
  });
})();
  1. Convert createTime from Unix seconds or milliseconds to an ISO timestamp. Keep only posts whose timestamp falls within the caller's inclusive {start-date} through {end-date} range. If no timestamp resolves, place the post in unresolved rather than guessing its date.
  2. Sort qualifying posts by numeric views descending. Put posts with missing view counts after posts with known counts and mark those metrics unavailable. Return canonical TikTok URLs and the ranked report. The endpoint is a same-origin optimization, not a guarantee; retain the embedded-state fallback.

Site-Specific Gotchas

  • TikTok profile grids are lazy-loaded and may virtualize older cards; accumulate links during scrolling rather than reading only the final DOM.
  • The same post may be addressable as /video/{id} or /photo/{id}. Preserve the canonical type and numeric ID discovered from the profile; do not fabricate a URL variant.
  • TikTok exposes a same-origin item-detail route at /api/item/detail/?itemId={post-id}. The itemId is the numeric ID captured from the post URL; never guess it. Endpoint availability can depend on the current session or regional state.
  • Individual post pages also embed richer state in __UNIVERSAL_DATA_FOR_REHYDRATION__ or SIGI_STATE, where the matching object commonly stores createTime and stats.playCount.
  • The post ID must be matched against the embedded or endpoint object's id, awemeId, or itemId; never use the first stats object because it may belong to another recommendation.
  • View counts may be absent, localized in visible text, or unavailable behind login, consent, regional, or bot-check screens. Preserve null rather than treating it as zero.
  • Profile discovery and per-post metric resolution are separate stages: the profile supplies the collection, while each post URL or its item-detail response supplies authoritative date and metric state.
  • Infinite scrolling, rate limits, and deleted or private posts can make the discovered collection incomplete. Report the accessible count and any unresolved posts.

Expected Output

Return an object such as:

{
  "profileUrl": "https://www.tiktok.com/@{username}",
  "dateRange": { "start": "{start-date}", "end": "{end-date}" },
  "accessiblePosts": 0,
  "matchedPosts": 0,
  "rankedPosts": [
    {
      "rank": 1,
      "url": "https://www.tiktok.com/@{username}/video/{post-id}",
      "type": "video",
      "publishedAt": "{ISO-timestamp}",
      "views": 0,
      "likes": null,
      "comments": null,
      "shares": null
    }
  ],
  "unresolved": []
}

Use only values observed from TikTok page state or the same-origin TikTok item-detail response, include canonical TikTok URLs, and do not mention unrelated platforms in the report.

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=tiktok.com&task=analyze-tiktok-post-performance