# Douyin 抖音用户主页 Profile Scraper - Followers & Likes (`lanternlab318/douyin-profile`) Actor

抖音用户主页 of any public Douyin (抖音) account: Douyin ID, nickname, bio, followers, following, total likes, post count and IP region, as exact numbers rather than the rounded 万 the page shows. No login. An account that could not be read says so with the reason, never as an empty account.

- **URL**: https://apify.com/lanternlab318/douyin-profile.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.99 / 1,000 results

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 抖音用户主页 Profile Scraper

Read the public profile (抖音用户主页) of any Douyin (抖音) account: Douyin ID, nickname, bio, followers, following, total likes received, the number of posts and the IP region Douyin shows - exact numbers, not Douyin's rounded `4.8万`. Paste profile links or `sec_uid` values; no login, no cookies, no account of your own. **$4.99 per 1,000 profiles, plus $0.005 per run.** See [Pricing](#pricing).

Input:

```json
{
  "profileUrls": ["https://www.douyin.com/user/MS4wLjABAAAAv7iSuuXDJGDvn02WrOdGwRKUPahnT9VWCM-vBsDmj04"]
}
```

Output - exactly one row per input line (`recordType: "profile"`). Sample from a real run; nickname partially masked, bio and ID shortened:

```json
{
  "recordType": "profile",
  "platform": "douyin",
  "dataSource": "guest_web",
  "secUid": "MS4wLjABAAAA…",
  "uniqueId": "gdthyang",
  "nickname": "挪威***",
  "signature": "共创伙伴：@雅苒国际…",
  "followers": 5480,
  "following": 1289,
  "totalLikes": 3783,
  "videoCount": 23,
  "ipLocation": "广东",
  "verified": false,
  "profileUrl": "https://www.douyin.com/user/MS4wLjABAAAA…",
  "profileStatus": "available",
  "accessMode": "direct",
  "collectionStatus": "complete",
  "partial": false,
  "partialReason": null,
  "elapsedMs": 27954,
  "collectedAt": "2026-10-17T03:48:56+00:00"
}
```

Limits:

