# Douyin Live Recorder - Real-Time Chat, Gifts & MP4 Capture (`zen-studio/douyin-live-recorder`) Actor

Extract real-time chats, gifts, likes, joins, follows, and viewer counts from Douyin (抖音) live broadcasts. Save the stream as MP4 segments. Polymorphic JSON dataset, one row per event. SD to Full HD recording. Free tier included.

- **URL**: https://apify.com/zen-studio/douyin-live-recorder.md
- **Developed by:** [Zen Studio](https://apify.com/zen-studio) (community)
- **Categories:** Videos, Social media, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.85 / 1,000 recording seconds

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/platform/actors/running/actors-in-store#pay-per-event

## 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 Live Recorder | Real-time Chat, Gift & Viewer Stream + MP4 Capture (2026)

<blockquote style="border-left:4px solid #4C945E;background:#F0FDF4;padding:12px 16px">
<span style="font-size:16px;font-weight:700;color:#1C1917">15+ event types streamed in real time, optional MP4 capture in SD–Full HD</span> <span style="font-size:15px;color:#57534E"> — chats, gifts, likes, joins, follows, viewer counts, room rankings, and stream control signals. One row per event. The most complete Douyin (抖音) live recorder on Apify.</span>
</blockquote>

![Douyin Live Recorder](https://iili.io/CdCcTEN.png)

<table>
<tr>
<td colspan="4" style="padding:10px 14px;background:#4C945E;border:none;border-radius:4px 4px 0 0">
<span style="color:#FAFAF9;font-size:14px;font-weight:700;letter-spacing:0.5px">Zen Studio China Suite</span>
<span style="color:#E8F5E9;font-size:13px">&nbsp;&nbsp;&bull;&nbsp;&nbsp;Profiles, posts, transcripts, and live broadcasts from 抖音 — plus search from 小红书</span>
</td>
</tr>
<tr>
<td style="padding:12px 16px;border:1px solid #E7E5E4;border-radius:0 0 0 4px;border-right:none;border-top:none;vertical-align:top;width:25%">
<img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/NWYsOG96fMDy8ycdf-actor-043Hpy7gVQvWCOivr-Ccer3fv46L-douyin-profile-scraper-logo.png" width="24" height="24" style="vertical-align:middle"> &nbsp;<a href="https://apify.com/zen-studio/douyin-profile-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:14px">Profile Scraper</a><br>
<span style="color:#78716C;font-size:12px">46 author fields + post lists</span>
</td>
<td style="padding:12px 16px;border:1px solid #E7E5E4;border-right:none;border-top:none;vertical-align:top;width:25%">
<img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/NWYsOG96fMDy8ycdf-actor-bSlgoeK7kpcbT73gF-ajdm9GD5in-douyin-profile-scraper-logo.png" width="24" height="24" style="vertical-align:middle"> &nbsp;<a href="https://apify.com/zen-studio/douyin-video-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:14px">Video Scraper</a><br>
<span style="color:#78716C;font-size:12px">Full video metadata by URL</span>
</td>
<td style="padding:12px 16px;border:1px solid #E7E5E4;border-right:none;border-top:none;vertical-align:top;width:25%">
<img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/NWYsOG96fMDy8ycdf-actor-3UkyCcaAQLuQPa2DB-nKbW8d285P-douyin-profile-scraper-logo.png" width="24" height="24" style="vertical-align:middle"> &nbsp;<a href="https://apify.com/zen-studio/douyin-transcripts-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:14px">Transcripts Scraper</a><br>
<span style="color:#78716C;font-size:12px">Subtitles + ASR + translation</span>
</td>
<td style="padding:12px 16px;border:1px solid #E7E5E4;border-radius:0 0 4px 0;border-top:none;vertical-align:top;width:25%">
<img src="https://apify-image-uploads-prod.s3.us-east-1.amazonaws.com/NWYsOG96fMDy8ycdf-actor-hO5NqsA6aC1bz3jra-MjB05eVFEt-xiaohongshu-search-scraper-logo-square.png" width="24" height="24" style="vertical-align:middle"> &nbsp;<a href="https://apify.com/zen-studio/rednote-search-scraper" style="color:#1C1917;text-decoration:none;font-weight:700;font-size:14px">RedNote Search</a><br>
<span style="color:#78716C;font-size:12px">500 Xiaohongshu notes / 30s</span>
</td>
</tr>
</table>

#### Copy to your AI assistant

````

zen-studio/douyin-live-recorder on Apify. Streams real-time chat/gift/like/join/viewer-count events from Douyin (抖音) live broadcasts as type-discriminated rows, with optional MP4 recording. Call ApifyClient("TOKEN").actor("zen-studio/douyin-live-recorder").call(run\_input={...}), then client.dataset(run\["defaultDatasetId"]).list\_items().items. liveUrl accepts a live.douyin.com URL, share link, profile URL, or "auto". Full spec: GET https://api.apify.com/v2/acts/zen-studio~douyin-live-recorder/builds/default (Bearer TOKEN) → inputSchema, actorDefinition.storages.dataset, readme. Token: https://console.apify.com/account/integrations

````

### Key Features

- **15+ event types in real time** — chat, gift, like, member join, follow, share, fansclub, emoji, viewer count, room stats, room ranking, room messages, stream adaptation, control signals
- **Optional MP4 recording** — SD1, SD2, HD1, or Full HD segments uploaded to the key-value store every 1–60 minutes
- **Recording-only mode** — set `captureEvents: false` to skip the event stream entirely and just save the MP4. You only pay for recording time
- **One row per event** — polymorphic dataset, `type` is the discriminator. Filter chats with `type = "chat"`, gifts with `type = "gift"`, etc.
- **Pick the events you want** — `excludeEvents` lets you skip event types upfront (drop `like` if you only need chats and gifts, for example). Wire-noise types are filtered internally
- **Six input formats** — paste a `live.douyin.com` URL, app share link, profile URL, UserSecID, numeric user id, or `auto` for a demo room
- **Reconnect-safe** — capped exponential backoff; transient WS drops are recovered transparently and reflected in the summary's `reconnect_count`
- **Free tier** — 5 lifetime runs, 5 minutes per run, recording disabled. No credit card required

### How to Record Douyin Live Broadcasts

#### Basic — pick a currently-live demo room

```json
{
  "liveUrl": "auto",
  "runTime": 5
}
````

#### Record a specific broadcast

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 60
}
```

#### Save the stream as MP4

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 30,
  "recordVideo": true,
  "videoQuality": "HD1",
  "uploadEvery": 5
}
```

#### Resolve a broadcaster by profile URL

```json
{
  "liveUrl": "https://www.douyin.com/user/MS4wLjABAAAAY4cTwy_eXrFLF9JiUjt4QHb3-6c2vKE-bI0Xy_T2WCw",
  "runTime": 60
}
```

### Input Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `liveUrl` | string | `"auto"` | A Douyin live broadcast: `live.douyin.com` URL, bare numeric web id, `v.douyin.com` share link, profile URL, UserSecID, or numeric user id. Use `auto` for a demo room. |
| `runTime` | integer | `0` | Minutes to stay connected. `0` = until the broadcaster ends the stream. Range `0–240`. |
| `captureEvents` | boolean | `true` | When off, the actor skips the chat / gift / like event stream entirely and only saves the MP4. Requires `recordVideo: true`. |
| `excludeEvents` | array | `[]` | Event types to drop from the dataset. Wire-noise types (`system`, `stream_adaptation`, `connection_state`) are filtered internally and never appear regardless of this setting. |
| `recordVideo` | boolean | `false` | When on, the stream is recorded as MP4 segments and uploaded to the run's key-value store. |
| `videoQuality` | enum | `"HD1"` | Recording quality: `SD1`, `SD2`, `HD1`, or `FULL_HD1`. Ignored when `recordVideo` is off. |
| `uploadEvery` | integer | `5` | Minutes per MP4 segment before rotation. Range `1–60`. Set to `60` (or match `runTime`) for one consolidated file. Ignored when `recordVideo` is off. |

### What Data Can You Extract from Douyin Live Broadcasts?

Every run produces a polymorphic dataset. The first row is `room_info`, the last row is `summary`, and everything in between is one row per live event.

#### Per-event row types

- **`chat`** — user nickname, fans-club name + level, pay-grade level, gender, full chat content
- **`gift`** — gift name, diamond count, combo count, sender + recipient nicknames, total / repeat counts
- **`like`** — sender, click count, room total
- **`member`** — viewer who just joined, member count, enter type
- **`social`** — follow / share actions, follow count
- **`viewer_count`** — current viewers, total users, popularity, anchor-side breakdown
- **`fansclub`** — fansclub event type and content
- **`emoji_chat`** — emoji id and default text
- **`room_stats`** — display labels (short / middle / long) and value
- **`room_rank`** — top contributors with rank position
- **`room_message`** — system top messages with biz scene
- **`stream_adaptation`** — resolution / aspect-ratio changes during the stream
- **`control`** — broadcaster status changes (status `3` = stream ended)

#### Output Example

```json
[
  {
    "type": "room_info",
    "room_id": "7644153789288680238",
    "web_rid": "457647640330",
    "broadcaster": { "nickname": "林悦" },
    "title": "我是林悦，音乐的悦",
    "user_count_text": "206",
    "raw_url": "https://live.douyin.com/457647640330"
  },
  {
    "type": "chat",
    "msg_id": "7644159790989972516",
    "room_id": "7644153789288680238",
    "web_rid": "457647640330",
    "user": {
      "user_id": "4215971040468057",
      "short_id": "33364754953",
      "sec_uid": "MS4wLjABAAAAJ7L2GblVdXfdUjKx8g76iSv25rT3_3eiro_DxOXcFFms54WHBidCTpLgAR0HYFJ-",
      "display_id": "12D798",
      "nickname": "你若成风",
      "gender": 1,
      "fans_club_name": "悦悦有",
      "fans_club_level": 13,
      "pay_grade_level": 32
    },
    "content": "我好像认识你！"
  },
  {
    "type": "gift",
    "msg_id": "7644159811234567890",
    "room_id": "7644153789288680238",
    "web_rid": "457647640330",
    "user": { "nickname": "热心观众", "pay_grade_level": 18 },
    "gift_id": "12345",
    "gift_name": "小心心",
    "gift_diamond_count": 1,
    "gift_describe": "送给主播一颗小心心",
    "combo_count": 5,
    "repeat_count": 5,
    "total_count": 5,
    "send_type": 1
  },
  {
    "type": "like",
    "user": { "nickname": "粉丝-A" },
    "count": 3,
    "total": 12847
  },
  {
    "type": "member",
    "user": { "nickname": "新观众" },
    "member_count": 2225,
    "enter_type": 1,
    "action": 1
  },
  {
    "type": "viewer_count",
    "current_viewers": 2225,
    "total_users": 48172,
    "popularity": 9831,
    "current_viewers_text": "2,225",
    "total_users_text": "48,172"
  },
  {
    "type": "room_stats",
    "display_short": "人气",
    "display_middle": "在线人气 2225",
    "display_long": "当前在线人气 2225",
    "display_value": 2225
  },
  {
    "type": "room_rank",
    "ranks": [
      { "rank": 1, "user": { "nickname": "榜一大哥" }, "score_text": "12.4w" },
      { "rank": 2, "user": { "nickname": "土豪二" }, "score_text": "8.1w" },
      { "rank": 3, "user": { "nickname": "粉丝三" }, "score_text": "5.6w" }
    ]
  },
  {
    "type": "summary",
    "room_id": "7644153789288680238",
    "web_rid": "457647640330",
    "capture_events": true,
    "duration_seconds": 3600.0,
    "produced_output": true,
    "connection_opened": true,
    "reconnect_count": 0,
    "total_events": 28828,
    "event_counts": {
      "chat": 8420,
      "like": 11230,
      "member": 7104,
      "gift": 982,
      "viewer_count": 612,
      "room_stats": 480
    },
    "recording_segments": 12,
    "recording_total_bytes": 1283456789,
    "recording_total_duration_s": 3597.4,
    "recording_stop_reason": "deadline_reached",
    "reason_for_exit": "runtime_cap",
    "record_video_requested": true,
    "record_video_effective": true,
    "video_quality": "HD1"
  }
]
```

#### Recording manifest (run OUTPUT)

The run's OUTPUT manifest is the canonical way to find the recordings. It always has the shape below — `recordings` is an empty array on runs that didn't capture any video:

```json
{
  "results": "https://api.apify.com/v2/datasets/.../items",
  "recordings": [
    {
      "key": "recording_376034101029_0001.mp4",
      "seq": 1,
      "started_at_ms": 1779796533135,
      "ended_at_ms": 1779796833135,
      "duration_s": 300.0,
      "bytes": 104857600,
      "quality": "HD1",
      "kv_url": "https://api.apify.com/v2/key-value-stores/.../records/recording_376034101029_0001.mp4"
    }
  ]
}
```

#### Did the run succeed? (`produced_output` and `reason_for_exit`)

The summary row carries two fields that make it easy to filter for "this run delivered value":

- **`produced_output`** — `true` iff at least one live event arrived OR at least one MP4 segment was uploaded. Use this as the canonical success filter across all three modes (events-only, events + recording, recording-only).
- **`connection_opened`** — whether the live connection opened. Always `false` in recording-only mode by design. Use this when you specifically need to know about the event-stream side.

The `reason_for_exit` enum tells you *why* the run ended:

| Value | Meaning |
|---|---|
| `stream_ended` | The broadcaster ended the stream (a `control` event with `status: 3` was received). |
| `runtime_cap` | The run hit your requested `runTime` and exited cleanly. |
| `charge_limit_reached` | A pay-per-event billing cap was reached. |
| `recorder_failed` | The recorder gave up — repeated stream-URL fetches, ffmpeg failures, or KV upload failures. The MP4 segments that did upload are still in the key-value store. |
| `disconnect_unrecoverable` | The live connection failed to reconnect after multiple attempts with zero events received. |
| `transport_closed` | The live connection closed for an unattributed reason. |

### Advanced Usage

#### Record until the broadcaster ends the stream

Set `runTime: 0` and the actor stays connected until the broadcast ends. Apify's hard timeout of 4 hours still applies.

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 0
}
```

#### Recording-only mode (skip the event stream)

Save the broadcast as MP4 without paying for the event firehose. Useful when you only need video for later review or transcription. The dataset gets `room_info` + `summary` and nothing else; the run's OUTPUT manifest lists every recorded segment.

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 60,
  "captureEvents": false,
  "recordVideo": true,
  "videoQuality": "HD1"
}
```

Free tier doesn't allow recording-only — recording is disabled on the free preview, so the run would do nothing.

#### High-bitrate archival capture

Pick the highest quality and short segments for granular replay.

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 120,
  "recordVideo": true,
  "videoQuality": "FULL_HD1",
  "uploadEvery": 1
}
```

