Use it for trend and hashtag research, brand and competitor monitoring, creator and music discovery, or structured datasets for analysis and AI/ML pipelines. It runs in your Apify account, and you do not need a Douyin login or cookies.
What it collects
- Posts: ID, URL, full caption, publish time and region.
- Creators: the poster’s name, handle, follower count, verification and bio, on every result at no extra charge.
- Engagement: likes, comments, and share and collect counts.
- Media: video duration, dimensions, format, cover images, and play and download URLs, plus the sound’s title and play URL.
- Hashtags on each post, and the keyword that found it.
How it works
Douyin Scraper searches Douyin by keyword and pushes the results to your dataset as structured JSON. There is no headless browser, no signing algorithm to maintain and no login wall to work around on your end. Give it one or more keywords and it queues exactly the searches needed, deduplicating across keywords and respecting your maxItems cap and pay-per-event budget as it goes.
Each keyword is searched on its own, and a post that two different keywords surface is stored, and charged, once. A sort order, a publish-time window and a duration filter narrow the results without any extra cost. Every result carries the posting creator’s profile, so no separate profile lookup is needed.
Use cases
- Trend and content researchers — see what’s trending under a hashtag or topic on Douyin.
- Brand and competitor monitoring — track how competitor keywords or brand names perform across creators.
- Music and creator discovery — surface the creators and sounds driving a topic.
- Data analysts — build structured datasets of Douyin search results for modeling.
- AI/ML pipelines — feed real Douyin post and creator data into downstream models.
Input
Set these in the Actor’s input form on Apify, or send them as JSON through the API.
-
keywords— Required: at least one. The search terms. Chinese, English, hashtags, brand names and emoji all work. Each keyword is searched on its own, and results are deduplicated across keywords, so you never pay twice for the same post. -
maxResultsPerQuery— Results per keyword.0fetches all available results, capped at 2,000. The input form pre-fills 100. -
sort— Result order:general, the default, shown in the form as “Relevance (general)”;most_liked(“Most liked”); orlatest(“Latest”). -
publishTime— Publish window:unlimited, the default;one_day(“Past day”);one_week(“Past week”); orhalf_year(“Past 6 months”). -
duration— Video length:unlimited, the default;under_1m(“Under 1 minute”);one_to_five(“1 to 5 minutes”); orover_5m(“Over 5 minutes”). -
maxItems— Overall cap across all keywords combined.0or empty sets no overall cap, and each keyword is still capped bymaxResultsPerQuery. The input form pre-fills 1,000. -
customMapFunction— Optional synchronous(object) => objectJavaScript function applied to every item. It may add or reshape fields but must not filter items. Leave it empty to store items unchanged.
Custom map function
Use customMapFunction only to add or reshape fields. The README’s example adds an isViral flag to posts with more than 10,000 likes:
(object) => {
return { ...object, isViral: (object.statistics?.diggCount || 0) > 10000 };
}; The Actor enforces three rules. A violation is logged and the function is ignored; the run never crashes.
- No filtering. Returning
null,{}or a non-object falls back to the original item. - No date-based logic. A function whose source contains
new Date,Date.nowor1970is rejected. - Synchronous only.
asyncand generator functions, and returned Promises, are rejected.
The input form adds that the function must not be used for filtering, and that accounts using it for filtering may be banned.
Input tips
Take a quick sample
Set maxResultsPerQuery to 10–20 and run a single keyword: a cheap, fast check that the Actor and your keyword return what you expect.
Get the most-liked posts for a keyword
This input searches one keyword for food content, most-liked first:
{
"keywords": [
"美食"
],
"sort": "most_liked",
"maxResultsPerQuery": 100
} Track a hashtag or trend
Use sort: latest with a publishTime window, such as one_week, to see what’s newly posted, not just what’s historically popular. This input tracks a hashtag’s newest posts from the last week:
{
"keywords": [
"#苹果"
],
"sort": "latest",
"publishTime": "one_week"
} Research several brands or competitors in one run
List every keyword up front. Cross-keyword dedup means an overlapping result is never charged twice, so batching keywords into one run costs the same as running them separately, minus the duplicates:
{
"keywords": [
"iphone",
"huawei",
"xiaomi"
],
"sort": "latest",
"maxResultsPerQuery": 200
} Find long-form content
Combine duration: over_5m with sort: most_liked.
Output
Each run writes its results to an Apify dataset. Each item is one unique Douyin post. A keyword that returns no matches adds no items; it isn’t stored as a placeholder row. In Apify Console, the dataset’s Overview shows the Keyword, Caption, Author, Author handle, Likes, Comments, Published and URL columns.
These are the main fields of an item.
-
idurl— The post ID and its Douyin URL. -
textcaption— The full caption with its trailing hashtags inline, and a separate, shorter caption field. Both are genuine Douyin fields, not derived. -
createTimecreateDate— The publish time as a number, and the publish date. -
authorMeta— Creator name, username, bio (signature), verification and follower count. -
statistics—diggCount(likes),commentCount(comments),shareCountandcollectCount. -
videoMetamusicMeta— Video duration, dimensions, format, covers and media URLs; the sound’s title and play URL. -
hashtags— Hashtags on the post, each with an ID and name. -
searchKeyword— The keyword that returned the post.
{
"id": "0000000000000000001",
"url": "https://www.douyin.com/video/0000000000000000001",
"text": "Example caption #example",
"createDate": "2026-09-01",
"authorMeta": {
"name": "Example creator",
"username": "example_creator",
"verified": false,
"followersCount": 1200
},
"statistics": {
"diggCount": 340,
"commentCount": 12,
"shareCount": 5,
"collectCount": 21
},
"hashtags": [
{
"id": "0",
"name": "example"
}
],
"searchKeyword": "coffee"
} Field reference
Every documented key of a dataset item, with its JSON type. A dot marks a key inside an object.
| Field | Type | Meaning |
|---|---|---|
type | string | The item type: video in the README’s sample. |
id groupId | string | The post ID, and a group ID with the same value in the README’s sample. |
url | string | The post’s Douyin URL. |
shareUrl | string | A share link for the post. |
text | string | The full caption, Douyin’s own desc field, with trailing hashtags inline. |
caption | string | A separate, shorter caption field. |
createTime | number | The publish time. The README does not state its unit. |
createDate | string | The publish date, as in 2026-04-28. |
region | string | A region code: CN in the README’s sample. |
authorMeta.name authorMeta.username | string | The creator’s name and handle. |
authorMeta.id authorMeta.secUid | string | The creator’s IDs. |
authorMeta.signature | string | The creator’s bio. |
authorMeta.verified | boolean | Whether the creator is verified. |
authorMeta.avatarThumb | string | An avatar thumbnail URL. |
authorMeta.followersCount | number | The follower count. |
videoMeta.duration | number | The video’s length. The README does not state its unit. |
videoMeta.width videoMeta.height | number | The video’s dimensions. |
videoMeta.format | string | The video format, as in mp4. |
videoMeta.hasWatermark | boolean | Whether the video carries a watermark. |
videoMeta.cover videoMeta.originCover videoMeta.dynamicCover | string | Cover image URLs. |
videoMeta.playUrl videoMeta.downloadUrl | string | The video’s play and download URLs. |
musicMeta.title musicMeta.playUrl | string | The sound’s title and play URL. |
musicMeta.isOriginal | boolean | Whether the sound is marked as original. |
statistics.diggCount | number | Likes. |
statistics.commentCount | number | Comments. |
statistics.shareCount statistics.collectCount | number | The share and collect counts. |
hashtags | array | Hashtags on the post, each an object with an id and a name. |
searchKeyword inputKeyword | string | The keyword that returned the post. Both hold it in the README’s sample. |
authorMeta.followingCount authorMeta.awemeCount statistics.playCount | number | In the README’s sample, where each is 0; the README does not describe them. Check a run of your own before relying on them. |
How to run it
- Open the Actor on Apify, listed as “Douyin 抖音 Search Scraper”, and sign in. You need an Apify account.
- Fill in the Input tab, then click Start.
- When the run finishes, open the Output tab and click Export to download the dataset as JSON, CSV or another format.
Or send the same input to the Apify API. This call runs the Actor and returns its dataset items. Replace $APIFY_TOKEN with the API token from your Apify Console.
curl -X POST \
"https://api.apify.com/v2/actors/batscrape~douyin-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"keywords":["coffee"],"maxResultsPerQuery":20}' The call waits up to 300 seconds. For a longer run, start it asynchronously and read the dataset when it finishes. Read the Apify API reference.
New to Apify? See how BatScrape Actors run on Apify. To run it on a schedule, or send results on when a run finishes, see schedules and webhooks.
Pricing
Pay per event. Each run is charged an Actor start event, then one result event for every unique post delivered, with no separate per-keyword or per-page fee. A post found by more than one keyword is stored and charged once. Every result is checked against the run’s charge budget before it’s stored, so a run stops cleanly instead of overspending. See current rates on Apify .
Limitations
- It returns post metadata and media URLs, including a play URL. It does not download or store video files.
- Fetching all results for a keyword stops at 2,000. A narrow or rare keyword may return only a few, and one with no matches adds no rows.
- Each keyword stops at
maxResultsPerQuery. The whole run stops when it reachesmaxItemsor can’t afford another result within your charge budget. -
customMapFunctionmay only add or reshape fields. Anull,{}or non-object return falls back to the original item, and a function with date-based logic or asynchronous code is logged and ignored, so items are stored unchanged.
FAQ
Answers from the Actor’s README on Apify.
Do I need a Douyin account or login?
No. Searches run through a backend service, and no login or cookies are required on your end.
Can I search by hashtag?
Yes. Any string Douyin’s own search box accepts works, including hashtags like #美食.
Is creator data included in the price?
Yes, at no extra charge: every result includes the poster’s profile.
Does it download videos?
No. It returns each post’s metadata and media URLs, including a play URL, but does not download or store video files itself.
Why did a run return few or no results?
Check these first:
-
maxItemsandmaxResultsPerQuery: the run stops once either is reached. - The charge budget: a run that can’t afford another result stops cleanly rather than erroring.
- The keyword: a very narrow or rare one may simply have little content on Douyin, so try a broader or more common one.
-
keywordsitself: at least one keyword is required.
How do I keep the cost of a run down?
Three settings help:
- Set
maxResultsPerQueryto only what you need per keyword. - Set
maxItemsto a hard cap that matches your budget. - Batch related keywords into one run: cross-keyword dedup means overlaps are never charged twice.
Related reading
- What to check before budgeting an Actor run — Understand the questions behind a scraping budget: the Actor’s current pricing, your input limits, sample output, and the effect of retries or incomplete runs.
- Planning predictable output for our Apify Actors — How we’re approaching output schemas for BatScrape: explicit fields, example records, and clear change notes before consumers build on a dataset.
- Planning a scraper around the data you need — A practical starting checklist for a scraping task: define the records, inspect a sample, identify useful fields, and make the output easy to validate.
- How to use BatScrape Actors on Apify — Run an Actor in Apify Console or through the API, export its dataset, schedule runs and pass results to automation tools.
Other BatScrape Actors
- TikTok Shop Scraper — Scrape US TikTok Shop products by keyword or store, and full product pages and buyer reviews from product URLs. Export prices, discounts, ratings, units sold, SKU stock and shipping.
- Reddit Scraper — Search Reddit posts or comments by keyword across all subreddits. Export titles, text, subreddits, authors, scores and comment counts, sorted by relevance, newest, top or most comments.
- Threads Scraper — Search Threads by keyword or collect a public account’s latest posts. Export text, authors, engagement, topics, mentions, links and media URLs without a Threads login.
- Realtor.com Scraper — Scrape Realtor.com for-sale, rental and sold listings by city, ZIP code or URL. Export price, beds, baths, photos, sale and tax history, schools and agents.
- TikTok Scraper — Search TikTok by keyword and export unique videos with creator profiles, engagement stats, music, hashtags and media URLs. Sort by relevance or likes, filter by date and region.
- FollowScores Scraper — Extract live football fixtures, results and team profiles for 11 European competitions and 214 teams.
Every published Actor is on the scrapers hub.