TikTok Scraper With Engagement Data
Pricing
from $3.99 / 1,000 results
TikTok Scraper With Engagement Data
TikTok Scraper extracts TikTok video data with engagement metrics, including views, likes, comments, shares, saves, captions, hashtags, creators, and video URLs. Ideal for trend tracking, competitor analysis, content research, and audience insights.
Pricing
from $3.99 / 1,000 results
Rating
0.0
(0)
Developer
Scrapio
Maintained by CommunityActor stats
0
Bookmarked
10
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
TikTok Scraper — Extract Videos, Reposts and Search Results
TikTok Scraper With Engagement Data pulls profile videos, reposts, and hashtag or keyword search results from TikTok and returns typed JSON — no HTML, no parsing. Every row carries the platform's raw stats (views, likes, comments, shares) plus derived engagementRate, likeViewRatio, and commentViewRatio fields computed locally from that same batch, alongside an optional best-posting-time summary row per profile or query. Unlike scraping frameworks that hand back raw HTML for you to parse, this Actor returns typed fields ready for your model, your spreadsheet, or your pipeline without any parsing step. This guide covers every input, every output field, and how teams actually deploy it for monitoring, enrichment, and dataset builds.
🧭 What Does TikTok Scraper With Engagement Data Do?
TikTok Scraper With Engagement Data extracts four kinds of TikTok content from one input schema: a creator's profile videos, a creator's reposts, hashtag search results, and keyword search results. It runs against TikTok's public web pages and its internal web API — no TikTok account, login, or API key is required to start a run. On every successful row it also computes engagementRate, likeViewRatio, and commentViewRatio locally from the same stats it just fetched, and can build a best-posting-time summary row per profile or search query.
- 📹 Scrapes every video on a public profile, paginated through TikTok's own creator item-list endpoint
- 🔄 Scrapes a profile's Reposts tab, captured via a headless browser session
- #️⃣ Runs hashtag search and returns every matched item
- 🔑 Runs keyword search and returns every matched item
- 📈 Computes
engagementRate,likeViewRatio,commentViewRatioper row (optional, on by default) - 🕒 Builds a
topDay/topHourposting-time summary per profile or query (optional, on by default) - 🌐 Escalates automatically to an alternate connection when a request is blocked, times out, or comes back as an interstitial page
⚡ Features & Capabilities
TikTok Scraper With Engagement Data covers four scrape modes, derived engagement analytics, and connection resilience — all inside one input schema and one dataset.
Core features
- Profile videos — every video on a public profile, paginated via TikTok's creator item-list endpoint until no pages remain
- Profile reposts — captured by driving a real Chromium session to the profile's Reposts tab and intercepting
/api/repost/item_listresponses - Hashtag search — items matched against a
#hashtag, captured from TikTok's own search API responses - Keyword search — items matched against a free-text query, captured the same way
- Typed fields on every profile-video row:
videoId,videoTitle,videoUrl,videoCreatedAt,videoAuthor,videoViews,videoLikes,videoComments,videoShares - Derived analytics computed with no extra network requests:
engagementRate,likeViewRatio,commentViewRatio— returned asnull(never a fabricated0) when the source view count is missing or zero - Posting-time summary rows —
topDay,topHour,sampleSize, plus a full weekday/hour histogram, per profile or per search query
What sets this scraper apart
No verified competitor data was available at the time of writing, so this section names this Actor's own capabilities rather than a rival's. Most single-purpose TikTok scrapers cover one content type — only profile videos, or only search. This Actor covers profile videos, reposts, hashtag search, and keyword search from one input schema and one dataset, with engagement ratios computed locally on every row at no extra request cost. If your use case is feeding structured data to an LLM, the output format is the decision-maker: every field returns as a typed JSON primitive, not HTML you have to parse inside an agent loop — HTML parsing inside an agent loop is a reliability failure mode, not a feature.
When another tool might suit you better
This Actor returns metadata, stats, and CDN media URLs — not comment threads and not downloaded video files. The keyword-search item payload carries a comments field, but TikTok's own search API leaves it empty; only commentCount is populated. If your workflow needs actual comment text or bulk video downloads, a scraper built specifically for that will fit better. If you only need a single one-off profile check, a narrower single-mode scraper will also do less work per run than this Actor's full multi-mode pipeline.
TikTok Scraper With Engagement Data within the Scrapio data stack
This is Scrapio's TikTok Actor for this platform — it covers profile videos, reposts, and both search modes with analytics built in. For the same short-form-video and creator-monitoring workflow on adjacent platforms, pair it with Snapchat Spotlight Scraper: Recommended & Trending Videos, Snapchat User Spotlight Scraper With Comment Threads, or Instagram Post Details Scraper With Comments & Replies — all in the same account.
Why do developers and data teams scrape TikTok?
🏢 Marketing and social media teams
Marketing teams feed startUrls with their own or a competitor's profile handles and pull back every video with videoViews, videoLikes, videoComments, videoShares, and the derived engagementRate per post. That feeds a weekly campaign report or a spreadsheet dashboard without anyone opening TikTok Studio by hand. Hashtag and keyword search modes do the same for a branded hashtag or a product-name query — every matching video's itemViews/itemLikes/itemComments land in the same dataset, so reach and engagement for an organic campaign are measurable from one run.
📊 AI training data and RAG indexing
videoTitle and videoDescription-style text (repostText, itemText) are this platform's high-information text fields — they carry the caption a creator actually wrote, not a generated summary. For RAG enrichment, index videoTitle/itemText alongside videoAuthor and videoCreatedAt so a retrieval query can surface a specific creator's specific post. For training data, engagementRate, likeViewRatio, and commentViewRatio are numeric fields with a consistent structure across all four scrape modes — useful as a label or feature without any cleanup, since every field returns as a typed primitive.
📱 Competitive and market intelligence
Run the same startUrls on a schedule and track engagementRate and videoViews per videoId over time to catch a competitor's viral post before it shows up in a manual check. Hashtag search on an industry tag surfaces itemViews/itemLikes across every creator posting under it in one run, which is a faster read on category momentum than checking individual profiles.
🔬 Research and academic use
Public-data-only scope: every field in this dataset comes from TikTok's own public profile pages, search results, and hashtag pages — nothing requires a login or a private API grant. That makes it usable for social-media research datasets built on engagementRate, posting-time patterns (topDay/topHour), and caption text, within the platform's own terms and applicable data protection law.
🎥 Product and SaaS development
Creator-analytics dashboards, influencer-discovery tools, and monitoring products can build directly on videoViews, videoLikes, engagementRate, and the postingHistogram summary rows — the same fields a human analyst would read off the profile, already typed and joined into one dataset per run.
🍚 Input Parameters
All eight parameters, read directly from .actor/actor.json. Nothing is required — provide startUrls, searchQuery, or both.
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
startUrls | No | array | 📋 Add TikTok profile URLs (e.g., https://www.tiktok.com/@username) or just usernames (e.g., username). Supports bulk input - add as many profiles as you want! 💪 Leave empty if you only want to do search scraping. | ["https://www.tiktok.com/@mrbeast", "charlidamelio"] |
profileScrapeSections | No | array | 🎯 Choose which sections to scrape from profiles: 📺 'videos' (all profile videos) and/or 🔄 'repost' (reposted content). Select one or both! (enum: videos, repost; default: ["videos"]) | ["videos", "repost"] |
searchQuery | No | string | 🔎 Enter a search query for hashtag or keyword search. Leave empty if you only want to scrape profiles. Examples: 'Ronaldo', '#travel', 'cooking tips' (default: "") | "#travel" |
searchType | No | string | 📌 Choose search type: 🔑 'keyword' (general TikTok search) or #️⃣ 'hashtag' (hashtag-specific search). Default: keyword (enum: keyword, hashtag) | "hashtag" |
maxResults | No | integer | 🎯 Maximum number of results to collect per profile/search query. Range: 1-1000. Higher values = more data but longer scraping time. ⏱️ (minimum: 1, maximum: 1000, default: 100) | 200 |
enableAnalytics | No | boolean | 📊 Turn on derived engagement analytics: engagementRate, likeViewRatio and commentViewRatio computed on every row across all 4 scrape modes (profile videos, keyword search, hashtag search, reposts), plus a best-posting-time summary row. See 'Analytics Scope' below to choose which of these two outputs you get. Default: on. (default: true) | true |
analyticsScope | No | string | 🔎 'per_item' = only add engagementRate/likeViewRatio/commentViewRatio to each scraped row. 🕒 'per_profile' = only add the best-posting-time summary row per profile/query. 🔀 'both' = get both outputs in the same run. Default: both. (enum: per_item, per_profile, both) | "both" |
proxyConfiguration | No | object | 🔒 Proxy settings for enhanced reliability. By default, no proxy is used (direct connection). The actor automatically retries with an alternate connection if a request doesn't come through. 🛡️ | {"useApifyProxy": true} |
Complete JSON input example:
{"startUrls": ["https://www.tiktok.com/@mrbeast", "charlidamelio"],"profileScrapeSections": ["videos", "repost"],"searchQuery": "#travel","searchType": "hashtag","maxResults": 200,"enableAnalytics": true,"analyticsScope": "both","proxyConfiguration": { "useApifyProxy": true }}
Supported URL types and input formats
startUrls accepts a mix of full profile URLs and bare usernames in the same list — each entry is normalized independently:
https://www.tiktok.com/@mrbeast→ parsed from the URL path@mrbeast→ the leading@is strippedmrbeast→ used as-is
searchQuery accepts a plain keyword ("cooking tips") or a hashtag with or without the leading # ("#travel" or "travel" both work when searchType is hashtag — the leading # is stripped before the search runs). Leave startUrls empty to run search-only, or leave searchQuery empty to run profile-only — both can also run in the same call.
📦 Output Format
TikTok Scraper With Engagement Data returns typed, normalized JSON — one row per video, repost, or search item, plus an optional posting-time summary row per profile or query. Field names stay constant per scrape mode: profile-video rows always carry videoId…videoShares; repost rows always carry repostId…repostLikes; search-result rows (both hashtag and keyword modes) always carry itemId…itemComments. Results land in the run's default Apify Dataset — exportable as JSON, CSV, Excel, XML, or an HTML table, or read live through the Apify API and apify_client.
Charging note: every successful row (success: true) is pushed with the row_result charged event — this covers profile-video, profile-repost, hashtag-search, keyword-search, and posting-time-summary rows. Rows pushed after a failure (success: false, carrying an error string and no entity payload) are pushed uncharged. Filter them out with success === true in JS or item["success"] is True in Python before counting billed rows.
Output for Profile Videos
type: "profile_video" — pushed once per video found on a scraped profile.
{"type": "profile_video","username": "mrbeast","video": {"id": "7321456789012345678","url": "https://www.tiktok.com/@mrbeast/video/7321456789012345678","title": "I Gave My 100,000,000th Subscriber An Island","description": "I Gave My 100,000,000th Subscriber An Island","create_time": 1707408000,"duration": 62,"width": 1080,"height": 1920,"cover_url": "https://p16-sign-va.tiktokcdn.com/obj/cover.jpeg","dynamic_cover_url": "https://p16-sign-va.tiktokcdn.com/obj/dynamic.jpeg","play_url": "https://v16-webapp.tiktok.com/obj/play.mp4","download_url": "https://v16-webapp.tiktok.com/obj/download.mp4","author": {"username": "mrbeast","nickname": "MrBeast","avatar": "https://p16-sign-va.tiktokcdn.com/obj/avatar.jpeg","verified": true},"stats": {"views": 182300000,"likes": 9800000,"comments": 45600,"shares": 312000},"music": {"title": "original sound","author": "MrBeast","duration": 62}},"videoId": "7321456789012345678","videoTitle": "I Gave My 100,000,000th Subscriber An Island","videoUrl": "https://www.tiktok.com/@mrbeast/video/7321456789012345678","videoCreatedAt": "2024-02-08T16:00:00Z","videoAuthor": "mrbeast","videoViews": 182300000,"videoLikes": 9800000,"videoComments": 45600,"videoShares": 312000,"success": true,"engagementRate": 0.055719,"likeViewRatio": 0.053757,"commentViewRatio": 0.00025,"scrapedAt": "2026-08-15T09:12:03Z"}
A profile that fails after retrying pushes an uncharged row instead: {"type": "profile_video", "username": "...", "success": false, "error": "Could not fetch profile videos after retrying.", "scrapedAt": "..."}.
Output for Reposts
type: "profile_repost" — pushed once per repost found on a scraped profile's Reposts tab. Note the raw repost object uses dotted string keys (e.g. "authorMeta.name"), matching the source payload exactly.
{"type": "profile_repost","username": "mrbeast","repost": {"authorMeta.avatar": "https://p16-sign-va.tiktokcdn.com/obj/avatar.jpeg","authorMeta.name": "khaby.lame","text": "This is wild","diggCount": 542000,"shareCount": 18300,"playCount": 12400000,"commentCount": 9800,"videoMeta.duration": 18,"musicMeta.musicName": "original sound","musicMeta.musicAuthor": "khaby.lame","musicMeta.musicOriginal": true,"createTimeISO": "2026-07-30T14:22:10Z","webVideoUrl": "https://www.tiktok.com/@khaby.lame/video/7398765432109876543","authorMeta.nickname": "Khaby Lame","authorMeta.verified": true,"authorMeta.signature": "","authorMeta.following": 45,"authorMeta.fans": 162000000,"authorMeta.heart": 2500000000,"authorMeta.video": 1200,"authorMeta.digg": 890,"stats.collectCount": 34200,"videoMeta.width": 1080,"videoMeta.height": 1920,"videoMeta.ratio": "1080p","videoMeta.cover": "https://p16-sign-va.tiktokcdn.com/obj/cover.jpeg","videoMeta.originCover": "https://p16-sign-va.tiktokcdn.com/obj/origin.jpeg","videoMeta.dynamicCover": "https://p16-sign-va.tiktokcdn.com/obj/dynamic.jpeg","videoMeta.playAddr": "https://v16-webapp.tiktok.com/obj/play.mp4","videoMeta.downloadAddr": "https://v16-webapp.tiktok.com/obj/download.mp4","videoMeta.shareCover": [],"musicMeta.musicId": "7398765432109876500","musicMeta.musicAlbum": "","musicMeta.musicCover": "https://p16-sign-va.tiktokcdn.com/obj/music.jpeg","musicMeta.musicPlayUrl": "https://sf16.tiktokcdn.com/obj/music.mp3","musicMeta.musicDuration": 18,"id": "7398765432109876543","secretID": "","isAd": false,"itemCommentStatus": 0,"privateItem": false,"duetEnabled": true,"stitchEnabled": true,"shareEnabled": true,"locationCreated": "","forFriend": false,"officalItem": false,"originalItem": false,"duetDisplay": 0,"stitchDisplay": 0,"stickersOnItem": [],"effectStickers": []},"repostId": "7398765432109876543","repostText": "This is wild","repostUrl": "https://www.tiktok.com/@khaby.lame/video/7398765432109876543","repostCreatedAt": "2026-07-30T14:22:10Z","repostAuthor": "khaby.lame","repostViews": 12400000,"repostLikes": 542000,"success": true,"engagementRate": 0.045976,"likeViewRatio": 0.043710,"commentViewRatio": 0.00079,"scrapedAt": "2026-08-15T09:14:20Z"}
Output for Search Results
Hashtag search (type: "hashtag_search") and keyword search (type: "keyword_search") both flatten to the same itemId/itemText/itemUrl/itemCreatedAt/itemViews/itemLikes/itemComments columns, but their raw item payloads come from two different parsers and carry different keys.
Keyword search (type: "keyword_search"):
{"type": "keyword_search","query": "cooking tips","item": {"url": "https://www.tiktok.com/@chefjohn/video/7401122334455667788?is_copy_url=1&is_from_webapp=v1","id": "7401122334455667788","desc": "3 cooking tips every beginner needs","createTime": "1723651200","scheduleTime": 0,"video": {"id": "7401122334455667788","height": 1920,"width": 1080,"duration": 34,"ratio": "1080p","cover": "https://p16-sign-va.tiktokcdn.com/obj/cover.jpeg","originCover": "https://p16-sign-va.tiktokcdn.com/obj/origin.jpeg","dynamicCover": "https://p16-sign-va.tiktokcdn.com/obj/dynamic.jpeg","playAddr": "https://v16-webapp.tiktok.com/obj/play.mp4","downloadAddr": "https://v16-webapp.tiktok.com/obj/download.mp4","shareCover": [],"reflowCover": "https://p16-sign-va.tiktokcdn.com/obj/cover.jpeg","bitrate": 0,"encodedType": "","format": "","videoQuality": "","encodeUserTag": "","codecType": "","definition": "","subtitleInfos": [],"zoomCover": {},"volumeInfo": {},"bitrateInfo": []},"author": "chefjohn","music": {"id": "7401122330000000000","title": "original sound","playUrl": "https://sf16.tiktokcdn.com/obj/music.mp3","coverLarge": "https://p16-sign-va.tiktokcdn.com/obj/music.jpeg","coverMedium": "https://p16-sign-va.tiktokcdn.com/obj/music.jpeg","coverThumb": "https://p16-sign-va.tiktokcdn.com/obj/music.jpeg","authorName": "chefjohn","original": true,"duration": 34,"album": "","scheduleSearchTime": 0},"challenges": [],"stats": { "diggCount": 88400, "shareCount": 4100, "commentCount": 1200, "playCount": 950000 },"isActivityItem": false,"duetInfo": { "duetFromId": "0" },"warnInfo": [],"originalItem": true,"officalItem": false,"textExtra": [],"secret": false,"forFriend": false,"digged": false,"itemCommentStatus": 0,"showNotPass": false,"vl1": false,"takeDown": 0,"itemMute": false,"effectStickers": [],"authorStats": { "followerCount": 240000, "followingCount": 120, "heart": 5200000, "heartCount": 5200000, "videoCount": 340, "diggCount": 900 },"privateItem": false,"duetEnabled": true,"stitchEnabled": true,"stickersOnItem": [],"isAd": false,"shareEnabled": true,"comments": [],"duetDisplay": 0,"stitchDisplay": 0,"indexEnabled": true,"diversificationLabels": [],"adAuthorization": false,"adLabelVersion": 0,"locationCreated": "","nickname": "Chef John","authorId": "6812345678901234567","authorSecId": "MS4wLjABAAAA...","avatarThumb": "https://p16-sign-va.tiktokcdn.com/obj/avatar.jpeg","downloadSetting": 0,"authorPrivate": false},"itemId": "7401122334455667788","itemText": "3 cooking tips every beginner needs","itemUrl": "https://www.tiktok.com/@chefjohn/video/7401122334455667788?is_copy_url=1&is_from_webapp=v1","itemCreatedAt": "2024-08-14T16:00:00Z","itemViews": 950000,"itemLikes": 88400,"itemComments": 1200,"success": true,"engagementRate": 0.098421,"likeViewRatio": 0.093053,"commentViewRatio": 0.001263,"scrapedAt": "2026-08-15T09:16:45Z"}
Hashtag search (type: "hashtag_search") — same flat itemId…itemComments columns, but its raw item uses a distinct shape (authorMeta, musicMeta, videoMeta, mediaUrls, mentions, hashtags instead of video/music/authorStats):
{"type": "hashtag_search","query": "travel","item": {"id": "7402233445566778899","text": "Hidden beach in Bali #travel","createTime": 1723737600,"createTimeISO": "2024-08-15T16:00:00Z","isAd": false,"isMuted": false,"authorMeta": {"id": "6899988877766655544","name": "wanderlust_amy","nickName": "Amy Wanders","verified": false,"signature": "traveling the world one beach at a time","bioLink": null,"avatar": "https://p16-sign-va.tiktokcdn.com/obj/avatar.jpeg","privateAccount": false,"ttSeller": false,"following": 210,"fans": 89000,"heart": 1450000,"video": 410,"digg": 3200},"musicMeta": {"musicName": "Tropical Vibes","musicAuthor": "DJ Sunset","musicOriginal": false,"musicAlbum": "","playUrl": "https://sf16.tiktokcdn.com/obj/music.mp3","coverMediumUrl": "https://p16-sign-va.tiktokcdn.com/obj/music.jpeg","musicId": "7402233440000000000"},"webVideoUrl": "https://www.tiktok.com/@wanderlust_amy/video/7402233445566778899","mediaUrls": ["https://v16-webapp.tiktok.com/obj/play.mp4", "https://v16-webapp.tiktok.com/obj/download.mp4"],"videoMeta": {"height": 1920,"width": 1080,"duration": 21,"coverUrl": "https://p16-sign-va.tiktokcdn.com/obj/cover.jpeg","originalCoverUrl": "https://p16-sign-va.tiktokcdn.com/obj/origin.jpeg","definition": "","format": "","originalDownloadAddr": "https://v16-webapp.tiktok.com/obj/download.mp4","downloadAddr": "https://v16-webapp.tiktok.com/obj/download.mp4"},"diggCount": 41200,"shareCount": 2900,"playCount": 610000,"collectCount": 8700,"commentCount": 640,"mentions": [],"hashtags": [{ "name": "travel" }]},"itemId": "7402233445566778899","itemText": "Hidden beach in Bali #travel","itemUrl": "https://www.tiktok.com/@wanderlust_amy/video/7402233445566778899","itemCreatedAt": "2024-08-15T16:00:00Z","itemViews": 610000,"itemLikes": 41200,"itemComments": 640,"success": true,"engagementRate": 0.073098,"likeViewRatio": 0.067541,"commentViewRatio": 0.001049,"scrapedAt": "2026-08-15T09:18:02Z"}
authorMeta.bioLink is always null in the hashtag-search shape — the underlying parser never populates it, regardless of the creator's actual bio link. comments in the keyword-search shape is always an empty array — TikTok's search item payload returns commentCount but not comment text.
Output for Posting-Time Summary Rows
type: "posting_pattern" — pushed once per profile (scope: "profile_summary") or per search query (scope: "hashtag_search_summary" / "keyword_search_summary"), only when enableAnalytics is on and analyticsScope is per_profile or both, and only if at least one timestamped row was collected in that batch.
{"type": "posting_pattern","scope": "profile_summary","username": "mrbeast","sampleSize": 84,"topDay": "Friday","topDayCount": 19,"topHour": 16,"topHourCount": 11,"postingHistogram": {"byWeekday": { "Friday": 19, "Saturday": 14, "Monday": 12, "Wednesday": 11, "Thursday": 10, "Tuesday": 9, "Sunday": 9 },"byHour": { "14": 6, "15": 9, "16": 11, "17": 8, "18": 5 }},"success": true}
For a search-based summary row, the username key above is replaced by query (holding the hashtag or keyword), and scope reads hashtag_search_summary or keyword_search_summary.
Schema stability and export options
Field names stay stable across runs and are not renamed when TikTok updates its front end — the parsers pull from TikTok's underlying API responses, not from rendered HTML, so a front-end redesign does not by itself change the output schema. Results are stored in the run's default Apify Dataset and can be exported as JSON, CSV, Excel, XML, or an HTML table from the Apify Console, or read programmatically through the Apify API and the apify_client / apify-client SDKs. There is no separate webhook-only or streaming delivery mode beyond what the Apify Dataset and API already provide.
💡 TikTok Scraper With Engagement Data Strategy Guide
🎯 Strategy 1: Real-time enrichment pipeline
Trigger a run whenever a new creator handle lands in your CRM or lead list: pass it as a single startUrls entry with analyticsScope: "per_item", then read back videoViews, videoLikes, and engagementRate for that creator's latest videos and write them onto the CRM record. Because analytics are computed locally with no extra request, a single run enriches the record without a second API call.
🎯 Strategy 2: Scheduled monitoring and alerting
Set up an Apify Schedule to re-run the same startUrls daily or weekly. Diff each run's videoId → engagementRate / videoViews pairs against the previous run's dataset and alert when a specific video's engagementRate jumps past your own threshold — that is a faster signal than watching the profile manually. The topDay/topHour posting-time summary from analyticsScope: "per_profile" also lets you confirm whether a spike lines up with the creator's usual posting window.
🎯 Strategy 3: Bulk dataset build
Feed a long list of profile handles or a list of search queries through startUrls/searchQuery across parallel runs, keeping each run's maxResults inside the schema's 1–1000 range, then aggregate the resulting datasets to CSV or a database for a research or training set. No concurrency limit is documented for this Actor beyond the per-run maxResults cap — plan run count against your own Apify plan's concurrency, not a number stated here.
Strategy comparison at a glance
| Strategy | Best for | Run pattern | Output format |
|---|---|---|---|
| Real-time enrichment | Appending live engagement fields to a CRM or lead record | Single on-demand run per trigger | JSON row read via API |
| Scheduled monitoring | Tracking engagementRate/videoViews change over time | Recurring run via Apify Schedule | Dataset diffed run-over-run |
| Bulk dataset build | Building a research or training dataset across many profiles/queries | Parallel runs, one per profile/query batch | CSV/JSON export or database load |
🌴 Related TikTok Scrapers & Tools
| Scraper Name | What it extracts |
|---|---|
| TikTok Scraper With Engagement Data (this Actor) | Profile videos, reposts, hashtag and keyword search results, with engagement ratios |
| Snapchat Spotlight Scraper: Recommended & Trending Videos | Cross-platform short-video alternative — trending Spotlight videos |
| Snapchat User Spotlight Scraper With Comment Threads | Cross-platform creator video data with comment threads |
| Instagram Post Details Scraper With Comments & Replies | Cross-platform post-level engagement data |
| YouTube Video Subtitles Scraper: Caption Coverage Insights Data | Complementary — video caption/subtitle text for content analysis |
| Reddit Scraper With Subreddit Data | Complementary — community discussion data for cross-platform sentiment |
How to integrate TikTok Scraper With Engagement Data with your stack
TikTok Scraper With Engagement Data works with any language or tool that can make an HTTP request, through the Apify API or the official apify-client SDKs.
Python
from apify_client import ApifyClientimport csvclient = ApifyClient("<APIFY_API_TOKEN>")run_input = {"startUrls": ["mrbeast", "khaby.lame", "charlidamelio"],"profileScrapeSections": ["videos"],"maxResults": 100,"enableAnalytics": True,"analyticsScope": "per_item",}run = client.actor("<your-username>/tiktok-scraper-with-engagement-data").call(run_input=run_input)rows = [item for item in client.dataset(run["defaultDatasetId"]).iterate_items() if item.get("success")]fieldnames = ["videoId", "videoAuthor", "videoViews", "videoLikes", "engagementRate"]with open("tiktok_videos.csv", "w", newline="", encoding="utf-8") as f:writer = csv.DictWriter(f, fieldnames=fieldnames)writer.writeheader()for row in rows:writer.writerow({k: row.get(k) for k in fieldnames})print(f"Saved {len(rows)} rows to tiktok_videos.csv")
Node.js
import { ApifyClient } from 'apify-client';import { writeFileSync } from 'fs';const client = new ApifyClient({ token: '<APIFY_API_TOKEN>' });const input = {startUrls: ['mrbeast', 'khaby.lame'],profileScrapeSections: ['videos'],maxResults: 100,enableAnalytics: true,analyticsScope: 'per_item',};const run = await client.actor('<your-username>/tiktok-scraper-with-engagement-data').call(input);const { items } = await client.dataset(run.defaultDatasetId).listItems();const rows = items.filter((item) => item.success);const csv = ['videoId,videoAuthor,videoViews,videoLikes,engagementRate'].concat(rows.map((r) => `${r.videoId},${r.videoAuthor},${r.videoViews},${r.videoLikes},${r.engagementRate}`)).join('\n');writeFileSync('tiktok_videos.csv', csv);console.log(`Saved ${rows.length} rows to tiktok_videos.csv`);
Async and scheduled pipelines
For fire-and-forget large jobs, call client.actor(...).start() instead of .call() and poll the run status, or check back later through the Apify Console or API — the run keeps executing after your script disconnects. For recurring jobs, use an Apify Schedule to trigger this Actor with fixed input on a cron-style interval; each scheduled run writes to its own dataset for you to diff or aggregate afterward.
🎯 Who Needs TikTok Scraper With Engagement Data? (Use Cases & Industries)
🏢 Marketing and social media teams
A brand or agency tracking a campaign hashtag runs hashtag search with analyticsScope: "both" and pulls itemViews/itemLikes/engagementRate per post plus the topDay/topHour summary, turning "how did our hashtag do this week" into one dataset instead of a manual scroll.
📊 Data and AI teams
Teams building an internal analytics or RAG index over creator content pull videoTitle, itemText, and engagementRate for a defined set of profiles, keeping the numeric fields as structured features and the text fields as retrievable context, all typed on arrival.
📱 Competitive intelligence analysts
Analysts feed a competitor's profile into startUrls on a recurring schedule and watch videoViews and engagementRate per videoId for early signs of a breakout post, without manually revisiting the profile.
🔬 Researchers
Academic and market researchers use hashtag or keyword search to build datasets on public discourse around a topic, drawing only on publicly accessible profile, video, and search data.
🎥 Creator-analytics product builders
SaaS tools that rank or recommend creators can build directly on videoViews, videoLikes, engagementRate, and the posting-time histogram, rather than re-implementing TikTok stats collection from scratch.
Is it legal to scrape TikTok?
Scraping publicly accessible TikTok pages is generally lawful in the United States: in hiQ Labs, Inc. v. LinkedIn Corp., 938 F.3d 985 (9th Cir. 2019), the Ninth Circuit held that scraping data a website makes publicly accessible does not violate the Computer Fraud and Abuse Act. That precedent addresses the CFAA specifically — it does not clear every legal question a given use case might raise.
Three separate questions apply here. Scraping public data is generally permitted under the precedent above. Violating TikTok's Terms of Service by scraping is a matter of civil contract risk between the scraper and TikTok, not a criminal one — check TikTok's current terms before running this at scale. Data protection obligations apply because this Actor returns identifiable creator information — usernames, nicknames, avatar URLs, and follower counts — which is personal data under GDPR and CCPA. TikTok Scraper With Engagement Data returns only publicly accessible data. What you do with that data is your responsibility — consult legal counsel for commercial applications involving personal data.
❓ Frequently asked questions
Does TikTok Scraper With Engagement Data work without a TikTok account?
Yes. Every scrape mode reads TikTok's public profile pages, search pages, and their underlying web API responses — no TikTok login, session cookie, or API key is required to start a run.
How does it handle TikTok's anti-scraping measures?
It escalates the outbound connection automatically: it starts on a direct connection, and if a request comes back blocked, rate-limited, timed out, or as a small interstitial page instead of real content, it switches to an alternate connection and retries, escalating further if that also fails. The video scraper also checks response size and looks for TikTok's own embedded-state markers, since a block can arrive as an HTTP 200 with a too-small body. Requests are retried with exponential backoff before this escalation kicks in.
Can I run it at scale without getting blocked?
⚠️ There is a real, schema-enforced constraint: maxResults is capped between 1 and 1000, applied per profile or per search query, not as a total across a whole run. No uptime or success-rate figure is published for this Actor — the connection-escalation behavior above is what's implemented, not a guaranteed outcome.
How fresh is the data it returns?
Live. Every run fetches directly from TikTok's pages and API at request time — there is no caching layer in this Actor, so each run reflects TikTok's state at the moment it ran.
Which TikTok fields work best for AI training and RAG indexing?
For RAG: videoTitle/itemText (the creator's actual caption) paired with videoAuthor/videoCreatedAt for retrieval context. For training data: engagementRate, likeViewRatio, and commentViewRatio, since they carry a consistent numeric structure across all four scrape modes. All fields return as typed JSON primitives, so no normalization step is needed before use.
Can I scrape reposts and search results in the same run as profile videos?
Yes. If both startUrls and searchQuery are set, the Actor runs profile scraping (videos and/or reposts, per profileScrapeSections) first, then runs the configured search mode — both write to the same dataset in one run.
Are error rows charged?
No. Rows pushed after a scrape failure carry success: false and an error string, and are pushed without the row_result charged event. Filter a dataset export on success === true (or item["success"] is True) to see only billed rows.
Does TikTok Scraper With Engagement Data work with Claude, ChatGPT, and other AI agent tools?
It's callable as an HTTP endpoint by any agent framework through the Apify API — start a run, poll or wait for completion, then read the dataset via apify_client or a raw HTTP call. Every response is typed JSON, so no HTML parsing step is needed before passing results into an LLM context window.
How does it compare to other TikTok scrapers?
Most single-purpose TikTok scrapers cover one content type at a time — only profile videos, or only search. This Actor covers profile videos, reposts, hashtag search, and keyword search from one input schema, with engagementRate/likeViewRatio/commentViewRatio computed locally on every row. It does not return comment threads or downloadable video files — a scraper built specifically for those will fit that use case better.
ℹ️ Disclaimer
TikTok Scraper With Engagement Data extracts only publicly available data from TikTok. This tool is intended for lawful use cases only. Users are responsible for complying with TikTok's terms of service and applicable data protection laws in their jurisdiction.