TikTok Scraper API
for account videos and keyword search
Pull TikTok videos as clean JSON, either every video from an account or everything matching a keyword or hashtag. Captions, hashtags, engagement counters and playable URLs come back parsed, with proxies and rendering handled for you.
of requests succeed
median response
95% finish faster
per 1k scrapes at volume
TikTok changes weekly. Your code shouldn't notice.
- Signed URLs that expire in an hour
- Signing params you re-derive after every release
- An interstitial instead of the JSON payload
- Rotating residential IPs
- Cursor formats that change without notice
One GET Request. That's the whole integration.
Start with just a query. Add more parameters when your use case needs them.
curl -G 'https://api.hasdata.com/scrape/tiktok/search' \
--data-urlencode 'keyword=dance' \
--header 'x-api-key: <YOUR_API_KEY>' \
--header 'Content-Type: application/json'keyword * Keywordtype Result TypenextPageToken Next Page Tokencurl -G 'https://api.hasdata.com/scrape/tiktok/posts' \
--data-urlencode 'handle=tiktok' \
--header 'x-api-key: <YOUR_API_KEY>' \
--header 'Content-Type: application/json'handle * TikTok HandlenextPageToken Next Page TokenSearch all of TikTok, or one account in full
Search returns a videos array and an account feed returns posts, with the same fields on each video apart from the two ready-made links search adds to the author. Read pagination.count for how many came back.
// From the search endpoint: keyword or hashtag results from across TikTok.
[
{
"id": "7675122879293410591",
"description": "Have you ever wondered what each shoe is made for? 🤭🏃♀️ should …",
"hashtags": [
"runner",
"running",
"runningshoes",
"marathontraining",
"runningshoesexplained"
],
"createTime": "2026-08-17T21:56:58.000Z",
"url": "https://www.tiktok.com/@saramontoyar/video/7675122879293410591",
"duration": 36,
"ratio": "720p",
"coverUrl": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/…",
"playUrl": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast8/tos-use…",
"downloadUrl": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast5/tos-use…",
"author": {
"id": "7330816931056845866",
"username": "saramontoyar",
"nickname": "Sara🏃♀️Runner&Gym Girl",
"hasdataLink": "https://api.hasdata.com/scrape/tiktok/profile?handle=saramontoya…",
"hasdataPostsLink": "https://api.hasdata.com/scrape/tiktok/posts?handle=saramontoyar"
},
"music": {
"id": "7128257885397583874",
"title": "Naughty Girl",
"authorName": "Beyoncé",
"playUrl": "https://sf19.tiktokcdn-us.com/obj/tos-alisg-ve-2774/cbe9585cb0eb…"
},
"likes": 495,
"comments": 31,
"shares": 36,
"plays": 10600,
"collects": 137,
"reposts": 0
}
]id stringVideo id, the number after /video/ in the URL
description stringCaption text as posted, absent when the video has no caption
hashtags string[]Tags parsed out of the caption, without the #. Absent when the caption has none
mentions string[]@-mentions parsed from the caption, present when the caption has one
createTime stringUpload time, ISO 8601
url stringVideo URL
duration numberLength in seconds
ratio / width / height string, numberQuality label as TikTok reports it, such as 720p, with the pixel size. The label is missing on some videos
coverUrl / dynamicCoverUrl stringStill and animated thumbnails
playUrl / downloadUrl stringPlayable and downloadable video URLs, when TikTok exposes them for that video
author objectid, username and nickname, plus verified when the account carries the badge
music objectid and title always, plus authorName, original and playUrl when TikTok returns them
likes / comments / shares numberEngagement counters
plays / collects / reposts numberViews and saves. reposts is present but comes back 0
author.hasdataLink / hasdataPostsLink stringReady-made calls for that account's profile and its videos. Search results only
// From the posts endpoint: one page of a single account's uploads, by handle.
[
{
"id": "7654752160051121422",
"description": "AJ Dybantsa drafted 1st overall by the @Washington Wizards 🏀 #NB…",
"hashtags": [
"nbadraft",
"nba",
"basketball",
"ajdybantsa",
"washingtonwizards"
],
"mentions": [
"wash_wizards"
],
"createTime": "2026-06-24T00:28:10.000Z",
"url": "https://www.tiktok.com/@nba/video/7654752160051121422",
"duration": 28,
"ratio": "720p",
"coverUrl": "https://p16-common-sign.tiktokcdn-us.com/tos-useast5-p-0068-tx/o…",
"playUrl": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast5/tos-use…",
"downloadUrl": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast5/tos-use…",
"author": {
"id": "134941634731003904",
"username": "nba",
"nickname": "NBA",
"verified": true
},
"music": {
"id": "7654752185711856397",
"title": "original sound",
"authorName": "NBA",
"original": true,
"playUrl": "https://v16m.tiktokcdn-us.com/33a5dfa5673cabb81bc3323620df2ac9/6…"
},
"likes": 682400,
"comments": 2826,
"shares": 21000,
"plays": 5600000,
"collects": 29467,
"reposts": 0
}
]id stringVideo id, the number after /video/ in the URL
description stringCaption text as posted
hashtags string[]Tags parsed out of the caption, without the #
mentions string[]@-mentions parsed from the caption, present when the caption has one
createTime stringUpload time, ISO 8601
url stringVideo URL
duration numberLength in seconds
ratio / width / height string, numberQuality label as TikTok reports it, e.g. 720p, and the pixel size
coverUrl / dynamicCoverUrl stringStill and animated thumbnails
playUrl / downloadUrl stringPlayable and downloadable video URLs, when TikTok exposes them for that video
author objectid, username and nickname, plus verified when the account carries the badge
music objectid, title, authorName, original, playUrl
likes / comments / shares numberEngagement counters
plays / collects / reposts numberViews and saves. reposts is present but comes back 0
{
"count": 33,
"hasMore": true,
"nextPageToken": "1786839505673"
}count numberItems in this page
hasMore booleanWhether another page exists
nextPageToken stringPass it back to get the next page
// From the search endpoint with type=user: accounts instead of videos.
[
{
"id": "7664638705177150477",
"username": "nasa",
"nickname": "NASA",
"signature": "Making the seemingly impossible, possible.✨",
"verified": true,
"followers": 993200,
"avatarUrl": "https://p16-common-sign.tiktokcdn.com/tos-maliva-avt-0068/e51432…",
"hasdataLink": "https://api.hasdata.com/scrape/tiktok/profile?handle=nasa",
"hasdataPostsLink": "https://api.hasdata.com/scrape/tiktok/posts?handle=nasa"
}
]username stringHandle, as in the profile URL
id / secUid stringNumeric and opaque account ids
nickname stringDisplay name
signature stringBio text, absent when the account left it empty. Named signature here, biography in the Profile API
verified booleanVerified badge, present only on verified accounts
followers numberFollower count
avatarUrl stringAvatar CDN URL
hasdataLink / hasdataPostsLink stringReady-made calls for this account's profile and videos
The hard parts already happened
JS rendering, signed URL refresh and residential proxy rotation run on our side. Your code sends one GET and reads fields.
Discover similar
scrapers and APIs
to expand your projects.
TikTok Profile Scraper API
Social Media Intelligence • $0.75 / 1k Request
Instagram Posts Scraper API
Social Media Intelligence • $0.75 / 1k Request
Fits right into your stack.
Works with the tools you already use.
View Documentation ->Teams that deleted their scraper
Now it's the part of the pipeline they don't think about
HasData delivers exactly what we need: speed and comprehensive search features. It's the fastest API we've used in this space. Plus, their customer support is fantastic.
We rely on HasData for search performance data and broader scraping needs. Their APIs deliver highly structured data that integrates directly into our platforms.
Great web scraping API which is incredibly easy to use. It requires minimal effort to get up and running, and the documentation is very clear and helpful.
I needed to scrape some information they didn't already support, and they wrote the code for me right away, which was super nice of them.
We were particularly impressed with how easily we could integrate HasData into our existing workflow.
Plans that get cheaper at scale
Fixed price, fixed volume, no surprises at the end of the month. Upgrade when you need more.
Free
Startup
Business
RecommendedGrowth
Monthly scrape volume
Custom price based on required volume
Past 100M credits a month, or terms the self-serve plans do not cover. We shape the contract around your workload. Past 100M credits a month, or need terms the self-serve plans do not cover? We shape the contract, concurrency, and support around your workload.
HasData accesses publicly available data only. TikTok's terms may restrict automated access; you are responsible for compliance. Where data includes personal information, ensure a lawful basis under GDPR/CCPA.
Questions, answered
Per successful request. The number of videos that come back does not change the price, and a failed request costs nothing.
Yes, a one-time trial of 100 scrapes with every field included. No credit card required.
Paid plans start at $49 per month billed monthly for 20,000 scrapes and scale to 10 million a month. The unit price drops with volume, from $2.45 down to $0.75 per 1,000 scrapes.
Account videos take a handle and return that account's uploads. Search takes a keyword and returns matching videos from across TikTok.
Yes. Set type to user and the response comes back in a users array instead of videos, with the handle, nickname, bio, avatar, verified flag and follower count. The bio is named signature there, while the Profile API calls it biography, so map that one field when you switch between them.
Yes. Pass the tag as the keyword and search returns videos relevant to it. Percent-encode the hash as %23 if your client does not do it for you, otherwise the tag is read as a URL fragment and never reaches the API. When a caption carries tags, the video comes back with a hashtags array, so exact-match filtering happens in your code.
Each response carries hasMore and nextPageToken. Pass the token back on the next call and you continue where you stopped.
No. Requests run on HasData infrastructure against publicly available data, so there is nothing to apply for and no OAuth flow to wire up.
Nothing on your side. We track the changes and keep the response schema stable. Field names and types stay put, and an optional field is simply absent from a video that has no value for it, so read those with a default.
No. Requests run on HasData's infrastructure, so there's nothing to provision or maintain. You're responsible for using the results in line with each target site's terms and applicable law.
Your first scrape
is minutes away
100 scrapes free · no credit card