#### Pull viewer-count timeseries only

Skip every event except viewer counts. The dataset shrinks dramatically and only carries `current_viewers`, `total_users`, and `popularity` rows with ms-precise timestamps.

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 60,
  "excludeEvents": [
    "chat", "gift", "like", "member", "social",
    "fansclub", "emoji_chat", "control",
    "room_stats", "room_rank", "room_message"
  ]
}
```

#### Chat-and-gift focus

Drop everything except chat, gift, social (follows), and room ranks — the rows that matter for live-commerce analysis.

```json
{
  "liveUrl": "https://live.douyin.com/376034101029",
  "runTime": 60,
  "excludeEvents": [
    "like", "member", "viewer_count",
    "fansclub", "emoji_chat", "control",
    "room_stats", "room_message"
  ]
}
```

#### Bulk-resolve a profile URL with an unknown room id

Paste the broadcaster's profile URL or numeric user id. The actor resolves to the current live room or fails cleanly if the broadcaster isn't streaming.

```json
{
  "liveUrl": "1929015166249580",
  "runTime": 30
}
```

### Pricing — Pay Per Event (PPE)

You only pay for connection time and recording time.

| Event | Per second | Per minute |
|-------|-----------|------------|
| Metadata seconds (event capture) | $0.0015 | **$0.09** |
| Recording seconds (MP4) | $0.00085 | **$0.051** |

#### Cost Examples

| Run | Metadata | Recording | Total |
|-----|----------|-----------|-------|
| 10 minutes, events only | $0.90 | — | **~$0.90** |
| 30 minutes, events + HD recording | $2.70 | $1.53 | **~$4.23** |
| 60 minutes, events + HD recording | $5.40 | $3.06 | **~$8.46** |
| 60 minutes, recording-only at Full HD | — | $3.06 | **~$3.06** |

**Free tier** — 5 lifetime runs, 5 minutes per run, recording disabled. No credit card required. Use it to validate the event shape and decide if the pricing fits before scaling up.

#### Cost Optimization

- Set `runTime` to bound the connection. Open-ended runs are easy to forget about.
- Need MP4 only? Run with `captureEvents: false, recordVideo: true` — recording-only at $0.051/min is the cheapest mode (no metadata billing).
- Need events only? Leave `recordVideo: false` — events-only at $0.09/min skips the recording cost.
- Want both? Combined is ~$0.141/min ($8.46/hour).
- Use SD1 / SD2 quality when the goal is archival, not playback fidelity.

### FAQ

**Which live broadcasts can I record?**
Any public Douyin (抖音) live broadcast. Paste a `live.douyin.com` URL, an app share link, a profile URL, a UserSecID, a numeric user id, or use `auto` for a demo room.

**How fresh is the data?**
Every event is streamed in real time. Rows land in the dataset within seconds of the event firing in the live room.

**What happens if the broadcaster goes offline mid-run?**
The actor emits a `control` row with `status: 3` and then exits cleanly with `reason_for_exit: "stream_ended"` on the summary row.

**What happens if the live connection drops temporarily?**
The actor reconnects with capped exponential backoff (2s, 4s, 8s, 16s, 30s). The summary's `reconnect_count` reflects how many drops happened during the run.

**What happens if Apify migrates my run to a new host (long runs)?**
The actor detects the restart, writes a single `migration_notice` row to the dataset, preserves whatever MP4 segments did upload before the migration, and exits without re-doing work or re-billing. Re-trigger the run for a fresh, complete capture if you need uninterrupted coverage. Runs shorter than ~20 minutes rarely see migrations.

**Can I filter the dataset by event type?**
Yes. The `type` column is the discriminator. Use `type = "chat"` for chat-only views, `type = "gift"` for tips, `type = "viewer_count"` for timeseries, and so on.

**What's the free tier?**
5 lifetime runs, capped at 5 minutes each, recording disabled. The cap resets only by upgrading to a paid plan.

**How do I download the MP4 recordings?**
The run's OUTPUT manifest lists every segment with a `kv_url`. Each URL is a direct download for the MP4 file. Segment length is controlled by `uploadEvery` (1–60 minutes).

**Can I record commercial / restricted broadcasts?**
The actor only attaches to publicly visible broadcasts. Private or password-gated rooms aren't accessible.

**Why is `metadata_seconds_charged` `0` on my local run?**
Pay-per-event billing only fires on the Apify platform. Local runs and pre-publication runs show `0` here — the actor still emits everything, you just don't get charged.

### Support

- **Bugs**: Issues tab
- **Features**: Issues tab

### Legal Compliance

Extracts publicly available data from public live broadcasts. Users must comply with Douyin's terms of service and applicable data protection regulations (GDPR, CCPA). Do not record private, restricted, or copyright-protected content without authorization.

***

*Real-time chat, gift, like, join, and viewer-count capture from Douyin (抖音) live broadcasts, with optional MP4 recording in SD–Full HD.*

# Actor input Schema

## `liveUrl` (type: `string`):

A Douyin live broadcast to record.<br><br>Accepted formats:<ul><li>Browser address bar: <code>https://live.douyin.com/376034101029</code></li><li>Bare numeric web id: <code>376034101029</code></li><li>App share link: <code>https://v.douyin.com/iAbc123/</code></li><li>Profile URL of a live broadcaster: <code>https://www.douyin.com/user/MS4w...</code></li><li>UserSecID: <code>MS4wLjABAAAA...</code></li><li>Numeric user id: <code>1929015166249580</code></li></ul><i>Tip: paste <code>auto</code> to pick a currently-live demo room.</i>

## `runTime` (type: `integer`):

How long to stay connected to the broadcast.<br><br><ul><li><code>0</code> (default) — until the broadcaster ends the stream.</li><li>Range: <code>0–240</code>.</li></ul><i>Apify run timeout is set to 4 hours regardless; this field bounds the connection earlier.</i>

## `captureEvents` (type: `boolean`):

When enabled (default), every chat, gift, like, join, follow, and viewer-count update is streamed into the dataset as one row per event.<br><br>Turn this off to run in <b>recording-only mode</b> — the actor skips the event stream entirely and just saves the MP4. <i>Record video</i> below must be on. You only pay for recording time, and the dataset only carries <code>room\_info</code> and <code>summary</code> rows.

## `excludeEvents` (type: `array`):

Event types to drop from the dataset. Use this to trim the output to only what you need (for example, exclude <code>like</code> if you only care about chats and gifts).<br><br>Wire-level signals (<code>system</code>, <code>stream\_adaptation</code>) and lifecycle markers (<code>connection\_state</code>) are always filtered internally — they're never in the dataset. Ignored when <i>Capture events</i> is off.

## `recordVideo` (type: `boolean`):

When enabled, the stream is recorded as MP4 segments and uploaded to the run's key-value store. Each segment is at most <b>Upload every</b> minutes long.<br><br><b>Billed separately</b> from event capture — recording seconds accrue only while a segment is actively being written. Disabled by default; events are captured either way.

## `videoQuality` (type: `string`):

Choose the stream resolution.<br><br><ul><li><code>SD1</code> — lowest, smallest files.</li><li><code>SD2</code> — standard definition.</li><li><code>HD1</code> — default, balanced.</li><li><code>FULL\_HD1</code> — highest, largest files.</li></ul>Ignored when <b>Record video</b> is off.

## `uploadEvery` (type: `integer`):

Each MP4 segment covers this many minutes before rotating. Smaller values give more, smaller files with better crash resilience; larger values give fewer, bigger files.<br><br><ul><li>Default: <code>5</code>. Range: <code>1–60</code>.</li><li>Set to <code>60</code> (or match your <b>Run time</b>) for one large consolidated file.</li><li>Set to <code>1–2</code> for finer-grained recovery on long runs.</li></ul>Ignored when <b>Record video</b> is off.

## Actor input object example

```json
{
  "liveUrl": "auto",
  "runTime": 0,
  "captureEvents": true,
  "excludeEvents": [],
  "recordVideo": false,
  "videoQuality": "HD1",
  "uploadEvery": 5
}
```

# Actor output Schema

## `results` (type: `string`):

Link to the dataset containing all live events (chat, gifts, likes, joins, viewer counts, stream state) plus the room\_info first row and summary last row. When Record video was enabled, the run's OUTPUT key-value record additionally contains a recordings\[] manifest with KV keys, durations, and direct download URLs for each MP4 segment.

# 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 = {
    "liveUrl": "auto"
};

// Run the Actor and wait for it to finish
const run = await client.actor("zen-studio/douyin-live-recorder").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 = { "liveUrl": "auto" }

# Run the Actor and wait for it to finish
run = client.actor("zen-studio/douyin-live-recorder").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 '{
  "liveUrl": "auto"
}' |
apify call zen-studio/douyin-live-recorder --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Douyin Live Recorder - Real-Time Chat, Gifts & MP4 Capture",
        "description": "Extract real-time chats, gifts, likes, joins, follows, and viewer counts from Douyin (抖音) live broadcasts. Save the stream as MP4 segments. Polymorphic JSON dataset, one row per event. SD to Full HD recording. Free tier included.",
        "version": "0.0",
        "x-build-id": "5q8yMAfDFHtbu8Suc"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/zen-studio~douyin-live-recorder/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-zen-studio-douyin-live-recorder",
                "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/zen-studio~douyin-live-recorder/runs": {
            "post": {
                "operationId": "runs-sync-zen-studio-douyin-live-recorder",
                "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/zen-studio~douyin-live-recorder/run-sync": {
            "post": {
                "operationId": "run-sync-zen-studio-douyin-live-recorder",
                "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",
                "required": [
                    "liveUrl"
                ],
                "properties": {
                    "liveUrl": {
                        "title": "🎥 Douyin live URL",
                        "type": "string",
                        "description": "A Douyin live broadcast to record.<br><br>Accepted formats:<ul><li>Browser address bar: <code>https://live.douyin.com/376034101029</code></li><li>Bare numeric web id: <code>376034101029</code></li><li>App share link: <code>https://v.douyin.com/iAbc123/</code></li><li>Profile URL of a live broadcaster: <code>https://www.douyin.com/user/MS4w...</code></li><li>UserSecID: <code>MS4wLjABAAAA...</code></li><li>Numeric user id: <code>1929015166249580</code></li></ul><i>Tip: paste <code>auto</code> to pick a currently-live demo room.</i>"
                    },
                    "runTime": {
                        "title": "⏱ Run time (minutes)",
                        "minimum": 0,
                        "maximum": 240,
                        "type": "integer",
                        "description": "How long to stay connected to the broadcast.<br><br><ul><li><code>0</code> (default) — until the broadcaster ends the stream.</li><li>Range: <code>0–240</code>.</li></ul><i>Apify run timeout is set to 4 hours regardless; this field bounds the connection earlier.</i>",
                        "default": 0
                    },
                    "captureEvents": {
                        "title": "Capture events",
                        "type": "boolean",
                        "description": "When enabled (default), every chat, gift, like, join, follow, and viewer-count update is streamed into the dataset as one row per event.<br><br>Turn this off to run in <b>recording-only mode</b> — the actor skips the event stream entirely and just saves the MP4. <i>Record video</i> below must be on. You only pay for recording time, and the dataset only carries <code>room_info</code> and <code>summary</code> rows.",
                        "default": true
                    },
                    "excludeEvents": {
                        "title": "🚫 Event types to skip",
                        "uniqueItems": true,
                        "type": "array",
                        "description": "Event types to drop from the dataset. Use this to trim the output to only what you need (for example, exclude <code>like</code> if you only care about chats and gifts).<br><br>Wire-level signals (<code>system</code>, <code>stream_adaptation</code>) and lifecycle markers (<code>connection_state</code>) are always filtered internally — they're never in the dataset. Ignored when <i>Capture events</i> is off.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "chat",
                                "gift",
                                "like",
                                "member",
                                "social",
                                "viewer_count",
                                "fansclub",
                                "emoji_chat",
                                "control",
                                "room_stats",
                                "room_rank",
                                "room_message"
                            ],
                            "enumTitles": [
                                "chat — viewer chat messages",
                                "gift — virtual gifts and tips",
                                "like — like / tap events",
                                "member — viewer joined the room",
                                "social — follow / share actions",
                                "viewer_count — running viewer counts",
                                "fansclub — fansclub join / level events",
                                "emoji_chat — emoji-only chat",
                                "control — broadcaster control signals",
                                "room_stats — room-stat updates",
                                "room_rank — top-contributor rankings",
                                "room_message — system top messages"
                            ]
                        },
                        "default": []
                    },
                    "recordVideo": {
                        "title": "Record video",
                        "type": "boolean",
                        "description": "When enabled, the stream is recorded as MP4 segments and uploaded to the run's key-value store. Each segment is at most <b>Upload every</b> minutes long.<br><br><b>Billed separately</b> from event capture — recording seconds accrue only while a segment is actively being written. Disabled by default; events are captured either way.",
                        "default": false
                    },
                    "videoQuality": {
                        "title": "Video quality",
                        "enum": [
                            "SD1",
                            "SD2",
                            "HD1",
                            "FULL_HD1"
                        ],
                        "type": "string",
                        "description": "Choose the stream resolution.<br><br><ul><li><code>SD1</code> — lowest, smallest files.</li><li><code>SD2</code> — standard definition.</li><li><code>HD1</code> — default, balanced.</li><li><code>FULL_HD1</code> — highest, largest files.</li></ul>Ignored when <b>Record video</b> is off.",
                        "default": "HD1"
                    },
                    "uploadEvery": {
                        "title": "Upload every (minutes)",
                        "minimum": 1,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Each MP4 segment covers this many minutes before rotating. Smaller values give more, smaller files with better crash resilience; larger values give fewer, bigger files.<br><br><ul><li>Default: <code>5</code>. Range: <code>1–60</code>.</li><li>Set to <code>60</code> (or match your <b>Run time</b>) for one large consolidated file.</li><li>Set to <code>1–2</code> for finer-grained recovery on long runs.</li></ul>Ignored when <b>Record video</b> is off.",
                        "default": 5
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
