TikTok Profile, Hashtag, Search, Video & Trending Scraper
Pricing
from $2.20 / 1,000 results
TikTok Profile, Hashtag, Search, Video & Trending Scraper
Scrape TikTok without login. Extract profiles, bios, follower stats and videos; search by hashtag or keyword; scrape individual videos with full engagement stats; or collect posts from the current trending feed.
Pricing
from $2.20 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
1
Bookmarked
94
Total users
21
Monthly active users
4 days ago
Last modified
Categories
Share
TikTok Scraper: Profiles, Videos, Hashtag Search & Trending Feed
TikTok turns into a clean, structured feed with this scraper. Give it usernames to get a public profile plus its videos, hashtags or keywords to get matching videos, direct video links to get full stats for a single video, or run it with no input at all to get the current trending feed. No login is ever required. Export the result to JSON, CSV or Excel, or pull it straight into your app through the API.
Why This Scraper?
- Five ways in. Profile, hashtag/keyword search, video, trending, and paste-any-link URL mode in one actor, so you do not need separate tools for each job.
- Profiles. Profile mode returns the full public profile (bio, avatar, follower/following/heart/video counts, verified flag) plus a paginated list of that account's videos.
- Search. Search mode accepts a plain keyword or a hashtag (start the term with #) and returns matching videos either way.
- Video stats. Video mode returns full stats for a single video: plays, likes, comments, shares and saves, plus music info, hashtags, duration and cover images.
- Trending. Trending mode needs no input at all beyond the mode itself; run it as-is to pull the current trending feed.
- Public data only. Public data only: the actor never logs in, so it can only read what is publicly visible; private accounts and their videos return no data.
- Honest run reporting. Clear, honest run reporting: a run that could not read anything fails loudly, so an empty dataset always means "nothing matched", never "something broke".
Use Cases
- Social listening and brand monitoring: track a hashtag or keyword and pull matching videos as they post.
- Creator and competitor research: collect a profile's stats and video history to benchmark growth and content.
- Trend and sound discovery: scan the trending feed or a hashtag to see what is gaining traction right now.
- Dataset building for analytics or ML: collect video metadata (captions, stats, hashtags, music) at scale for research.
Data You Get
Sample shape: values are illustrative placeholders, not from a live profile or video.
| Field | Example |
|---|---|
recordType | "video" (also "profile") |
sourceMode | "search" (also "profile", "video", "trending") |
id | "7000000000000000000" (video only) |
url | link to the video or profile |
uniqueId | "samplecreator" (profile only) |
nickname | "Sample Creator" (profile only) |
authorUsername | "samplecreator" (video only) |
desc | "Sample video caption #sample" (video only) |
playCount | 500000 (video only) |
diggCount | 42000 (likes; video and profile both carry this field) |
commentCount | 1200 (video only) |
shareCount | 1200 (video only) |
collectCount | 3300 (saves; video only) |
followerCount | 125000 (profile only) |
followingCount | 340 (profile only) |
heartCount | 2400000 (total likes received; profile only) |
videoCount | 210 (profile only) |
verified | false (profile only) |
createTimeIso | "2026-01-01T00:00:00.000Z" (video only) |
scrapedAt | "2026-01-01T00:00:00.000Z" |
Video records also carry authorNickname, authorSecUid, authorAvatarUrl, duration, width, height, musicId, musicTitle, musicAuthor, musicUrl, hashtags, coverUrl, dynamicCoverUrl, originCoverUrl and region. Profile records also carry secUid, signature (bio), avatarUrl, privateAccount and bioLink.
How to Use
- Pick a mode:
profile(usernames or profile URLs),search(hashtags or keywords),video(direct video URLs),trending(no input needed), orurl(paste any TikTok link, see URL mode below). - Fill in the field for that mode; leave the other mode fields blank.
- Set Max results per unit to control how many videos come back per username, per query, or from the feed, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Profile mode:
{"mode": "profile","usernames": ["samplecreator", "@anothercreator"],"maxItems": 20}
Hashtag or keyword search:
{"mode": "search","queries": ["#fyp", "cooking tutorial"],"maxItems": 20}
Single video, full stats:
{"mode": "video","videoUrls": ["https://www.tiktok.com/@samplecreator/video/7000000000000000000"]}
Any TikTok link (URL mode):
{"mode": "url","startUrls": ["https://www.tiktok.com/@tiktok", "https://www.tiktok.com/@tiktok/video/7673909736131038495"],"maxItems": 20}
Trending mode needs only {"mode": "trending", "maxItems": 20}.
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/tiktok-scraper").call(run_input={"mode": "search", "queries": ["#fyp"], "maxItems": 20})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item.get("desc"), item.get("playCount"))
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/tiktok-scraper').call({ mode: 'search', queries: ['#fyp'], maxItems: 20 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
URL mode
Set Mode to url and paste links; each is routed by shape. Anything else gets a warning in the run log and is counted as unreachable, never silently skipped.
| URL shape | Example | Routed to |
|---|---|---|
| Profile | https://www.tiktok.com/@tiktok | profile mode |
| Video | https://www.tiktok.com/@tiktok/video/7673909736131038495 | video mode |
| Hashtag | https://www.tiktok.com/tag/fyp | hashtag search |
| Keyword search | https://www.tiktok.com/search?q=cats | keyword search |
| Explore | https://www.tiktok.com/explore | trending feed |
| Mobile host | https://m.tiktok.com/@tiktok | rewritten to www.tiktok.com |
| Short link | https://vm.tiktok.com/ZMabc/ | resolved to the profile or video it points to |
URL mode is new. Left untouched, the TikTok URLs field scrapes the two example links (the @tiktok profile and one video). Hashtag, search, /explore and short links are accepted and routed, but are not part of the default. Plain names and #tags already work in search mode.
Video links must be the full link
Short share links (vm.tiktok.com, vt.tiktok.com) are not resolved in video mode. Open the share link once in a browser, copy the full address it redirects to (it looks like https://www.tiktok.com/@username/video/1234567890), and paste that instead.
Post filters
The date and likes filters are applied to the videos the actor reads, after they are read; Max results per unit still caps what is returned. They apply in every mode, including trending, video links and /explore in URL mode. A run where every video is filtered out ends as an honest empty result, not a failure.
Output views
The dataset has four views in the Console: Results (all records), Profiles (profile records only), Videos (video records only) and Changes (rows tagged by incremental mode).
How Max results per unit adds up
The cap applies per unit, not to the whole run: per username in profile mode, per hashtag or keyword in search mode, or to the whole feed in trending mode. Three usernames with a cap of 20 can return up to 60 video records plus the 3 profile records themselves, since a profile record does not count against the video cap. Set it to 0 to walk a unit until it runs out on its own. Only rows actually written count toward the cap: rows skipped because an earlier run already delivered them (resume) or because they are UNCHANGED do not use it up, so a resumed run delivers up to the cap of new rows. Every run also saves an OUTPUT record in the run's key-value store listing, per username, query or link, its status and row count.
Send results into your apps (MCP connectors)
The input form includes an optional Pipe results into your apps field for Model Context Protocol connectors, but connector export is not active in the current build of this actor: selecting a connector has no effect, and the run log notes that nothing was sent. Your results are always in the dataset; to move them into Notion, Airtable, Google Sheets or similar apps today, use the Integrations tab (Make, Zapier, n8n, webhooks) or the API.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
mode | string | profile | profile, search, video, trending, or url. |
usernames | array | ["tiktok"] | Usernames or profile URLs, with or without @. Profile mode only. |
queries | array | ["#fyp"] | Hashtags (start with #) or plain keywords. Search mode only. |
videoUrls | array | one sample video link | Direct TikTok video URLs. Video mode only. |
startUrls | array | the @tiktok profile and one example video | TikTok links for url mode, routed by shape (see URL mode). |
oldestPostDate | string | (none) | Keep only videos posted on or after this date: YYYY-MM-DD, an ISO timestamp, or a relative value such as 30 days. |
newestPostDate | string | (none) | Keep only videos posted on or before this date, same formats. |
leastDiggs | integer | (none) | Keep only videos with at least this many likes. |
mostDiggs | integer | (none) | Keep only videos with at most this many likes. |
maxItems | integer | 3 | Max results per unit: videos per username, per query, or from the feed. 0 = unlimited; values above 1,000,000 are rejected. Not used in video mode: every video URL is fetched once. |
resumeFromRunId | string | (none) | Run or dataset ID of an earlier run; records it already holds are skipped. |
incrementalMode | boolean | false | Tag rows NEW / UPDATED / UNCHANGED / REAPPEARED against the remembered previous run of the same targets; unchanged rows are not pushed or charged. Signed link fields that change on every fetch (coverUrl, dynamicCoverUrl, originCoverUrl, authorAvatarUrl, avatarUrl, musicUrl, musicCoverMediumUrl, subtitleLinks, slideshowImageLinks) and scrapedAt never count as a change. |
stateKey | string | (automatic) | Name of the remembered baseline. Empty keys it on mode, targets and filters. |
emitUnchanged | boolean | false | Incremental mode: also push UNCHANGED rows. |
ignoreFieldsForChanges | array | [] | Incremental mode: field names (for example playCount) not counted as a change. |
proxyTier | string | residential | residential (default) or datacenter. See Plan Requirement below. |
proxy | object | (none; prefilled with Apify Proxy on) | Advanced connection settings. Leave apifyProxyGroups unset so proxyTier above decides Residential vs Datacenter. |
mcpConnectors | array | (none) | Reserved for connector export, which is not active in the current build; leave empty. |
notionParentPageUrl | string | (none) | Only used by connector export (not active in the current build). |
maxNotifyListings | integer | 50 | Only used by connector export (not active in the current build). |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"recordType": "video","sourceMode": "search","id": "7000000000000000000","url": "https://www.tiktok.com/@samplecreator/video/7000000000000000000","desc": "Sample video caption #sample","createTime": 1767225600,"createTimeIso": "2026-01-01T00:00:00.000Z","authorUsername": "samplecreator","authorNickname": "Sample Creator","authorSecUid": "MS4wSample0000000000000000000000000000000000000000000","authorAvatarUrl": "https://p16-sign-va.tiktokcdn.com/sample-avatar.jpeg","duration": 18,"width": 1080,"height": 1920,"playCount": 500000,"diggCount": 42000,"commentCount": 1200,"shareCount": 1200,"collectCount": 3300,"musicId": "7000000000000000001","musicTitle": "Sample Sound","musicAuthor": "Sample Artist","musicUrl": "https://sf16-ies-music.tiktokcdn.com/sample-music.mp3","hashtags": ["sample", "fyp"],"coverUrl": "https://p16-sign-va.tiktokcdn.com/sample-cover.jpeg","dynamicCoverUrl": "https://p16-sign-va.tiktokcdn.com/sample-dyncover.jpeg","originCoverUrl": "https://p16-sign-va.tiktokcdn.com/sample-origcover.jpeg","region": "US","scrapedAt": "2026-01-01T00:00:00.000Z"}
Plan Requirement
The default Proxy tier is Residential. Datacenter was measured to work too: profile and video pages loaded with full data through the datacenter tier in our checks, so it is a valid cheaper choice, though TikTok can answer some connections with a short "Please wait" challenge page (HTTP 200, no data) and results can vary by run. The actor treats that page as a failed load, never as an empty result, and tries the next connection.
Apify free plans do not include Residential proxies. On a free plan the run logs that the selected connection is unavailable and continues on a backup connection; some pages may still be unavailable. If nothing can be loaded the run fails with one of these messages:
- "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required."
- "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested TikTok pages."
- "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."
A paid plan with Residential access, or your own proxy URLs, avoids all three.
FAQ
How much does it cost?
This actor is pay per event, and two events are billed: Actor start (once per run, scaled by the memory the run uses, per GB) and Result (once per record written to the dataset: each profile, video or search hit). Nothing else is charged; video mode has no separate per-video fee. The Pricing tab shows the current rates. Use Max results per unit to cap the cost of any run.
Is it legal to scrape TikTok?
This actor collects only publicly available profile and video data; login is never required. You are responsible for how you use it: follow TikTok's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Video, cover and music files themselves may belong to their creators or rights holders even though the metadata is public.
Can I run this on a schedule to catch new videos?
Yes, schedule it from the Schedules tab. Turn on Incremental mode to tag each row NEW, UPDATED (with changedFields), UNCHANGED or REAPPEARED against the previous run of the same targets (firstSeenAt / lastSeenAt are added too). Unchanged rows are not pushed and so are not billed unless you enable Emit unchanged. Link fields that TikTok re-signs on every fetch (coverUrl, dynamicCoverUrl, originCoverUrl, authorAvatarUrl, avatarUrl, musicUrl, musicCoverMediumUrl, subtitleLinks, slideshowImageLinks) and scrapedAt are ignored when deciding whether a row changed, so a changed cover image alone does not make a row UPDATED. Engagement counters change constantly, so list the ones you do not care about in Ignore fields when detecting changes. Videos that disappear are not reported, because TikTok feeds have no end marker. With it off, output is exactly as before. To continue an interrupted run, paste its run ID into Resume from run or dataset ID.
Why does video mode say it could not find a video?
Video mode needs the full link, https://www.tiktok.com/@username/video/1234567890. Short share links (vm.tiktok.com, vt.tiktok.com) are not resolved; open one in a browser first and copy the full address it redirects to.
Why did my run fail instead of returning an empty dataset?
A run fails only when it collected 0 records and at least one target could not be read at all, or loaded without carrying any usable data; the status message says which. A keyword or hashtag that genuinely matches nothing still ends as a successful, honestly empty run. If some targets were read and others were not, the run stays green with a warning: the dataset is just incomplete for the targets that failed. For trending mode specifically, a run that fails outright most often means the feed's underlying request shape changed; please report it so the mapping can be refreshed.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client like any other actor. Sending results to connected apps through the connector field is not active in the current build; use the Integrations tab instead.
π Want more TikTok data?
Pair this actor with these related scrapers from the same team:
| π§© TikTok Comments Scraper Scrape comments from TikTok videos using one or more video URLs or IDs. Extract comment... | π± Tiktok Live Recorder Record TikTok live streams to MP4 with full metadata, all stream quality URLs, and... |
| π§© TikTok Shop Scraper Scrape TikTok Shop by keyword, category or product URL. Extract prices, discounts, sales... | π± Mastodon From $1/1K. Scrape trending Mastodon profiles and related posts from any Mastodon... |
| π± Lemon8 Profile Scraper Scrape Lemon8 user profiles with automated multi-profile discovery. Extract profile data... | π± Lemon8 Search Scraper Scrape Lemon8 posts by keyword across multiple regions. Extract posts, images, videos... |
π Browse all abotapi scrapers
π¬ Support
- π Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- π οΈ Need another site, extra fields or a private build? Email contact@abotapi.com.
- β Enjoying it? A quick review on the actor page helps other users find it.