TikTok Comments Scraper avatar

TikTok Comments Scraper

Pricing

from $30.00 / 1,000 results

Go to Apify Store
TikTok Comments Scraper

TikTok Comments Scraper

Scrape the comments of one or MANY TikTok videos in a single run. Returns text, author, likes, thread structure (parent/reply) and the source video itself. No login, no cookies — uses a real browser so TikTok signs its own API requests.

Pricing

from $30.00 / 1,000 results

Rating

0.0

(0)

Developer

code craker

code craker

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

Apify actor that scrapes the comments of one or many TikTok videos in a single run and exports them as structured data.

This actor drives a real Chrome (Playwright) so TikTok's own JavaScript signs its API requests, then intercepts the /api/comment/list/ (and /api/comment/list/reply/) responses — the same browser-interception pattern as the Reddit and YouTube comment scrapers.

⚠️ A logged-in session is required

Unlike TikTok's public hashtag/search feeds (which our tiktok-post-search-scraper reads logged-out), the comment endpoint is auth-gated. For a logged-out guest, TikTok answers /api/comment/list/ with an empty HTTP 200 body no matter how clean the exit IP is — verified across many fresh residential IPs. The video's comment panel then silently falls back to the "You may like" tab and no comments load. This is the same gating we found on Facebook comments.

So provide a logged-in TikTok session via the cookies input (export it with a "Get cookies.txt" / EditThisCookie-style browser extension while signed in to tiktok.com). With a session the comment API returns real data; without one, a run still succeeds but collects only the source video(s) and 0 comments, logging a clear warning. For a fully hands-off option, use a vendor (clockworks/tiktok-comments-scraper) that maintains accounts server-side, and keep this actor as the session-backed fallback (see Integration below).

Features

  • Batched: pass many video URLs and they are all scraped in ONE run (maxComments applies per video). Results are pushed after each video, so an abort keeps everything collected so far.
  • Any video reference works: tiktok.com/@user/video/<id>, photo posts, or a bare numeric video id. (Short vm.tiktok.com links are not supported — use the full URL.)
  • Replies included with thread structure (parentCommentId + replyLevel), or top-level comments only (includeReplies: false).
  • The source video is emitted too (flagged is_source_post: true) with caption, author and engagement counts; disable with includeSourcePost: false.
  • Anti-detection built in: stealth fingerprint, residential-proxy preflight, and relaunch on a fresh IP with a per-video retry when TikTok serves a captcha / empty responses.

Input

{
"urls": [
"https://www.tiktok.com/@nasa/video/7401234567890123456",
"https://www.tiktok.com/@bbc/video/7409999999999999999"
],
"maxComments": 100,
"includeReplies": true,
"includeSourcePost": true,
"region": "US",
"cookies": [ { "name": "sessionid", "value": "…", "domain": ".tiktok.com", "path": "/" } ],
"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" }
}

cookies accepts the exported cookie array (objects with name/value/domain) or a raw name=value; name2=value2 Cookie-header string. It is optional but strongly recommended — see the session note above.

startUrls ([{ "url": ... }]) is accepted as an alternative to urls. resultsLimit and maxReplies are accepted as aliases for maxComments.

Output

One dataset item per comment. Every item carries videoId / aweme_id / videoWebUrl / postUrl / postId / inputUrl, so comments from a multi-video run are always attributable to their source video.

{
"id": "7500000000000000001",
"cid": "7500000000000000001",
"text": "First! love this",
"createTimeISO": "2026-07-01T09:30:12.000Z",
"diggCount": 152, "likesCount": 152,
"replyCommentTotal": 3, "repliesCount": 3,
"uniqueId": "fan1",
"author": {
"name": "Fan One", "username": "fan1", "id": "111",
"profileUrl": "https://www.tiktok.com/@fan1", "avatar": "https://a/av.jpg"
},
"videoId": "7401234567890123456",
"aweme_id": "7401234567890123456",
"videoWebUrl": "https://www.tiktok.com/@nasa/video/7401234567890123456",
"parentCommentId": null,
"repliesToId": null,
"replyLevel": 0,
"isReply": false,
"postId": "7401234567890123456",
"inputUrl": "https://www.tiktok.com/@nasa/video/7401234567890123456",
"is_source_post": false
}
  • parentCommentId is null for a top-level comment; otherwise the id of the comment it replies to. TikTok reply ids point at the parent (nested replies point at the reply they answer).
  • createTimeISO is exact — TikTok exposes real comment timestamps.

Integration (scraping-tool)

Drop-in for the existing TikTok comment path — the output matches the clockworks/tiktok-comments-scraper shape that saveCommentsWithPostLink() and _matchCommentToPost() already read (cid, text, createTimeISO, diggCount, replyCommentTotal, uniqueId, videoWebUrl, aweme_id, repliesToId).

Because the comment endpoint is auth-gated (see the session note above), make the vendor the primary (it maintains accounts server-side, so it needs no session from us) and use this actor as the session-backed fallback — the same arrangement as the Facebook comment path. When you have a maintained TikTok session in the store, pass it as cookies.

// ACTOR_FALLBACKS in scrapingService.js — vendor primary, ours as the fallback
'clockworks/tiktok-comments-scraper': 'outspoken_strategy/tiktok-comments-scraper',
// scrapingService.scrapeCommentsBatch — tiktok branch
} else if (actor === 'outspoken_strategy/tiktok-comments-scraper') {
delete input.startUrls;
input.urls = urls;
input.maxComments = cappedComments(input.maxComments ?? resultsLimit);
input.includeSourcePost = input.includeSourcePost ?? false;
input.includeReplies = input.includeReplies ?? false; // match FB: top-level only
input.cookies = input.cookies ?? getTikTokSessionCookies(); // required: comment API is auth-gated
input.proxyConfiguration = input.proxyConfiguration ?? { useApifyProxy: true, apifyProxyGroups: ['RESIDENTIAL'], apifyProxyCountry: 'US' };
}

_postMatchKey('tiktok', videoWebUrl) extracts the id via /\/video\/(\d+)/, and every comment also carries videoId/aweme_id — so attribution works unchanged.

Local development

npm install
echo '{ "urls": ["https://www.tiktok.com/@tiktok/video/7106594312292453675"], "maxComments": 30, "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" } }' > storage/key_value_stores/default/INPUT.json
npm start

TikTok flags IPs that make many requests, so local runs from a home IP may be served empty responses — the actor is designed to run on Apify residential proxies. Deploy with apify push. Remember that without a cookies session the comment API returns empty bodies (see the session note at the top), so a cookie-less run will collect only the source video(s).