TikTok Hashtag, Search & Profile Scraper avatar

TikTok Hashtag, Search & Profile Scraper

Pricing

from $10.00 / 1,000 results

Go to Apify Store
TikTok Hashtag, Search & Profile Scraper

TikTok Hashtag, Search & Profile Scraper

Extract TikTok videos from any hashtag page without dealing with TikTok's anti-bot defenses, sign-in walls, or undocumented APIs.

Pricing

from $10.00 / 1,000 results

Rating

0.0

(0)

Developer

Tin

Tin

Maintained by Community

Actor stats

0

Bookmarked

7

Total users

3

Monthly active users

9 days ago

Last modified

Categories

Share

Extract TikTok videos from any hashtag page, search query, or user profile without dealing with TikTok's anti-bot defenses, sign-in walls, or undocumented APIs. Paste one or more hashtag, search, or profile URLs and get back a structured dataset of videos with full metadata: captions, authors, play counts, likes, music, hashtags, and direct video URLs.

Download your data as JSON, CSV, Excel, HTML, or XML directly from the Apify console.

What you can do with the data

  • Trend discovery — track which videos are gaining traction inside any hashtag, who their creators are, and how engagement (plays, likes, shares) scales over time.
  • Creator research — identify rising creators inside a niche by sorting hashtag results by play count or engagement ratio.
  • Music intelligence — see which tracks are driving views inside a hashtag; useful for music labels, artist managers, and ad targeting.
  • Competitive monitoring — run scheduled scrapes of hashtags relevant to your brand or competitor campaigns and watch the leaderboard shift.
  • Dataset building — collect labeled video data (caption, hashtags, music, stats) for ML training or content classification.
  • Audience research — opt-in to commentsPerPost to pull comment text + commenter handles alongside each video, useful for sentiment analysis, FAQ mining, or surfacing high-intent viewers in a niche.

Input

