Tiktok Video Scraper
Pricing
$19.99/month + usage
Tiktok Video Scraper
Scrape TikTok videos effortlessly with the Apify TikTok Video Scraper. Extract video URLs, captions, hashtags, views, likes, comments, and creator info. Perfect for trend analysis, influencer tracking, and content research. Fast, accurate, and easy to integrate into automated workflows.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
API Empire
Maintained by CommunityActor stats
0
Bookmarked
19
Total users
2
Monthly active users
5 days ago
Last modified
Categories
Share
TikTok Video Scraper — Extract Videos, Creators and Music as JSON
TikTok Video Scraper turns public TikTok video URLs into structured records: the video itself, the creator behind it, the sound it uses, plus hashtags, @-mentions and direct media URLs. Every run returns typed, normalized JSON with a fixed 26-key row shape — no HTML, no selectors, no parsing step. It reads TikTok's own embedded page payload rather than rendered markup, and it needs no TikTok account, cookie or login. By the end of this page you will know exactly which keys land in your dataset and where the real ceilings sit.
⚠️ resultsPerPage accepts 1,000,000 — TikTok returns roughly a dozen
This is the one number worth reading before you configure a run, and it is stated up front rather than buried in the input table.
The input schema lets resultsPerPage go as high as 1000000, and the source clamps your value to that same range with max(1, min(1000000, ...)). That is the Actor's limit. It is not TikTok's. The schema's own description for the field says: "TikTok typically limits related videos to 12-16 per video." The source repeats it as a code comment and logs (requested: N, TikTok limit reached) when it collects fewer than you asked for.
So a large resultsPerPage does not buy you more related videos. The related-video collector scrolls the page until five consecutive scrolls produce nothing new, then stops — that break condition is what ends the loop in practice, not your number.
Two more things this field does not do:
- It is inert unless
scrapeRelatedVideosistrue. In the source,results_per_pageis only passed to the related-videos collector insideif scrape_related:. With the flag left at itsfalsedefault, the value is read and never used. - It does not affect the videos you submitted.
postURLsalways returns one row per URL, regardless of this setting.
What is TikTok Video Scraper?
TikTok Video Scraper is an Apify Actor that takes a list of public TikTok video URLs and returns one flat JSON row per video. Each row carries the video's engagement counters, the author's profile metadata as TikTok attaches it to the post, the sound's metadata, every hashtag and @-mention parsed out of the caption, and the direct media addresses present in the payload. Optionally it expands each input URL into that video's related feed, pushing those as additional rows.
No TikTok account, login, cookie or session is used anywhere in the source. Requests go out anonymously with a fixed desktop Chrome user-agent. The only credential involved is your Apify token.
- Scrape videos by direct URL — captions, timestamps, plays, likes, comments, shares, saves, reposts
- Scrape creator metadata carried on the post — handle, nickname, verified flag, bio, bio link, follower and video counts
- Scrape music and sound data — sound title, sound author, original-vs-borrowed flag, cover art, music ID
- Scrape hashtags and @-mentions parsed from the caption, including per-mention
secUidand caption offsets - Optionally scrape related videos for each input URL, as separate rows in the same shape
- Export as JSON, JSONL, CSV, Excel, HTML or XML — no proxy account to create, no parsing to write
What data does TikTok Video Scraper collect?
Six entity groups come back inside every video row, and related videos arrive as more rows of that same shape.
| Data Type | Key Fields | JSON Field Names |
|---|---|---|
| Videos | Caption, publish time, location, engagement counters, ad flags | id, text, createTime, createTimeISO, locationCreated, playCount, diggCount, commentCount, shareCount, collectCount, repostCount, isAd, isSponsored, isPinned, isSlideshow |
| Creators | Handle, display name, verified status, bio, bio link, profile totals | authorMeta.name, authorMeta.nickName, authorMeta.verified, authorMeta.signature, authorMeta.bioLink, authorMeta.fans, authorMeta.following, authorMeta.heart, authorMeta.video, authorMeta.privateAccount, authorMeta.ttSeller |
| Music / sounds | Sound title, sound author, original flag, audio URL, cover art | musicMeta.musicName, musicMeta.musicAuthor, musicMeta.musicOriginal, musicMeta.playUrl, musicMeta.musicId, musicMeta.coverMediumUrl |
| Hashtags | Tag name, tag ID, tag description, tag cover | hashtags[].name, hashtags[].id, hashtags[].title, hashtags[].cover |
| @-mentions | Mentioned handles, plus per-mention identity and caption offsets | mentions, detailedMentions[].userUniqueId, detailedMentions[].userId, detailedMentions[].secUid, detailedMentions[].start, detailedMentions[].end |
| Video files and specs | Resolution, duration, covers, subtitle tracks, direct media addresses | videoMeta.height, videoMeta.width, videoMeta.duration, videoMeta.coverUrl, videoMeta.originalCoverUrl, videoMeta.definition, videoMeta.format, videoMeta.subtitleLinks, mediaUrls |
| Related videos | Same 26-key row shape, pushed as separate dataset rows | id, webVideoUrl, submittedVideoUrl, plus every key above |
Two naming details worth knowing before you map fields: authorMeta.name holds the handle (uniqueId) while authorMeta.nickName holds the display name, and diggCount is TikTok's internal name for likes. Both are returned as TikTok names them rather than renamed to something friendlier, so your mapping matches the upstream payload.
The dataset's default table view surfaces 17 of these fields as columns. The JSON export always contains all of them.
Need more TikTok data?
This Actor is URL-driven — it answers "tell me everything about these specific videos." If you need commenter data instead, TikTok Comments Scraper With Commenter Profile Contacts goes one level down into each video's comment thread. For creator-level work, TikTok Profile Scraper With Bio Link Contact Leads starts from the profile rather than the post. And for discovery rather than lookup, TikTok Trending Hashtags Scraper — Find New Rising Tags and TikTok Trending Hashtags Analytics Scraper surface tags you do not have URLs for yet.
How does TikTok Video Scraper differ from the official TikTok API?
TikTok's own developer surfaces are permissioned: the Display API returns content belonging to users who have authorized your app through OAuth, and the Research API is available only through an application TikTok reviews and approves. TikTok Video Scraper reads the public video page anonymously and returns whatever any logged-out visitor can already see.
| Feature | TikTok Display API / Research API | TikTok Video Scraper |
|---|---|---|
| Approval required before first call | ✅ App registration and TikTok's app review; Research API access is granted by application | ❌ None — an Apify account and a video URL |
| User authorization (OAuth) | ✅ Display API returns content only for users who authorize your app | ❌ No login, no cookie, no OAuth |
| Scope restrictions | ✅ Fields and endpoints are gated per approved scope | ❌ Returns the full public payload the page carries |
| Arbitrary third-party video by URL | Governed by the scopes TikTok grants you | ✅ Any publicly viewable video URL |
| Related-videos feed | Consult TikTok's current developer documentation for the endpoints in scope | ✅ Optional, via scrapeRelatedVideos |
| Output shape | TikTok's documented API schema, varying by scope | ✅ One fixed 26-key row per video, every run |
| Rate limits | Published in TikTok's developer documentation — check it before you build | Nothing is configured in the Actor; see the blocking section below |
Figures for TikTok's own APIs change and are not reproduced here — read them from TikTok's current developer documentation before you choose.
Use the official APIs when you need first-party content, contractual terms, or data about accounts that have authorized your app. Use TikTok Video Scraper when you have public video URLs and no route through an approval queue.
Why do developers and teams scrape TikTok?
Short-video data serves four groups particularly well, and each leans on a different part of the row.
For AI engineers and agent builders
text gives you the caption as written, hashtags[].name and mentions give you clean topical and entity tags, and videoMeta.subtitleLinks lists the subtitle tracks TikTok published for the clip. Indexing caption plus tags per id into a vector store gives an agent something to retrieve against; keeping playCount, diggCount and commentCount alongside lets it rank retrieved content by reach rather than recency alone. Because the row is already typed JSON with fixed keys, it goes straight into a tool response with no parsing step in the agent loop.
For marketers and brand teams
Feed a list of competitor or campaign video URLs in weekly and diff the counters per id. playCount, diggCount, shareCount, collectCount and repostCount move at different rates, and saves-to-plays is a much better creative signal than likes alone. musicMeta.musicName and musicMeta.musicOriginal tell you whether a post rides a trending sound or an original one. Turn scrapeRelatedVideos on for a seed video and you get TikTok's own adjacency judgement — which creators the algorithm files next to your campaign.
For researchers and analysts
Everything returned is publicly visible on the video page to any logged-out visitor: caption, counters, the author metadata TikTok attaches to the post, sound credits. createTime (Unix epoch) plus locationCreated support time-series and regional slices, isAd and isSponsored let you separate paid from organic before analysis, and textLanguage supports multilingual corpora. Because runs are URL-driven, a sample frame defined elsewhere — a coding sheet, a prior study, a court filing — can be reproduced exactly rather than re-sampled from a search.
For developers building data products
The Actor is a normal Apify Actor, so it is one HTTP call or one apify_client line, and it can be put on an Apify schedule with an Apify webhook firing on completion. Row shape is fixed: 26 top-level keys, always present, never conditionally omitted. That means your database schema is stable and a missing upstream value arrives as "", 0, false, [] or null instead of a KeyError. Related videos land in the same table as the videos you submitted, so one query covers both.
How to scrape TikTok videos (step by step)
TikTok Video Scraper runs on Apify. Start it from the Apify Console or call it through the Apify API.
- Open TikTok Video Scraper on Apify and click Try for free
- Paste one or more TikTok video URLs into Direct URL(s) for scraping specific video(s) (
postURLs) — this is the only required input - Leave Scrape related videos (
scrapeRelatedVideos) off for a straight lookup, or tick it to also pull each video's related feed - If you ticked it, set Number of related videos per post (
resultsPerPage) — read the warning section above first - Click Start, then export the dataset as JSON, JSONL, CSV, Excel, HTML or XML
Only entries that start with http are processed; anything else is dropped before the run begins. If nothing valid survives that filter, the run logs No valid URLs provided and exits without pushing any rows.
What to do when TikTok changes its structure
The Actor reads TikTok's embedded rehydration payload (__UNIVERSAL_DATA_FOR_REHYDRATION__, falling back to __SIGI_STATE__) rather than CSS selectors, so a visual redesign generally does not touch it. It is maintained, and the output key names and types stay stable on your end when the upstream shape moves — your integrations keep working against the same schema.
⬇️ Input
Three parameters, exactly as published in the Actor's input schema. postURLs is the only required one.
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
postURLs | Yes | array | Add the URLs of posted videos you want to scrape. You can enter URLs one by one, or you can upload or link to a text file. Editor: string list. | ["https://www.tiktok.com/@apifyoffice/video/7200360993149553925"] |
scrapeRelatedVideos | No | boolean | Tick to scrape related videos for the post URLs you provide. The maximum number of scraped related videos is set by the resultsPerPage count. Default false. | true |
resultsPerPage | No | integer | Add the number of related videos you want to scrape for each post URL. This field is applicable when the Scrape related videos option is enabled. Note: TikTok typically limits related videos to 12-16 per video. Minimum 1, maximum 1000000, default 10. | 10 |
The schema default for resultsPerPage is 10. If the key is absent from the input object altogether, the source falls back to 1. Values below the minimum are raised to 1 and values above the maximum are lowered to 1000000 before use.
The schema exposes no proxy parameter, no concurrency parameter and no download toggles — those are not part of this Actor's input surface. The source also reads a keywords array that is not published in the input schema; it is not exposed in the Console form and should be treated as unsupported.
Example input
{"postURLs": ["https://www.tiktok.com/@apifyoffice/video/7200360993149553925","https://www.tiktok.com/@apifytech/video/7398101551744552225"],"scrapeRelatedVideos": true,"resultsPerPage": 10}
Common pitfall: raising resultsPerPage while leaving scrapeRelatedVideos at its false default. The value is read, then never used, and you get exactly one row per submitted URL. The flag is what turns the feature on.
⬆️ Output
Every video becomes one dataset row with the same 26 top-level keys — 60 keys including the nested authorMeta, musicMeta and videoMeta objects. Keys are never dropped when a value is missing: absent values arrive as "", 0, false or [], so downstream code never needs a presence check. Two keys are nullable by design — authorMeta.bioLink is null when the creator has no bio link, and authorMeta.followDatasetUrl is always null. Export as JSON, JSONL, CSV, Excel, HTML or XML, or read the dataset through the Apify API.
A few values are derived rather than copied, and it is worth knowing which: createTimeISO is built from the epoch createTime and suffixed with .000Z, so use createTime if you need timezone-exact arithmetic. textLanguage falls back to the literal "en" when TikTok sends nothing, musicMeta.musicName falls back to "original sound", musicMeta.musicOriginal falls back to true, and videoMeta.format falls back to "mp4". locationCreated is taken from the first of location.countryCode, location.region, location.country, then the post-level region, locationCreated, ipLocation or countryCode that carries a value, and is "" if none do.
Scraped video
{"id": "7200360993149553925","text": "Wanna work at Apify? 👀 @apify #hiring #techtok","textLanguage": "en","createTime": 1677690936,"createTimeISO": "2023-03-01T16:35:36.000Z","locationCreated": "CZ","isAd": false,"authorMeta": {"id": "6976999329680966","name": "apifyoffice","profileUrl": "https://www.tiktok.com/@apifyoffice","nickName": "Apify","verified": false,"signature": "Your full-stack web scraping platform.","bioLink": "apify.com","originalAvatarUrl": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/large.jpeg","avatar": "https://p16-sign-va.tiktokcdn.com/tos-maliva-avt-0068/medium.jpeg","privateAccount": false,"roomId": "","ttSeller": false,"followDatasetUrl": null,"following": 12,"friends": 8,"fans": 1450,"heart": 9800,"video": 63,"digg": 210},"musicMeta": {"musicName": "original sound","musicAuthor": "Apify","musicOriginal": true,"playUrl": "https://sf16-ies-music-va.tiktokcdn.com/obj/tos-useast2a-ve-2774/audio.mp3","coverMediumUrl": "https://p16-sign.tiktokcdn.com/music-cover-medium.jpeg","originalCoverMediumUrl": "https://p16-sign.tiktokcdn.com/music-cover-medium.jpeg","musicId": "7200360991234567890"},"webVideoUrl": "https://www.tiktok.com/@apifyoffice/video/7200360993149553925","mediaUrls": ["https://v16-webapp.tiktok.com/video/tos/useast2a/playaddr.mp4"],"videoMeta": {"height": 1024,"width": 576,"duration": 22,"coverUrl": "https://p16-sign.tiktokcdn.com/cover.jpeg","originalCoverUrl": "https://p16-sign.tiktokcdn.com/origin-cover.jpeg","definition": "540p","format": "mp4","subtitleLinks": []},"diggCount": 5400,"shareCount": 120,"playCount": 89000,"collectCount": 340,"commentCount": 76,"repostCount": 4,"mentions": ["@apify"],"detailedMentions": [{"userUniqueId": "apify","userId": "6784390118902","secUid": "MS4wLjABAAAAexampleSecUidValue","awemeId": "","start": 21,"end": 27}],"hashtags": [{ "id": "12885", "name": "hiring", "title": "", "cover": "" },{ "name": "techtok" }],"effectStickers": [],"isSlideshow": false,"isPinned": false,"isSponsored": false,"submittedVideoUrl": "https://www.tiktok.com/@apifyoffice/video/7200360993149553925"}
Note the hashtags array: entries sourced from TikTok's challenges block carry all four keys (id, name, title, cover), while entries recovered from the caption's textExtra block carry name only. Read name and treat the rest as optional. Names are lowercased and de-duplicated across both sources.
Related video rows
Related videos are not nested inside the parent row. Each one is pushed as its own dataset row, with the identical 26-key shape shown above. That keeps your table uniform — one row is one video, always.
The trade-off is that no field links a related row back to the video that produced it. On a related row, both webVideoUrl and submittedVideoUrl hold that related video's own reconstructed URL:
{"id": "7331852901234567890","text": "3 scraping mistakes that cost you data #webscraping","webVideoUrl": "https://www.tiktok.com/@datadev/video/7331852901234567890","submittedVideoUrl": "https://www.tiktok.com/@datadev/video/7331852901234567890","authorMeta": { "name": "datadev", "nickName": "Data Dev", "fans": 24100 },"playCount": 412000,"diggCount": 31200}
To separate the two kinds of row, match against your own input list — the rows you asked for carry submittedVideoUrl values identical to the strings you submitted:
submitted = set(run_input["postURLs"])parents = [r for r in items if r.get("submittedVideoUrl") in submitted]related = [r for r in items if r.get("submittedVideoUrl") not in submitted]
Error rows
When a video URL cannot be fetched — the request fails after its retries, or TikTok returns an error status — the Actor pushes a two-key row instead of skipping silently:
{"error": "500, message='Internal Server Error'","url": "https://www.tiktok.com/@someuser/video/7200360993149553925"}
These rows go through the same Actor.push_data(..., charged_event_name="item_result") call as successful videos, so an error row is a charged row. Stating that plainly is fairer than letting you find it on an invoice. Filter them out before analysis:
clean = [r for r in items if "error" not in r]
$jq 'map(select(has("error") | not))' dataset.json
There is one failure mode that produces neither a video row nor an error row — see the FAQ below.
How does TikTok Video Scraper compare to other TikTok scrapers?
| Feature | TikTok Video Scraper | Generic alternative |
|---|---|---|
| Output format | ✅ Fixed 26-key typed JSON row, same keys every run | Often a flattened dotted-key sample or a raw upstream passthrough |
| Entity coverage in one row | ✅ Video + creator + music + hashtags + mentions + media URLs | Frequently video and creator only, with sound or mentions dropped |
| Related-videos expansion | ✅ Optional, as separate rows in the same shape | Usually not offered from a single video URL |
| Failure visibility | ✅ Error rows written to the dataset with the failing url | Silently missing rows you have to reconcile yourself |
| Documented ceilings | ✅ The resultsPerPage gap and the TikTok related cap are stated on this page | Maximum input values published without the real upstream limit |
| Proxy handling | ❌ No proxy input and none configured — see the blocking section | Varies; some configure Apify Proxy, some state none is needed |
That last row is a deliberate ❌. This Actor does not manage proxies, and pretending otherwise would be the easiest claim on this page to make and the least defensible.
If you are building an AI agent or a RAG pipeline, the output-format row is the decision-maker — parsing HTML inside an agent loop is a reliability failure mode, not a feature. The failure-visibility row is the second one to weigh, because an agent that cannot tell "no data" from "fetch failed" will confidently report the wrong thing.
How many results can you scrape with TikTok Video Scraper?
There is no cap on postURLs in the Actor — one row comes back per URL you submit. The list has no maximum length in the schema and the source builds one task per valid URL, so the practical ceiling is your Actor run's memory and timeout, not a configured number.
Related videos are where the real limit sits. resultsPerPage accepts up to 1000000, but the Actor's own schema description states TikTok typically limits related videos to 12–16 per video, and the source logs a TikTok limit reached line when it returns fewer than requested. The collector scrolls the video page up to max(20, resultsPerPage) times, but exits early after five consecutive scrolls yield no new videos — in practice that early exit, not your number, is what ends collection.
There is no pagination in the classic sense. The Actor does not walk numbered result pages; it fetches each submitted URL once, then optionally scroll-harvests that video's related feed in a headless browser. Total row count for a run is therefore len(postURLs) plus, when the flag is on, whatever TikTok surfaces for each of them.
No benchmark timings are published here, because none have been measured.
How does TikTok Video Scraper handle blocking and retries?
Straight answer, because this one is usually dressed up: the input schema exposes no proxy parameter, and the source configures none. Both the HTTP client (aiohttp) and the headless browser (Playwright Chromium) run without a proxy, so requests leave over the Apify platform's default network egress. There is no residential rotation and no session management to configure — and equally, no proxy account for you to create or fund.
What the Actor does implement:
- Retries. Each video page fetch is wrapped in a retry helper with up to 3 attempts and a linear backoff between them. The related-videos browser path retries its navigation up to 3 times as well.
- A browser-accurate header set. A fixed desktop Chrome user-agent plus
acceptandaccept-languageheaders on every HTTP request; the Playwright context uses the same user-agent, a 1920×1080 viewport and anen-USlocale. - A light stealth patch. The browser context launches with
--disable-blink-features=AutomationControlledand an init script that clearsnavigator.webdriver. - A DOM fallback. If TikTok's related-feed API responses are not captured during scrolling, the Actor re-reads the page's rehydration state and pulls items from there instead.
- Per-URL isolation. One URL failing does not stop the run — it produces an error row and the remaining URLs continue.
One thing to plan around: all submitted URLs are fetched concurrently, with no throttle and no delay between them. With no proxy rotation underneath, a very large postURLs list in a single run raises your block risk more than the same list split across several smaller runs or an Apify schedule.
Integrate TikTok Video Scraper and automate your workflow
TikTok Video Scraper works with any language or tool that can send an HTTP request, because it is a standard Apify Actor with a standard Apify run endpoint.
REST API integration
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("<YOUR_USERNAME>/tiktok-video-scraper").call(run_input={"postURLs": ["https://www.tiktok.com/@apifyoffice/video/7200360993149553925"],"scrapeRelatedVideos": True,"resultsPerPage": 10,})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if "error" in row:print("failed:", row["url"])continueprint(row["authorMeta"]["name"], row["playCount"], row["diggCount"], row["webVideoUrl"])
Works in Python, Node.js, Go, Ruby, cURL. Branch on the presence of error before touching any video key — that is the only shape check your integration needs.
Automation platforms (n8n, Make, LangChain)
In n8n, use the Apify node — or an HTTP Request node pointed at the Actor's run endpoint with your token — and pass the same JSON input shown above; a Filter node on the error key splits failures from videos before anything downstream sees them. In Make, the Apify module supports run-and-wait, so a weekly engagement sweep can map playCount, diggCount and collectCount straight into a Google Sheets or Airtable step. In LangChain (or CrewAI, or a hand-written tool definition), wrap the apify_client call above as a tool function and return the row list directly — it is already typed JSON, so no output parser is required. Apify schedules and Apify webhooks handle recurring runs and completion callbacks without any code on your side.
Is it legal to scrape TikTok?
Scraping publicly visible content is broadly treated as permissible where no authentication is bypassed, and TikTok Video Scraper reads only what a logged-out visitor already sees on the video page — no account, no cookie, no session.
But be clear about what comes back: TikTok videos carry creator handles, author profile metadata and user-generated captions, and this Actor returns all of it. That is personal data. GDPR in the EU, the CCPA in California and equivalent regimes elsewhere apply to how you store, process and retain it. You need a lawful basis before you keep it, retention limits, and a plan for subject-access and deletion requests — the fact that the data was public does not remove those obligations.
Consult legal counsel for commercial use cases involving bulk personal data.
❓ Frequently asked questions
Does TikTok Video Scraper work without a TikTok account?
Yes. No account, no login, no cookie and no session appear anywhere in the source. Requests are sent anonymously with a fixed desktop Chrome user-agent, and the Actor reads only the public video page payload. The one credential you need is your Apify token.
How often is the scraped data updated?
Every run fetches live. Nothing is cached between runs, so counters reflect the moment of the request — which also means two runs minutes apart will legitimately differ. For time series, put the Actor on an Apify schedule and diff playCount, diggCount, shareCount, collectCount and commentCount per id across runs.
What happens if a video is deleted, private or region-locked?
Two different outcomes, and knowing which you got matters.
If the request itself fails — a 404 for a removed video, a 403, a timeout — the Actor retries up to 3 times and then writes an error row: {"error": "...", "url": "..."}. You can see exactly which URL failed.
If TikTok returns a page successfully but without the video payload — the common shape for a deleted, private or region-locked post — no itemStruct is found, the function returns nothing, and no row is written at all, not even an error row. That URL is simply absent from the dataset. So reconcile against your input list rather than assuming a run that finished cleanly covered every URL:
returned = {r.get("submittedVideoUrl") for r in items}missing = [u for u in run_input["postURLs"] if u not in returned]
Can I scrape private, deleted or age-restricted TikTok content?
No. Only publicly accessible videos are returned. Content behind a private account, removed by TikTok or the creator, gated by age verification, or blocked in the region the run executes from will not produce data — see the previous answer for exactly how each absence shows up.
How is a run charged?
This Actor uses pay-per-event with a single charged event, item_result, fired once per row pushed to the dataset. That means related videos are charged like any other row, and error rows are charged too, since they go through the identical push call. Current per-event pricing is shown on the Actor's Apify Store listing. If you want a strict one-charge-per-URL run, leave scrapeRelatedVideos at false.
Does TikTok Video Scraper work for AI agent workflows and LLM pipelines?
Yes. It is callable as a standard HTTP endpoint through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step. There is no MCP server for this Actor — use the Apify API path shown above. Have your tool check for the error key before reading video fields, so the agent can distinguish a fetch failure from a genuinely empty result.
How does TikTok Video Scraper handle TikTok's anti-bot system?
By reading TikTok's embedded rehydration payload instead of rendered markup, retrying failed fetches up to 3 times with backoff, sending a browser-accurate header set, and — for the related-videos path only — driving a headless Chromium with navigator.webdriver cleared and automation flags disabled. What it does not do is rotate proxies: there is no proxy input and none is configured in the source, so requests use the Apify platform's default egress. Splitting very large URL lists across several runs is the practical mitigation.
How does TikTok Video Scraper compare to other TikTok scrapers?
Checked on the Apify Store on 25 July 2026: clockworks/tiktok-video-scraper is the most established TikTok video Actor and documents postURLs plus four media-download toggles (shouldDownloadCovers, shouldDownloadSlideshowImages, shouldDownloadSubtitles, shouldDownloadVideos), and it publishes a detailed error-item reference keyed on an errorCode field; a related-videos option is not documented in its README. novi/fast-tiktok-api takes a different shape entirely — one type field switching between SEARCH, TREND, HASHTAG, USER, MUSIC, VIDEO and COMMENT modes, and it advertises watermark-free video URLs; its own Limitations section states that pagination support is not available and that consideration for TikTok's rate limiting and throttling is not provided within the scraper. invideoiq/video-transcript-scraper is a multi-platform transcript tool that lists TikTok as supported for public videos with available captions; it returns transcripts and video metadata rather than engagement counters, creator stats or hashtag data.
Against those, this Actor's differences are the related-videos expansion from a single video URL, mentions returned both as plain handles and with per-mention secUid and caption offsets, and the ceilings on this page being documented rather than implied by the schema maximum.
Does TikTok Video Scraper return data in a format LLMs can use directly?
Yes. Every row is typed, normalized JSON with the same key names on every run — no HTML, no selectors, no dotted-string flattening. Pass a row straight into an LLM context window, index text, hashtags[].name and mentions into a vector store, or hand the whole array to an agent tool. Missing values arrive as typed empty defaults rather than absent keys, so no normalization pass is needed first.
Can I use TikTok Video Scraper without managing proxies?
Yes, in the sense that there is nothing to manage: no proxy input exists and none is configured, so you never create a proxy account or rotate an IP. Be aware of the flip side — no residential rotation happens either. Requests go out over Apify's default egress with retries and a browser-accurate header set as the only defences.
What happens when TikTok changes its structure or blocks the scraper?
The Actor is maintained. Because it parses TikTok's embedded JSON payload rather than CSS selectors, a front-end redesign generally does not affect it, and the related-videos path has a DOM fallback for when the API interception comes up empty. Your field names and types do not change on your end when the upstream shape moves.
🔗 Related scrapers
| Scraper Name | What it extracts |
|---|---|
| TikTok Comments Scraper With Commenter Profile Contacts | Comment threads on a video, with commenter profile and contact details |
| TikTok Profile Scraper With Bio Link Contact Leads | Creator profiles, bio links and contact leads from the profile page |
| TikTok Trending Hashtags Scraper — Find New Rising Tags | Rising and trending hashtags for discovery work |
| TikTok Trending Hashtags Analytics Scraper | Trending-hashtag analytics on a pay-per-event basis |
💬 Your feedback
Found a bug, or need a key that is present in TikTok's payload but not in the output? Open an issue on the Actor's Issues tab in Apify Console. Reports that include the exact input JSON and a video URL that reproduces the problem are the fastest to fix — and if a field you need is sitting unused in itemStruct, say which one and it can be added.
