TikTok Scraper — Profiles, Videos, Hashtags & Search avatar

TikTok Scraper — Profiles, Videos, Hashtags & Search

Pricing

from $0.40 / 1,000 results

Go to Apify Store
TikTok Scraper — Profiles, Videos, Hashtags & Search

TikTok Scraper — Profiles, Videos, Hashtags & Search

Scrape TikTok profiles, videos, hashtags, searches, and direct URLs into structured data, with optional MP4 and cover downloads. $0.50 per 1,000 results on the Free tier and as low as $0.40 per 1,000 on volume tiers.

Pricing

from $0.40 / 1,000 results

Rating

0.0

(0)

Developer

Coor Yu

Coor Yu

Maintained by Community

Actor stats

1

Bookmarked

11

Total users

4

Monthly active users

2 days ago

Last modified

Share

Use one Actor for TikTok profiles, creator videos, hashtags, keyword search, and direct video URLs. Export structured metadata, engagement statistics, music, mentions, hashtags, and optional MP4 or cover files.

Why users choose this Actor

  • Combines four common TikTok scraping workflows in one input schema.
  • Returns rich, analysis-ready video and creator metadata.
  • Optionally stores MP4 videos and cover images in the Key-Value Store.
  • Uses the non-residential Apify DEFAULT proxy pool and never silently switches to residential traffic.

Low-cost tiered pricing

  • Free tier: $0.0005 per result — $0.50 per 1,000 results.
  • Bronze: $0.45 per 1,000; Silver and above: $0.40 per 1,000.
  • A small Actor start event and normal Apify platform usage may also apply; see the Pricing tab.
  • Only rows written to the dataset incur the per-result fee.

Choose any combination of profiles, hashtags, keywords, or video URLs in Input, then click Start.

Why this actor

FeatureThis actorTypical alternatives
Inputsprofiles + hashtags + search + direct URLsprofiles only
Output27 fields (incl. music, hashtags, mentions, downloads)10-15 fields
Pricing$0.50 / 1,000 on Free; $0.40–$0.45 on volume tiersPay only for dataset rows
Extraction surfaceInline SIGI_STATE + Universal Data (survives redesigns)Private API (breaks weekly)
ProxyDefault non-residential Apify Proxy pool onlyOften residential or hidden fallback
Asset downloadOptional MP4 + cover JPG → Key-Value StoreNot supported

Proxy policy

All runs use the default non-residential Apify Proxy pool, pinned to US unless you choose another country. Residential groups, custom proxyUrls, and proxy disabling are ignored to prevent accidental residential traffic or cost.

The legacy fallbackToResidential input is ignored.


Input

All fields optional — provide at least one of profiles, hashtags, searchKeywords, or videoUrls.

{
"profiles": ["khaby.lame", "https://www.tiktok.com/@charlidamelio"],
"hashtags": ["fyp", "comedy"],
"searchKeywords": ["summer fashion 2026"],
"videoUrls": [
"https://www.tiktok.com/@khaby.lame/video/7321234567890123456"
],
"resultsPerInput": 50,
"hashtagPostedAfter": "2026-05-01",
"hashtagPostedBefore": "2026-05-19",
"shouldDownloadVideos": false,
"shouldDownloadCovers": false,
"proxy": {
"useApifyProxy": true,
"apifyProxyCountry": "US"
},
"fallbackToResidential": false,
"blockImages": true,
"maxConcurrency": 4,
"requestTimeoutSecs": 45,
"maxAttemptsPerSeed": 2,
"maxSearchAttempts": 1
}

Full schema: see ./.actor/input_schema.json.

Hashtag time window

hashtagPostedAfter / hashtagPostedBefore filter hashtag-sourced videos by their TikTok createTime. Accepts ISO 8601:

  • Date-only — "2026-05-01" — anchored to 00:00:00 UTC.
  • Date-time — "2026-05-01T14:30:00Z" — used as-is.

The filter runs inside the XHR sniffer, so out-of-window videos are dropped before billing — you only pay for what you keep.

⚠️ Hashtag feeds aren't strictly chronological — TikTok mixes viral-old and fresh content. If you need 50 recent results, set resultsPerInput to ~150-250 so the filter has enough room to find them. The scroller is given 2× patience (stagnant-threshold 8 vs the default 4) when the filter is active.

The filter is scoped to hashtagsprofiles, searchKeywords, and videoUrls ignore both fields.


Output (one row per video)

