# Douyin User Post Videos (`opspilot.cc/douyin-user-post-videos`) Actor

🎵 Douyin User Post Videos | An account's posts, with the counters Douyin actually gives Give it a sec_uid, profile URL or short_id and get every post as a row: description, likes, comments, shares, collects, duration, music, publish time and link — plus a per-account summary with median likes, the…

- **URL**: https://apify.com/opspilot.cc/douyin-user-post-videos.md
- **Developed by:** [wang wei](https://apify.com/opspilot.cc) (community)
- **Stats:** 38 total users, 35 monthly users, 98.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.05 / actor start

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 🎵 Douyin User Post Videos

> An account's posts, with the counters Douyin actually gives — and a plain statement of the one it does not.

### ✨ What you get

One dataset row per post:

| Field | What it is |
|---|---|
| `description` / `hasDescription` | the post text; `hasDescription: false` marks a caption-less post, kept not dropped |
| `diggCount` / `commentCount` / `shareCount` / `collectCount` | the four counters Douyin exposes, kept separate |
| `recommendCount` | the For-You recommend count — the closest thing to reach this source offers |
| `viewCount` / `viewsAvailable` | **always `null` / `false`** — Douyin does not expose view counts here |
| `durationSeconds` / `durationMs` | length converted from Douyin's milliseconds |
| `isVideo` / `imageCount` | video vs image post, so the two formats can be compared |
| `isAd` | promoted posts, flagged rather than mixed into organic reach |
| `musicTitle` | the audio used, including original sounds |
| `createdAt` / `shareUrl` | publish time and a working link |
| `secUid` / `authorName` / `authorUid` | the account |

The run summary gives, per account: posts collected, the account's own post total **when the lookup provides one** plus your `coveragePct`, follower count when available, median and total likes, median comments and recommends, video-vs-image split, ad-post count, the top 5 posts by likes, and a `viewsNote` explaining why there are no view counts.

### 💼 Use cases

- **Deciding whether an account's organic reach is worth buying** — sort by `diggCount` and filter `isAd = false`. A promoted post's engagement legitimately includes paid boosting, so mixing ads into an organic comparison inflates the account you are about to pay.
- **Reading reach at all on Douyin** — `viewCount` is always `null` with `viewsAvailable: false`, because this source does not expose view counts. `recommendCount` is the closest available proxy: it is the For-You recommendation count, not views, but it moves with distribution.
- **Deciding video vs image for the next post** — `isVideo`, `imageCount` and `durationSeconds` let you compare the two formats on the same account instead of assuming the format you see most is the one that works.
- **Running a competitor content audit** — median and total likes, median comments and recommends, the video-vs-image split and the top 5 posts by likes give you a per-account baseline you can re-run monthly and diff.

### ✅ Who this is for

- **Douyin (抖音) media buyers and creator-vetting teams** who need a per-account engagement baseline — like count, comment count, share and save — before opening a negotiation.
- **Content and growth teams** auditing an account's recent output to decide what format and length to brief next.
- **MCNs managing several accounts** who need the same fields per account in one run, with posts deduplicated by aweme id so a repeated cursor cannot inflate the count.
- **Overseas brands and sellers** who can read Chinese captions but cannot judge Chinese account performance from the profile page alone.

### ❌ Who this is NOT for

- **Anything that needs Douyin view counts (播放量)** — `viewCount` is permanently `null` and `viewsAvailable` is `false`, because the field arrives as 0 regardless of a post's real popularity. A 0 here would be a false statement, so nothing in this actor can tell you how many people watched. Use `recommendCount` as the proxy and say so when you report it.
- **A profile-level verdict on account size** — the `short_id` profile lookup returns only a minimal profile, so `reportedTotalPosts` and follower count can be `null`. That is a light response, not missing data; `profileFieldsAvailable` records what came back, and passing a `sec_uid` directly unlocks the richer counters.
- **Reading `hasDescription: false` as a scraping miss** — those posts genuinely have no caption. They are kept because their counters are still data; dropping them would shift your averages.
- **An empty result as proof the account is inactive** — a wrong or expired `sec_uid` returns an empty list with `has_more = 0` rather than an error. Read `warnings` before concluding anything about the account.
- **Weibo, Kuaishou or Xiaohongshu accounts** — the `sec_uid` input is Douyin-specific. Cross-platform creator research needs the matching actor for that platform.

### 🚀 Quick start

1. Open the Actor and press **Start** (defaults to one public account).
2. Paste accounts — a `sec_uid` (`MS4wLjABAAAA…`), a `douyin.com/user/<sec_uid>` profile URL, or a numeric `short_id` that this Actor resolves for you.
3. Sort by `diggCount` to see what worked; filter `isAd = false` for organic only.

### 📥 Input parameters

| Parameter | Type | Default | What it does |
|---|---|---|---|
| `accounts` | list | one public account | sec_uids, profile URLs or short_ids, one per line, up to 20 per run. |
| `sort` | select | `newest` | `newest` or `popular` (most liked). |
| `maxPostsPerAccount` | number | `60` | 1–200 posts per account. Cost scales linearly with pages × accounts. |

### 📤 Output

- **Dataset** — one row per post (or a notice when nothing was collected)
- **SUMMARY** — per-account stats, coverage, top posts, the view-count note and warnings

### ⚙️ How it works

1. Each input is reduced to a `sec_uid`. A short_id is resolved through the provider's profile lookup first, and the resolved nickname is carried onto every row.
2. Posts are paged with Douyin's `max_cursor`, de-duplicated by aweme id, and stopped as soon as a page adds nothing new — a repeated cursor cannot spin the run.
3. All engagement lives inside `statistics`; nothing is read from the top level.
4. View counts stay `null` and `viewsAvailable` stays `false`, because this source does not expose them. Reporting `0` would claim "nobody watched this", which is a different statement.

### 💰 Billing

Pay per result — you are charged for the post rows this Actor actually returns, not for the run. Accounts that yield nothing are not charged. Cost scales linearly with pages × accounts. Free tier: 5 runs/day.

### ❓ FAQ

**Why is `viewCount` always null?**
Douyin does not expose view counts through this data source — the field arrives as 0 regardless of the post's real popularity. A 0 here would be a false statement, so this Actor reports `null` plus `viewsAvailable: false` and uses `recommendCount` as the closest available reach signal.

**What is `recommendCount`?**
The For-You recommendation count. It is not views, but it moves with distribution, so it is the most useful reach proxy available here.

**Why are some posts `hasDescription: false`?**
Those posts have no caption at all. They are kept, because the engagement counters are still data and dropping them would shift your averages.

**What does `isAd` catch?**
Promoted posts. They are flagged so you can exclude them; they are not removed, because a post's total engagement legitimately includes paid boosting.

**Why is `reportedTotalPosts` sometimes null?**
The short_id profile lookup returns only a minimal profile (id, sec_uid, nickname, avatar and a few flags) — no follower count and no post total. Rather than filling those from a heavier leg that gets blocked, they stay `null` and `profileFieldsAvailable` records what the lookup actually returned. Supply a `sec_uid` directly if you need the counters the richer profile carries.

**An account returned no posts.**
Usually the `sec_uid` is wrong or expired: this source answers with an empty list and `has_more = 0` rather than an error, so an empty result is not proof an account is inactive. The reason is in `warnings`.

**How deep does the paging go?**
As far as `maxPostsPerAccount` allows, following Douyin's own cursor.

### 📚 Resources

- Douyin public post data (no login required)
- Apify free tier: 5 runs/day, then upgrade

# Changelog

This Actor's version history is a separate document: https://apify.com/opspilot.cc/douyin-user-post-videos/changelog.md

# Actor input Schema

## `accounts` (type: `array`):

One per line, up to 20 per run. A sec_uid looks like MS4wLjABAAAA…; a profile URL is douyin.com/user/\<sec_uid>; a short_id is a short numeric handle that this Actor resolves for you.

## `sort` (type: `string`):

Newest for what an account is publishing now, most liked for its best-known posts.

## `maxPostsPerAccount` (type: `integer`):

Cap on posts collected per account. Cost scales linearly with pages × accounts.

## Actor input object example

```json
{
  "accounts": [
    "MS4wLjABAAAAUW22yKkqlra1vrTebmrdhVWb2tOYW6jX7wElmeRERQ8"
  ],
  "sort": "newest",
  "maxPostsPerAccount": 60
}
```

# Actor output Schema

## `dataset` (type: `string`):

One row per post, or a single notice when nothing was collected.

## `summary` (type: `string`):

Per-account stats, coverage, top posts, a note on view counts and warnings.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("opspilot.cc/douyin-user-post-videos").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("opspilot.cc/douyin-user-post-videos").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{}' |
apify call opspilot.cc/douyin-user-post-videos --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,opspilot.cc/douyin-user-post-videos"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/x0cKMemGCfYlX4Bao/builds/WNzbxHOLs9EqmzMdi/openapi.json