FieldTypeRequiredDescription
startUrlsarrayyesTikTok hashtag URLs (https://www.tiktok.com/tag/<name>), search URLs (https://www.tiktok.com/search?q=<query>), or profile URLs (https://www.tiktok.com/@<username>). Individual video URLs are rejected at startup.
maxItemsintegeryesMax videos to collect per URL (default: 10, range: 1–500).
commentsPerPostintegernoHow many top-level comments to fetch per video, embedded into the video record under comments. Default 0 (skip comments). Range: 0–500. Enabling this navigates to each video's page individually — a value of 50 across 50 videos can add several minutes to the run.
countryCodestringnoExit-node country for the Apify residential proxy. One of US, DE, VN, FR, GB. Default: US. Choose a country close to where the audience is concentrated for the most relevant results.

Example input

{
"startUrls": [
{ "url": "https://www.tiktok.com/tag/funny" },
{ "url": "https://www.tiktok.com/search?q=cooking%20tips" },
{ "url": "https://www.tiktok.com/@tranthanh123" }
],
"maxItems": 50,
"commentsPerPost": 20,
"countryCode": "US"
}

Output

Each scraped video is pushed to the dataset as one record. Multiple URLs run in parallel; the sourceType, sourceHashtag, sourceQuery, sourceUsername, and sourceUrl fields let you tell records apart downstream.

Example output record

{
"sourceType": "hashtag",
"sourceHashtag": "funny",
"sourceQuery": null,
"sourceUsername": null,
"sourceUrl": "https://www.tiktok.com/tag/funny",
"id": "7596344392374455570",
"caption": "Life With Two Cats 😂 | Part 111 #cat #catsoftiktok #funnyvideos #funnycat #funnycats",
"createTime": 1768661758,
"url": "https://www.tiktok.com/@funnyanimalshub/video/7596344392374455570",
"author": {
"id": "7511384928216138808",
"uniqueId": "funnyanimalshub",
"nickname": "Funny Animals hub",
"avatar": "https://p16-common-sign.tiktokcdn.com/...jpeg",
"verified": false
},
"stats": {
"playCount": 23100000,
"diggCount": 3200000,
"commentCount": 4852,
"shareCount": 606600,
"collectCount": 197600
},
"video": {
"duration": 65,
"width": 576,
"height": 1024,
"cover": "https://p16-common-sign.tiktokcdn.com/...image",
"dynamicCover": "https://p16-common-sign.tiktokcdn.com/...image",
"playUrl": "https://v16-webapp-prime.tiktok.com/video/...",
"downloadUrl": "https://v16-webapp-prime.tiktok.com/video/..."
},
"music": {
"id": "7596344513577388807",
"title": "som original",
"authorName": "Funny Animals hub",
"original": true,
"playUrl": "https://v16m.tiktokcdn.com/..."
},
"hashtags": ["cat", "catsoftiktok", "funnyvideos", "funnycat", "funnycats"],
"comments": [
{
"commentId": "7640259390033167112",
"text": "this cat is a whole mood",
"createTime": 1778886530,
"likeCount": 12,
"replyCount": 0,
"user": {
"id": "7298733093128307714",
"uniqueId": "zaii.bg",
"nickname": "kunn 🐍",
"avatar": "https://p16-common-sign.tiktokcdn.com/...jpg"
}
}
]
}

The comments array is only present when commentsPerPost > 0. When it's 0 (the default), the field is omitted.

Field reference

  • sourceType / sourceHashtag / sourceQuery / sourceUsername / sourceUrl — which input URL produced this record (set per item so concurrent scrapes don't get jumbled). sourceType is "hashtag", "search", or "profile"; sourceHashtag is populated only for hashtag URLs, sourceQuery only for search URLs, sourceUsername only for profile URLs.
  • id / url — TikTok video ID and canonical web URL.
  • caption / createTime — raw caption text and Unix timestamp of upload.
  • author — uploader's TikTok ID, @handle (uniqueId), display nickname, avatar, and verified flag.
  • stats — play / like (diggCount) / comment / share / save (collectCount) counts at scrape time.
  • video — duration in seconds, dimensions, static cover, animated cover, and the watermarked play URL plus the no-watermark downloadUrl. Note: the play/download URLs are signed and expire within a few hours — re-scrape or download immediately if you need the bytes.
  • music — sound metadata, including whether it's an original sound and a direct play URL.
  • hashtags — list of hashtags parsed from the caption.
  • comments — (optional, only when commentsPerPost > 0) top-level comments fetched by visiting each video's page. Each entry has commentId, text, createTime, likeCount, replyCount, and user (id, uniqueId, nickname, avatar). Reply threads are not included; only the first-level comments.

How it works

This actor uses PuppeteerCrawler (Crawlee) with a Chromium browser, the puppeteer-extra-plugin-stealth patch set, and Apify fingerprint generation to mimic a real desktop browser. Per request it:

  1. Loads tiktok.com first as a "warm-up" — drifting the mouse, scrolling, and waiting a few seconds so that session cookies and the msToken settle. Cold sessions hitting /tag/<name> or /search directly get the well-known empty 200 soft-block.
  2. Navigates to the target page and intercepts the relevant AJAX response — /api/challenge/item_list/ for hashtag pages, /api/search/general/full/ (or /api/search/item/full/) for search pages, /api/post/item_list/ for profile pages — to extract the structured video list. Search responses are unwrapped (data[].item) so the downstream output schema matches the other flows.
  3. Scrolls to load more videos until either maxItems is reached or the API reports no more results.
  4. (Optional) If commentsPerPost > 0, navigates the same browser session to each collected video's page, intercepts /api/comment/list/, scrolls the comment panel, and embeds the captured comments into that video's record before pushing to the dataset.

Heavy resources (images, media, fonts, stylesheets) and tracking hosts (analytics endpoints, GA, Facebook, DoubleClick) are blocked at the request level to cut bandwidth and speed up the run.

Notes and limitations

  • Use a residential proxy in production. TikTok soft-blocks fresh datacenter IPs and IPs with scraper history by returning HTTP 200 with an empty body. The actor will log TikTok API non-200 or Failed to parse TikTok API JSON when this happens — switching to RESIDENTIAL proxy almost always fixes it.
  • Hashtag, search, and profile URLs only. Single-video URLs (/@user/video/123…) are rejected at startup. Use a different actor for those.
  • Signed media URLs expire. playUrl, downloadUrl, cover, and avatar URLs are signed by TikTok with x-expires parameters — typically a few hours. Download or pin them immediately if needed.
  • Region-locked content. Some hashtags return different videos depending on proxy IP region. Set countryCode to pin reproducible results.
  • commentsPerPost adds wall-clock time linearly. Each video requires its own navigation + scroll cycle (~10–30s depending on how many comments load). 50 videos × 50 comments easily adds 10+ minutes to a run. Use it sparingly, or run separate scrapes for the video list and the comments if you only need comments on a small subset.
  • Only top-level comments. Reply threads under each comment are not fetched. replyCount tells you how many replies exist if you need to spot threads worth fetching by other means.

Contact

Found a bug or want a feature? Email dtrungtin@gmail.com.

For more scrapers, browse the Apify Store.