{
"videoId": "7321234567890123456",
"webVideoUrl": "https://www.tiktok.com/@khaby.lame/video/7321234567890123456",
"text": "When you finally fix the bug 🐛",
"createTimeISO": "2026-05-12T14:32:11.000Z",
"duration": 18,
"videoDownloadUrl": "https://v16-webapp-prime.tiktok.com/.../play.mp4",
"coverUrl": "https://p16-sign-va.tiktokcdn.com/.../cover.jpeg",
"author": {
"id": "6748834234567",
"uniqueId": "khaby.lame",
"nickname": "Khaby Lame",
"verified": true,
"avatar": "https://...",
"followers": 162500000
},
"music": {
"id": "7012345678",
"title": "Original Sound",
"author": "khaby.lame",
"playUrl": "https://..."
},
"stats": {
"playCount": 12500000,
"diggCount": 1850000,
"commentCount": 23400,
"shareCount": 156000,
"collectCount": 78000
},
"hashtags": ["fyp", "comedy"],
"mentions": [],
"sourceType": "profile",
"sourceInput": "khaby.lame",
"scrapedAt": "2026-05-19T07:23:11.234Z"
}

If shouldDownloadVideos / shouldDownloadCovers is enabled, additional fields storedVideoKey / storedCoverKey point to entries in the run's Key-Value Store.



Local development

# 1) Install deps
npm install
# 2) Set an INPUT.json under storage/key_value_stores/default/
mkdir -p storage/key_value_stores/default
cat > storage/key_value_stores/default/INPUT.json <<'JSON'
{
"profiles": ["khaby.lame"],
"resultsPerInput": 10
}
JSON
# 3) Run locally (uses Apify CLI if installed, otherwise raw node)
npm start

Output lands in storage/datasets/default/*.json.


Deploy to Apify

# Install Apify CLI once
npm install -g apify-cli
# Login
apify login
# Push from this folder
cd apify-tiktok-scraper
apify push

The actor builds on Apify's servers; first build takes 3-5 minutes.

Current pricing is shown on the Actor Pricing tab.


Architecture

src/main.ts ─ Actor.init → input validation → spawn crawler
src/routes.ts ─ Crawlee router: LIST + DETAIL handlers
src/extractors.ts ─ SIGI_STATE parser + auto-scroll harvester
src/utils.ts ─ URL builders, normalisers
src/types.ts ─ TypeScript types for input/output/internal shapes
.actor/actor.json ─ Actor manifest
.actor/input_schema.json ─ UI form schema
.actor/dataset_schema.json ─ Dataset views (powers Apify Store preview table)
.actor/Dockerfile ─ Build (apify/actor-node-playwright-chrome:20)

Extraction strategy:

  1. Navigate the page with Playwright + fingerprint randomisation.
  2. Read the embedded SIGI_STATE (legacy) or __UNIVERSAL_DATA_FOR_REHYDRATION__ (modern Universal Data) JSON blob — this is TikTok's own server-rendered Redux state.
  3. Map each item to our canonical schema.
  4. Auto-scroll until limit hit or 3 stagnant iterations detected.

Why this approach: TikTok's private REST endpoints (/api/post/item_list, etc.) rotate signatures roughly weekly. The inline state blob is what TikTok's own UI consumes on first paint — far more stable across redesigns.


Using this actor via MCP (Claude / Cursor / ChatGPT)

The actor is auto-exposed as a tool by Apify MCP Server at https://mcp.apify.com/. Apify reads input_schema.json to build the tool definition and dataset_schema.json to advertise the output shape so LLMs know what fields to expect.

Cursor / Claude Desktop config

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/",
"transport": "streamable-http",
"headers": {
"Authorization": "Bearer apify_api_xxxxxxxx"
}
}
}
}

Then prompt your agent:

"Use the tiktok-scraper actor to fetch the latest 30 videos from @khaby.lame and the top 50 results for hashtag #fyp."

Pair this with the tiktok-comments-scraper actor for a complete pipeline: profile → videos → comments → sentiment analysis.


Reliability tips

SymptomFix
SIGI_STATE not foundTikTok occasionally serves an interstitial. Increase requestTimeoutSecs to 120.
Empty results on hashtagsTikTok geo-filters tags; set apifyProxyCountry: "US".
429 / 403 burstsLower maxConcurrency to 1-2 and keep input batches smaller.
Slow scrolling on huge profilesSet resultsPerInput to a finite cap (e.g. 200).

License & disclaimer

MIT. Scrape only public data and respect TikTok's terms. You are responsible for compliance with applicable laws and platform policies.