- **Public data only.** The Actor reads what Douyin shows a signed-out visitor and never logs in. No posts (Douyin serves no account timeline to a visitor), no follower or following lists, no email or phone - only the public profile fields.
- **Every row says how complete it is.** An account Douyin would not describe - gone, renamed away, withheld - is reported as such with the reason, never as an account with nothing on it.
- **Your limits stop collection and appear in the row**: the `maxUpstreamRequests` request ceiling, the residential-fallback budget (`maxFallbackProfiles`) and the maximum charge per run (see [Pricing](#pricing)).

### What it does

- Reads each account's profile the way Douyin's own profile page shows it, and writes exactly one row per input line - even for a line that could not be read.
- Costs one Douyin request per account. A run of 100 accounts is one browser session and 100 requests.
- Marks a row `partial` with a reason when the profile was not read: your request limit, your cost limit, or a refusal by Douyin.
- Runs without a proxy by default. If Douyin refuses the request for an account, that one account is retried through an Apify residential proxy exiting in China (limited per run, can be turned off). An account Douyin *does* answer about - gone, renamed away, withheld - is never retried, because no exit changes that answer.
- Stops minting fresh visitor sessions once a run has seen two accounts refused and none answered: Douyin withholds from every visitor, not from one session, so a third page load would only buy the same answer. Each account is still asked once, so each keeps its own evidence.
- Writes a `RUN_SUMMARY` record with the totals, the requests sent against the ceiling in force, and why the run stopped.

### What it does not do

- **No posts.** Douyin serves no account timeline to a signed-out visitor, so this Actor does not pretend to collect one. See [What Douyin serves a visitor](#what-douyin-serves-a-visitor). For posts, the [Douyin keyword search Actor](https://apify.com/lanternlab318/douyin-search) reads what signed-out search returns.
- **No account search.** Bring profile links or `sec_uid` values. A numeric user ID is not enough: Douyin's web API only answers by `sec_uid`, which every profile link contains.
- **No follower or following lists, no liked posts, no live streams, no login-only content.**
- **No private data.** Only the public profile fields Douyin's own page shows. No email, no phone, no location beyond the IP region Douyin displays itself.
- No video downloads, no comments (use the [Douyin comments Actor](https://apify.com/lanternlab318/douyin-comments)), no scheduling built in.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `profileUrls` | - | Profile links (`https://www.douyin.com/user/MS4w...`), share links (`https://v.douyin.com/...`), links carrying `sec_uid=...`, or bare `sec_uid` values. |
| `maxUpstreamRequests` | worked out from your input | Hard ceiling on Douyin requests for the whole run, retries included. Leave empty and the Actor derives one, so a run is never unbounded. |
| `proxyConfiguration` | no proxy | Standard Apify proxy setting for all requests. |
| `residentialFallback` | true | Retry a refused account through a residential proxy in China. |
| `maxFallbackProfiles` | 3 | Accounts per run that may use that fallback, at most 10. 0 turns it off. |
| `requestDelaySecs` | 1 | Minimum pause between two requests from one visitor session. |
| `maxIdentityRotations` | 6 | Fresh visitor sessions a run may mint after a refusal. |
| `msTokenWaitSecs` | 20 | How long a new visitor page may take to receive its security token. |

### Output

One row per input line in the default dataset, plus a `RUN_SUMMARY` record in the key-value store with the run totals.

| Field | Meaning |
| --- | --- |
| `secUid` | Douyin's permanent account ID - the `MS4w...` value in every profile link. |
| `uniqueId` | The account's Douyin ID (抖音号), or its short ID when it never set one. |
| `nickname`, `signature` | Public display name and bio. |
| `followers`, `following` | Followers, and accounts this account follows - exact, not rounded. |
| `totalLikes` | Total likes received across the account's posts (获赞). |
| `videoCount` | Posts Douyin says the account has (作品). The posts themselves are not collected. |
| `ipLocation` | IP region Douyin shows (for example 内蒙古), without its `IP属地：` label. |
| `verified` | Whether Douyin shows a verification badge. |
| `profileUrl`, `input` | The canonical profile link, and your input line. |
| `profileStatus` | `available`, `unavailable`, `unknown` or `invalid_input`. See below. |
| `accessMode` | `direct`, `custom_proxy` or `residential_fallback`. |
| `collectedAt`, `elapsedMs` | When the row was collected (UTC, ISO 8601), and time spent on the account. |
| `collectionStatus`, `partial`, `partialReason` | How complete the row is, and why not. |

A row never carries an invented value: a field Douyin stated nothing about is `null`, not `0` and not an empty string.

#### Profile status

| `profileStatus` | Meaning |
| --- | --- |
| `available` | The profile was read. |
| `unavailable` | Douyin answered but would not describe the account - gone, renamed away, or withheld. Not "an empty account". |
| `unknown` | The request was refused, or the run stopped before sending it. |
| `invalid_input` | The input line carried no `sec_uid`. |

#### What `partial` means

`partial` is true whenever `collectionStatus` is not `complete`, and `partialReason` says why:

| Reason | Meaning |
| --- | --- |
| `cap:maxUpstreamRequests=N` | The run's request ceiling stopped it - yours, or the one derived from your input. `RUN_SUMMARY.upstreamCapSource` says which. |
| `cap:maxTotalChargeUsd` | Your **Maximum cost per run** was reached; rows past it are not stored. |
| `blocked:<accessMode>:...` | Douyin refused the request on that exit, after fresh visitor sessions. The text keeps the classification and Douyin's own status code. |
| `profile:no_user:...` | Douyin answered about the account but would not describe it. |
| `fallback_cap:maxFallbackProfiles=N` | The account was refused and the run had used up its residential retries. |
| `input:no_sec_uid` | No `sec_uid` in that input line. |

`collectionStatus` is `complete` when the profile was read, `blocked` when Douyin refused the request, and `not_collected` when nothing could be read or nothing was attempted.

### What Douyin serves a visitor

Measured 2026-10-15, four accounts (9 to 213 posts), two network exits (a data-centre IP and Apify residential IPs exiting in China), desktop and mobile clients:

| Asked for | Answer |
| --- | --- |
| Profile (`/aweme/v1/web/user/profile/other/`) | HTTP 200 with the full profile, every time. |
| Timeline (`/aweme/v1/web/aweme/post/`) | HTTP 200 with an **empty body**, every time. |
| Timeline, old share API (`/web/api/v2/aweme/post/`) | HTTP 200 with an empty body, on both `douyin.com` and `iesdouyin.com`. |
| Douyin's own profile page | Renders the profile header and the post count; the 作品 grid stays empty and no post appears anywhere in the page. |

There is no captcha and no error message for the timeline - Douyin simply answers nothing. That is why this Actor collects the profile and nothing else: a signed-out visitor cannot see an account's posts, so an Actor that promised them would be promising a login we do not use.

### Pricing

Pay per event: **$4.99 per 1,000 profiles, plus $0.005 for each run.** Apify platform usage, including the browser session and the residential fallback, is included; these two events are the whole price.

| Event | Price | Charged for |
| --- | --- | --- |
| `result` | $0.00499 ($4.99 per 1,000) | Each row that carries a profile. |
| `run-start` | $0.005, once per run | The signed-out browser session every run opens before it reads any account. |

A row that only reports a failure - an input line with no `sec_uid`, an account Douyin would not describe, an account the run never reached - is written **free**. A run that reads no account at all still pays the $0.005 session.

The session is a flat fee per run, not per account, so a batch costs less per profile than many small runs: 10 accounts cost $0.055 ($0.0055 each), 100 accounts cost $0.504 ($0.0050 each), 1,000 accounts cost $4.995.

To cap a run, set **Maximum cost per run** in the run options. When the cap is reached the Actor stores no further rows, stops before the next account, marks what it cut short `partial` with `cap:maxTotalChargeUsd`, and records it in `RUN_SUMMARY`.

### Resources

- Runs with **1 GB memory** by default; 1-8 GB can be chosen in the run options. A visitor session is minted in a real browser, which needs 1 GB.
- A run opens a visitor session first (about 20-40 seconds), then takes a couple of seconds per account.

### Notes

- Respect Douyin's terms and local law, and only collect what you need. Nicknames, bios and IP regions are personal data in many jurisdictions.
- Douyin changes its web client often. `RUN_SUMMARY` reports request outcomes per endpoint, so a change shows up there first.

# Actor input Schema

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

Public Douyin accounts, one per line: a profile link (https://www.douyin.com/user/MS4w...), a share link (https://v.douyin.com/...), a link carrying sec_uid=..., or the bare sec_uid. A numeric user ID is not enough - Douyin's web API only answers by sec_uid, which every profile link contains. Account search is not supported.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on requests sent to Douyin in this run, retries included. Leave it empty and the Actor works one out from the number of accounts you gave it, so a run is never unbounded; 0 sends no request at all. RUN_SUMMARY reports which ceiling was in force.

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

Proxy for all requests. Off by default: Douyin answers guests without a proxy.

## `residentialFallback` (type: `boolean`):

When Douyin refuses the request for an account (captcha, empty answer, block status), retry that account once through an Apify residential proxy exiting in China. Residential traffic costs more than direct requests. An account Douyin does answer about - gone, renamed away, withheld - is never retried: no exit changes that answer.

## `maxFallbackProfiles` (type: `integer`):

Upper limit per run on accounts moved to the residential proxy. 0 turns the fallback off.

## `requestDelaySecs` (type: `number`):

Minimum pause between two requests from the same guest identity.

## `maxIdentityRotations` (type: `integer`):

How many times a refused request may be retried on a freshly minted guest identity before the account is reported as blocked.

## `msTokenWaitSecs` (type: `integer`):

How long a new guest page may take to receive its security token (msToken) before requests start. Longer waits make requests more likely to be answered.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.douyin.com/user/MS4wLjABAAAAv7iSuuXDJGDvn02WrOdGwRKUPahnT9VWCM-vBsDmj04"
  ],
  "maxUpstreamRequests": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "residentialFallback": true,
  "maxFallbackProfiles": 3,
  "requestDelaySecs": 1,
  "maxIdentityRotations": 6,
  "msTokenWaitSecs": 20
}
```

# Actor output Schema

## `profiles` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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 = {
    "profileUrls": [
        "https://www.douyin.com/user/MS4wLjABAAAAv7iSuuXDJGDvn02WrOdGwRKUPahnT9VWCM-vBsDmj04"
    ],
    "maxUpstreamRequests": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/douyin-profile").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 = {
    "profileUrls": ["https://www.douyin.com/user/MS4wLjABAAAAv7iSuuXDJGDvn02WrOdGwRKUPahnT9VWCM-vBsDmj04"],
    "maxUpstreamRequests": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/douyin-profile").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 '{
  "profileUrls": [
    "https://www.douyin.com/user/MS4wLjABAAAAv7iSuuXDJGDvn02WrOdGwRKUPahnT9VWCM-vBsDmj04"
  ],
  "maxUpstreamRequests": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call lanternlab318/douyin-profile --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lanternlab318/douyin-profile"
        }
    }
}
```

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/7zacrQYmerf7UTFt7/builds/CJemeB8bPfNQA2far/openapi.json
