# Douyin Video Downloader & Scraper - No Watermark (`hgservices/douyin-scraper`) Actor

Download Douyin (抖音) videos without watermark and scrape profiles: captions, likes, comments, views, music & hashtags. Export to JSON, CSV, or Excel. No login, no code.

- **URL**: https://apify.com/hgservices/douyin-scraper.md
- **Developed by:** [Harish Garg](https://apify.com/hgservices) (community)
- **Categories:** Videos, AI, MCP servers
- **Stats:** 3 total users, 3 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Douyin Scraper — Download Videos & Export Profile Data (No Watermark)

Scrape **Douyin (抖音)**, China's version of TikTok, and export clean, structured data in seconds. Get **no-watermark video download URLs**, captions, view/like/comment counts, comment threads, author details, music, hashtags, and more — from single videos, entire creator profiles, or **keyword search**. No login, no coding, no browser extensions.

Export results to **JSON, CSV, or Excel**, or pull them straight into your app via the Apify API.

### What this Douyin scraper does

- 🎬 **Download Douyin videos without watermark** — get the direct MP4 URL for any video, or have the actor download the file for you.
- 🔎 **Search Douyin by keyword** — discover videos by search term, with sort, publish-window, and duration filters — no URLs needed.
- 👤 **Scrape Douyin profiles** — pull a creator's full video history, newest first.
- 📊 **Export engagement metrics** — likes, comments, shares, and saves for every video.
- 🖼️ **Handles photo/slideshow posts** — image posts are captured too, not just videos.
- 🎵 **Get the background music** — track title and audio URL for each post.
- ⚡ **Fast and reliable** — no login or cookies required; just paste a link and run.

### What data you get

For every Douyin post, this scraper returns:

| Data | Description |
|------|-------------|
| No-watermark video URL | Direct MP4 link, ready to download or embed |
| Caption & hashtags | Full description text plus extracted hashtags |
| Author | Display name, unique ID, and profile ID |
| Engagement | Likes, comments, shares, and saves |
| Publish date | When the video was posted |
| Duration & region | Video length and country |
| Cover image | Thumbnail URL |
| Music | Track title and audio URL |
| Photo posts | Image URLs for slideshow-style posts |
| Comments (optional) | Top-level comments with text, author, likes, reply count, and IP region |
| On-screen text (OCR) | Douyin's own OCR of text shown in the video, when available |

### How to use the Douyin scraper

1. **Add Douyin links or keywords** — paste one or more video URLs (e.g. `https://www.douyin.com/video/1234...` or a `v.douyin.com` share link), creator profile URLs, and/or search keywords. You can mix all three.
2. **Set your options** — choose how many videos to pull per profile or per keyword, the search sort/publish/duration filters, whether to include engagement stats, and whether to download the video files.
3. **Run and export** — click **Start**, then download your results as JSON, CSV, or Excel, or fetch them via the API.

#### Example input

```json
{
  "startUrls": [
    "https://www.douyin.com/video/7045159024525905183"
  ],
  "profileUrls": [
    "https://www.douyin.com/user/MS4wLjABAAAARhKifOuUM5SVneEoxKNleQQxW3tKGDzdd9rah6nEpLc"
  ],
  "maxVideosPerProfile": 30,
  "includeStats": true,
  "downloadVideos": true,
  "mediaFormat": "video"
}
````

#### Example output (one video)

```json
{
  "awemeId": "7045159024525905183",
  "url": "https://www.douyin.com/video/7045159024525905183",
  "desc": "37岁再次手绘家乡图寻亲 #抖音寻人",
  "createTime": "2021-12-24T14:55:12+00:00",
  "authorName": "黎方富",
  "durationMs": 123513,
  "region": "CN",
  "videoUrlNoWatermark": "https://v9-v2-mps-cdn.douyinvod.com/.../main.mp4",
  "videoMirrors": ["https://v9-v2-mps-cdn.douyinvod.com/.../main.mp4", "..."],
  "downloadedFileUrl": "https://api.apify.com/v2/key-value-stores/<storeId>/records/7045159024525905183.mp4",
  "cover": "https://p3-pc-sign.douyinpic.com/...jpeg",
  "musicUrl": "https://sf11-cdn-tos.douyinstatic.com/obj/ies-music/....mp3",
  "hashtags": ["抖音寻人"],
  "seoOcrContent": "37岁再次手绘家乡图寻亲",
  "diggCount": 9755,
  "commentCount": 1044,
  "shareCount": 551,
  "collectCount": 289,
  "admireCount": 12,
  "comments": [
    {
      "cid": "7045200000000000000",
      "text": "希望早日找到家人",
      "createTime": "2021-12-25T02:11:03+00:00",
      "diggCount": 88,
      "replyCount": 3,
      "authorName": "路人甲",
      "authorSecUserId": "MS4wLjABAAAA...",
      "authorUid": "1234567890",
      "ipLabel": "四川"
    }
  ]
}
```

The `comments` array is included only when **Include comments** (`includeComments`) is enabled — top-level comments only, up to `maxCommentsPerVideo` per video (newest first). Each comment carries its text, author, like count, reply count, and IP region. Enabling it adds paginated requests per video, so runs are slower and more billable.

`downloadedFileUrl` is included only when **Save media file to storage** (`downloadVideos`) is enabled — it's a permanent, non-expiring link to the media file saved in the run's key-value store. Set `mediaFormat` to `audio` to save the post's MP3 audio/music track instead of the MP4 (this is the sound attached to the post, not the video's own audio track). The direct CDN links (`videoUrlNoWatermark`, `videoMirrors`) are always returned as a fallback.

### Search Douyin by keyword

Instead of (or alongside) URLs, provide **search keywords** to discover videos by search term. Each keyword returns one row per matching video — the same rich output shape as above, plus a `searchKeyword` field telling you which term surfaced it. Duplicates across keywords are removed automatically, so a video is only returned (and billed) once.

> **⚠️ Keyword search needs a Douyin login cookie.** Unlike video URLs, profile URLs, and comments (which work with no login), Douyin gates its search endpoint behind login. Paste a logged-in cookie into the **Douyin cookie** (`douyinCookie`) field to enable search. **How to get it:** log in at [douyin.com](https://www.douyin.com), open DevTools → Network, click any `douyin.com` request, and copy the full `Cookie` request header. Cookies expire, so refresh if search starts returning a login error.

```json
{
  "searchKeywords": ["美食", "家常菜"],
  "maxResultsPerKeyword": 100,
  "searchSort": "most_liked",
  "searchPublishTime": "one_week",
  "searchDuration": "under_1m",
  "douyinCookie": "ttwid=...; sessionid=...; msToken=...; (your full logged-in cookie)"
}
```

| Option | Values | Description |
|--------|--------|-------------|
| `searchKeywords` | list of strings | Chinese, English, hashtags (`#美食`), brand/person names, and emoji all work. Each is searched independently. |
| `maxResultsPerKeyword` | `1`–`2000` (default `50`) | How many videos to fetch per keyword. Pages until reached or the keyword is exhausted. One row = one billable result. |
| `searchSort` | `general` / `most_liked` / `latest` | Relevance (default), most-liked first, or newest first. |
| `searchPublishTime` | `unlimited` / `one_day` / `one_week` / `half_year` | Restrict to videos published in this window. |
| `searchDuration` | `unlimited` / `under_1m` / `one_to_five` / `over_5m` | Restrict by video length. |
| `douyinCookie` | string | **Required for search.** A logged-in Douyin cookie (see note above). Not needed for URL/profile scraping. |

All filters stack server-side — e.g. `most_liked` + `one_week` + `under_1m` returns the most-liked sub-1-minute videos from the last 7 days. Narrow combinations return fewer results.

> **Note:** search rows include full engagement stats out of the box. The detail-only `seoOcrContent` (Douyin's OCR of on-screen text) is populated for search rows only when **Include engagement stats & OCR text** (`includeStats`) is enabled, which costs one extra request per video.

### Popular use cases

- **Content creators & marketers** — download Douyin videos without watermark to repurpose or archive.
- **Trend & influencer research** — track what's performing across creators and topics.
- **Competitor analysis** — monitor rival brands' Douyin content and engagement.
- **Datasets for AI/ML** — collect video, caption, and metadata at scale.
- **Social media analytics** — measure reach and engagement over time.

### Frequently asked questions

**Can I download Douyin videos without a watermark?**
Yes. Every result includes a direct no-watermark MP4 URL. The actor also saves the file to your storage automatically — **Save media file to storage** is on by default; turn it off if you only want the URLs.

**Do I need a Douyin account or login?**
No. This scraper works without any account, password, or cookies.

**Can I scrape all videos from a Douyin user?**
Yes. Add the profile URL and set how many videos to fetch. Videos are returned newest first.

**Can I search Douyin by keyword instead of pasting URLs?**
Yes. Add one or more terms to **Search keywords** and the actor returns one row per matching video, with sort, publish-window, and duration filters. Chinese, English, hashtags, and emoji all work, and duplicates across keywords are removed automatically. Note that keyword search requires a logged-in Douyin cookie in the **Douyin cookie** field (see the search section above) — URL and profile scraping do not.

**Why does search need a cookie but video/profile scraping doesn't?**
Douyin allows anonymous access to individual videos, profiles, and comments, but requires login for its keyword-search endpoint (it returns "请先登录" — "please log in" — otherwise). Paste a logged-in cookie into `douyinCookie` to enable search. Everything else keeps working with no login.

**Does it work with Douyin share links?**
Yes — both full `douyin.com/video/...` URLs and short `v.douyin.com/...` share links are supported.

**What formats can I export?**
JSON, CSV, and Excel, or pull data programmatically through the Apify API.

**Does it capture photo (slideshow) posts?**
Yes. Image posts return their picture URLs alongside the usual metadata.

### Integrations

Connect this Douyin scraper to your workflow with the **Apify API**, or automate it with **Make**, **Zapier**, **n8n**, Google Sheets, Slack, and more. Schedule runs to keep your Douyin data fresh automatically.

### Is it legal to scrape Douyin?

This scraper collects **publicly available** data only. You are responsible for how you use the data — respect Douyin's terms, applicable laws (such as GDPR), and copyright. Do not use scraped content in ways that infringe creators' rights.

# Actor input Schema

## `startUrls` (type: `array`):

Douyin video URLs or share links (e.g. https://www.douyin.com/video/123... or https://v.douyin.com/xxxx/). Each produces one row with the no-watermark MP4 URL.

## `profileUrls` (type: `array`):

Douyin user profile URLs (https://www.douyin.com/user/MS4wLjABAAAA...). The actor lists each user's posts, newest first.

## `searchKeywords` (type: `array`):

Search Douyin by keyword and get one row per matching video. Chinese, English, hashtags (e.g. #美食), brand or person names, and emoji all work. Each keyword is searched independently; duplicates across keywords are removed automatically so a video is only returned (and billed) once.

## `maxResultsPerKeyword` (type: `integer`):

Upper bound on videos fetched per keyword. The actor pages through results until it reaches this number or the keyword runs out of new videos. One row = one billable result, so set this to what you actually need.

## `searchSort` (type: `string`):

How Douyin orders search results. 'Relevance' is the standard search ranking.

## `searchPublishTime` (type: `string`):

Limit search results to videos published within this window.

## `searchDuration` (type: `string`):

Limit search results to videos in this length range.

## `douyinCookie` (type: `string`):

Douyin gates keyword search behind login, so a logged-in cookie is required to use <b>Search keywords</b>. Video URLs, profile URLs, and comments do <b>not</b> need it and keep working without a cookie.<br><br><b>How to get it:</b> log in at <a href="https://www.douyin.com" target="_blank">douyin.com</a> in your browser, open DevTools → Network, click any request to douyin.com, and copy the full <code>Cookie</code> request header. Paste it here.<br><br>Leave empty if you're only scraping URLs. A cookie set here overrides the actor's <code>DOUYIN\_COOKIE</code> environment secret if one is configured.

## `maxVideosPerProfile` (type: `integer`):

Upper bound on posts fetched per profile URL.

## `includeStats` (type: `boolean`):

Profile listings don't return like/comment/share/collect counts. Enable to fetch them per video — one extra request each (slower, more billable events). Search results already include these counts, but enabling this also fills in the detail-only `seoOcrContent` (Douyin's OCR of on-screen text) for search rows. Has no effect on individual Video URLs, which already return full detail.

## `includeComments` (type: `boolean`):

Fetch each video's top-level comments (text, author, like count, reply count, IP region) and attach them to the row as a `comments` array. Adds paginated requests per video — slower and more billable. Applies to both Video URLs and Profile URLs.

## `maxCommentsPerVideo` (type: `integer`):

Upper bound on top-level comments fetched per video when 'Include comments' is on. Fetched newest-first in pages of 20.

## `downloadVideos` (type: `boolean`):

Download each item's media file into the run's key-value store and expose it in the dataset as `downloadedFileUrl` — a permanent, non-expiring link you can use instead of the signed douyinvod.com CDN URLs, which expire after a while. On by default. Turn off to skip the download and rely only on the direct CDN URLs (videoUrlNoWatermark, videoMirrors), which are always returned regardless.

## `mediaFormat` (type: `string`):

Which file to save when 'Save media file to storage' is on. 'Video' stores the no-watermark MP4 (<awemeId>.mp4). 'Post audio / music track' stores the sound attached to the post as an MP3 (<awemeId>.mp3) — this is the post's music/original-sound resource, NOT a demux of the video's own audio track, so for videos set to a licensed song you get that song, not the in-video audio.

## `failOnDownloadError` (type: `boolean`):

Only applies when 'Save media file to storage' is on. Douyin's CDN links fail intermittently; the actor already retries across mirror URLs. If a file still can't be stored after retries, this marks the whole run as FAILED instead of finishing green — so a missing file is never reported as success. The dataset row is still written with the direct CDN URLs (videoUrlNoWatermark, videoMirrors) and a `downloadError` field. Turn off to keep the run green and just flag failures per row.

## `proxyConfiguration` (type: `object`):

Optional. Not required for correctness (the signed API works direct), but helps spread requests and avoid rate limits at scale.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.douyin.com/video/7045159024525905183"
  ],
  "maxResultsPerKeyword": 50,
  "searchSort": "general",
  "searchPublishTime": "unlimited",
  "searchDuration": "unlimited",
  "maxVideosPerProfile": 20,
  "includeStats": false,
  "includeComments": false,
  "maxCommentsPerVideo": 20,
  "downloadVideos": true,
  "mediaFormat": "video",
  "failOnDownloadError": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.douyin.com/video/7045159024525905183"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hgservices/douyin-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": ["https://www.douyin.com/video/7045159024525905183"] }

# Run the Actor and wait for it to finish
run = client.actor("hgservices/douyin-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.douyin.com/video/7045159024525905183"
  ]
}' |
apify call hgservices/douyin-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=hgservices/douyin-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Douyin Video Downloader & Scraper - No Watermark",
        "description": "Download Douyin (抖音) videos without watermark and scrape profiles: captions, likes, comments, views, music & hashtags. Export to JSON, CSV, or Excel. No login, no code.",
        "version": "0.1",
        "x-build-id": "4FmTQOFym5Ne7YM3j"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/hgservices~douyin-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-hgservices-douyin-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/hgservices~douyin-scraper/runs": {
            "post": {
                "operationId": "runs-sync-hgservices-douyin-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/hgservices~douyin-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-hgservices-douyin-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "startUrls": {
                        "title": "Video URLs",
                        "type": "array",
                        "description": "Douyin video URLs or share links (e.g. https://www.douyin.com/video/123... or https://v.douyin.com/xxxx/). Each produces one row with the no-watermark MP4 URL.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "profileUrls": {
                        "title": "Profile URLs",
                        "type": "array",
                        "description": "Douyin user profile URLs (https://www.douyin.com/user/MS4wLjABAAAA...). The actor lists each user's posts, newest first.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searchKeywords": {
                        "title": "🔎 Search keywords",
                        "type": "array",
                        "description": "Search Douyin by keyword and get one row per matching video. Chinese, English, hashtags (e.g. #美食), brand or person names, and emoji all work. Each keyword is searched independently; duplicates across keywords are removed automatically so a video is only returned (and billed) once.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxResultsPerKeyword": {
                        "title": "Max results per keyword",
                        "minimum": 1,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "Upper bound on videos fetched per keyword. The actor pages through results until it reaches this number or the keyword runs out of new videos. One row = one billable result, so set this to what you actually need.",
                        "default": 50
                    },
                    "searchSort": {
                        "title": "📊 Sort order",
                        "enum": [
                            "general",
                            "most_liked",
                            "latest"
                        ],
                        "type": "string",
                        "description": "How Douyin orders search results. 'Relevance' is the standard search ranking.",
                        "default": "general"
                    },
                    "searchPublishTime": {
                        "title": "📅 Published in",
                        "enum": [
                            "unlimited",
                            "one_day",
                            "one_week",
                            "half_year"
                        ],
                        "type": "string",
                        "description": "Limit search results to videos published within this window.",
                        "default": "unlimited"
                    },
                    "searchDuration": {
                        "title": "⏱️ Video duration",
                        "enum": [
                            "unlimited",
                            "under_1m",
                            "one_to_five",
                            "over_5m"
                        ],
                        "type": "string",
                        "description": "Limit search results to videos in this length range.",
                        "default": "unlimited"
                    },
                    "douyinCookie": {
                        "title": "🔑 Douyin cookie (required for keyword search)",
                        "type": "string",
                        "description": "Douyin gates keyword search behind login, so a logged-in cookie is required to use <b>Search keywords</b>. Video URLs, profile URLs, and comments do <b>not</b> need it and keep working without a cookie.<br><br><b>How to get it:</b> log in at <a href=\"https://www.douyin.com\" target=\"_blank\">douyin.com</a> in your browser, open DevTools → Network, click any request to douyin.com, and copy the full <code>Cookie</code> request header. Paste it here.<br><br>Leave empty if you're only scraping URLs. A cookie set here overrides the actor's <code>DOUYIN_COOKIE</code> environment secret if one is configured."
                    },
                    "maxVideosPerProfile": {
                        "title": "Max videos per profile",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "Upper bound on posts fetched per profile URL.",
                        "default": 20
                    },
                    "includeStats": {
                        "title": "Include engagement stats & OCR text (extra detail fetch)",
                        "type": "boolean",
                        "description": "Profile listings don't return like/comment/share/collect counts. Enable to fetch them per video — one extra request each (slower, more billable events). Search results already include these counts, but enabling this also fills in the detail-only `seoOcrContent` (Douyin's OCR of on-screen text) for search rows. Has no effect on individual Video URLs, which already return full detail.",
                        "default": false
                    },
                    "includeComments": {
                        "title": "Include comments",
                        "type": "boolean",
                        "description": "Fetch each video's top-level comments (text, author, like count, reply count, IP region) and attach them to the row as a `comments` array. Adds paginated requests per video — slower and more billable. Applies to both Video URLs and Profile URLs.",
                        "default": false
                    },
                    "maxCommentsPerVideo": {
                        "title": "Max comments per video",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Upper bound on top-level comments fetched per video when 'Include comments' is on. Fetched newest-first in pages of 20.",
                        "default": 20
                    },
                    "downloadVideos": {
                        "title": "Save media file to storage",
                        "type": "boolean",
                        "description": "Download each item's media file into the run's key-value store and expose it in the dataset as `downloadedFileUrl` — a permanent, non-expiring link you can use instead of the signed douyinvod.com CDN URLs, which expire after a while. On by default. Turn off to skip the download and rely only on the direct CDN URLs (videoUrlNoWatermark, videoMirrors), which are always returned regardless.",
                        "default": true
                    },
                    "mediaFormat": {
                        "title": "Media file to download",
                        "enum": [
                            "video",
                            "audio"
                        ],
                        "type": "string",
                        "description": "Which file to save when 'Save media file to storage' is on. 'Video' stores the no-watermark MP4 (<awemeId>.mp4). 'Post audio / music track' stores the sound attached to the post as an MP3 (<awemeId>.mp3) — this is the post's music/original-sound resource, NOT a demux of the video's own audio track, so for videos set to a licensed song you get that song, not the in-video audio.",
                        "default": "video"
                    },
                    "failOnDownloadError": {
                        "title": "Fail the run if a media file can't be saved",
                        "type": "boolean",
                        "description": "Only applies when 'Save media file to storage' is on. Douyin's CDN links fail intermittently; the actor already retries across mirror URLs. If a file still can't be stored after retries, this marks the whole run as FAILED instead of finishing green — so a missing file is never reported as success. The dataset row is still written with the direct CDN URLs (videoUrlNoWatermark, videoMirrors) and a `downloadError` field. Turn off to keep the run green and just flag failures per row.",
                        "default": true
                    },
                    "proxyConfiguration": {
                        "title": "Proxy",
                        "type": "object",
                        "description": "Optional. Not required for correctness (the signed API works direct), but helps spread requests and avoid rate limits at scale.",
                        "default": {
                            "useApifyProxy": false
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
