TikTok Video Scraper - Views, Likes & Hashtags
Pricing
from $1.00 / 1,000 video scrapes
TikTok Video Scraper - Views, Likes & Hashtags
TikTok video scraper that returns views, likes, comments, shares, saves and reposts for any list of video URLs, plus hashtags, sound, exact post date and the creator's follower count. No login, no API key, no browser.
Pricing
from $1.00 / 1,000 video scrapes
Rating
0.0
(0)
Developer
Eimantas V
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Share
TikTok Video Scraper — Views, Likes & Hashtags
TikTok video scraper that returns views, likes, comments, shares, saves and reposts for any list of video URLs, plus hashtags, the sound, the exact post date and the creator's follower count. No login, no API key, no browser.
Paste video URLs, get a spreadsheet of engagement. It is built for the job most TikTok data actually gets bought for: measuring how a specific set of posts performed — a campaign's deliverables, a competitor's last thirty uploads, a hashtag's top posts you collected elsewhere.
What you get
One row per video. Every field below was populated on a real captured video;
null means TikTok did not return it, never a stand-in zero.
| Field | Type | Notes |
|---|---|---|
playCount | integer | null | Views |
likeCount | integer | null | |
commentCount | integer | null | |
shareCount | integer | null | |
collectCount | integer | null | Saves |
repostCount | integer | null | Only present in TikTok's newer stats block |
engagementRate | number | null | (likes+comments+shares) ÷ views, as a % |
description | string | null | The caption |
createdAt | string | null | A real UTC timestamp, not a relative string |
createdAtUnix | integer | null | |
hashtags | array | Tags the creator typed, in caption order and original casing |
challenges | array | Every challenge TikTok associates — a superset of the above |
mentions | array | @-handles in the caption |
musicTitle / musicAuthor / musicId | string | null | The sound |
musicIsOriginal | boolean | null | Original audio vs a licensed track |
durationSeconds / width / height | integer | null | |
coverUrl | string | null | |
authorHandle / authorName / authorId | string | null | |
authorFollowerCount | integer | null | The creator's followers at scrape time |
authorHeartCount / authorVideoCount | integer | null | |
authorVerified | boolean | null | |
authorSignature | string | null | Creator bio |
isAd | boolean | null | Branded content flag |
isAigc | boolean | null | AI-generated content flag |
locationCreated | string | null | Country code |
duetEnabled / stitchEnabled | boolean | null | |
commentsDisabled | boolean | null |
Engagement rate is null when it cannot be computed
If views are missing or zero, engagementRate is null rather than 0 or
Infinity. A rate over an unknown denominator is not a measurement, and a
buyer sorting on it would otherwise be sorting on an artefact.
hashtags and challenges are different things, and are kept apart
TikTok exposes hashtags in two places that disagree, and the longer list is not the better one.
hashtags is what the creator actually typed. It comes from the caption,
with character offsets that prove where each tag appears, so it preserves the
creator's own casing rather than TikTok's normalised form.
challenges is TikTok's own association set. It uses canonical casing and
it can include tags that appear nowhere in the caption. On the video used to
build this Actor the two differed twice over: the creator typed
#petsoftiktok, challenges reports it as PetsOfTikTok, and challenges
additionally carried bonevoyage — a tag the caption never contained. Merging
them would have made every row assert something false about the creator, so the
two stay separate.
Use hashtags for "what did they tag this with". Use challenges for reach and
discovery analysis, where TikTok's own associations are the point.
Input
{"videos": ["https://www.tiktok.com/@scout2015/video/6718335390845095173","https://www.tiktok.com/@nasa/video/7016181555718065413","6718335390845095173"],"maxVideos": 1000,"requestDelayMs": 1500}
Full video URLs, photo-post URLs, /v/ and /embed/v2/ links, and bare numeric
ids all work; extra query parameters are ignored. You can also chain from
another Actor's dataset with sourceDatasetId.
Pricing
| Event | Price | When |
|---|---|---|
video-scraped (primary) | $1.00–$2.00 per 1,000 | Per video row written |
apify-actor-start | $0.00005 | Once per run |
Your whole bill is videos × row price + $0.00005. One request produces one
row, so there is no second event and nothing to forecast around.
Never charged: error rows of any kind; a video TikTok reports as deleted or non-existent; inputs that are not readable video URLs; and any request that came back blocked and was retried. Full arithmetic and the measured cost basis are in docs/PRICING.md.
What this does NOT do
Stated plainly, because a listing that overpromises collects one-star reviews:
- No comments. TikTok's comment endpoint was measured to answer either a
91-byte
status_code: 5envelope or an HTTP 200 with zero bytes unless the request carries a rotatingX-Bogus/msTokensignature. Reverse-engineering a rotating signature is a maintenance treadmill, so this Actor does not pretend to offer comments. - No video discovery. You supply the video URLs. Profile pages, tag pages, the explore feed and music pages were all measured to carry no video list server-side — zero ids across every route tried. Pair this with a discovery Actor if you need to find videos first.
- No profile-only scraping. Pass videos. A profile URL is rejected with a message telling you so.
- No video file downloads or transcripts.
- No
vm.tiktok.comshort links. Expanding one needs a redirect this Actor does not follow; it is rejected with a message rather than guessed at. - No historical or time-series data. Every count is as of the moment it was scraped. Run it on a schedule if you want a series.
- Private and deleted videos return an error row, not data.
How it works, and the honest part
One plain HTTP GET per video. TikTok server-renders the full video metadata into the watch page, so there is no browser, no login, no cookie, no API key and — the part that matters for durability — no signed request. Nothing here rotates on a TikTok deploy.
Reliability
- Measured 19/20 on real videos from Apify datacenter IPs, the single miss a timeout rather than a block. Datacenter is the IP class the platform actually runs on, which is why that is the number quoted rather than a residential one.
- Deleted is distinguishable from blocked. TikTok answers a missing video
with HTTP 200 and
statusCode 10204/"item doesn't exist", where a block has no rehydration payload at all. Those become different rows: a typedvideo-not-founderror that is never billed, versus a retry on a fresh IP. - Blocked requests are retried on a different datacenter IP, twice, with a hard backoff. A not-found is never retried — TikTok will say the same thing from every IP, so retrying spends requests to learn nothing.
- The id is checked on the way out. TikTok serves a video even when the handle in the URL is wrong, so the Actor refuses to emit a row whose returned id does not match the one requested.
- The response gate never keys on size. A not-found page measured 369 KB against a real page's 394 KB — close enough that any size threshold would misclassify. Only shape is used.
robots.txt
You should know this before you buy, so it is here rather than buried.
https://www.tiktok.com/robots.txt disallows a broad set of paths and bans
several named crawler user-agents outright. This Actor requests the ordinary
public watch page, sends no credentials, and bypasses no authentication or
access control — it reads what any logged-out visitor sees. Whether that is
acceptable for your purpose is a decision for you and your legal advisers, and
it depends on your jurisdiction and what you do with the data.
Proxy
Apify datacenter proxy, on by default, and used to rotate IPs when a video comes back blocked. Residential is deliberately not offered: it is billed per gigabyte, and at 87 KB per video it would dominate the cost of a run for a transport that was measured not to need it.