# Bilibili Danmaku (Bullet Comment) Extractor (`xtracto/bilibili-danmaku`) Actor

Extract timed danmaku (bullet comments) from Bilibili videos: resolve a BV id to its cid then pull every comment with its in-video timestamp, color, mode and send-time. By video or trending feed. No login.

- **URL**: https://apify.com/xtracto/bilibili-danmaku.md
- **Developed by:** [Farhan Febrian Nauval](https://apify.com/xtracto) (community)
- **Categories:** Lead generation, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Bilibili Danmaku (Bullet Comment) Scraper

Extract every timed **danmaku** — the "bullet comments" that fly across the screen of a Bilibili video — together with the exact second in the video each one was posted at. It is a crowd-sourced, second-by-second reaction stream with no clean Western equivalent, ideal for sentiment analysis, highlight detection, and engagement research.

### Why use this actor

- **No account, no login, no API key** — just paste a video id or link and run.
- **A dataset you can't get elsewhere** — the flying on-screen comments, each pinned to its moment in the video timeline. Great for spotting the funniest, most-hyped, or most-controversial seconds of a video.
- **Discover what's hot** — a "popular" mode that first pulls Bilibili's trending videos, then collects their comments, so you don't need to know video ids in advance.
- **Clean, structured output** — one row per comment with its timestamp-in-video, post time, colour, on-screen style, and an anonymized sender tag. Ready for spreadsheets, databases, or pipelines.
- **Runs on a schedule** with automatic retries — export to JSON, CSV, or Excel. No scrapers or browsers to babysit.

### How it works

1. Choose a **mode**: paste your own videos, or let the actor pull the current trending feed.
2. For each video, the actor looks up the video (including its individual parts, if any).
3. It collects the full set of on-screen comments for the video and reads out each comment's text, its position in the video timeline, and when it was posted.
4. Every comment becomes one row in your dataset.

You never manage scrapers, browsers, or blocks — just provide videos and read the results.

### Input

**Mode: Video (by BV id / link)** — you supply the videos.

```json
{
  "mode": "video",
  "videos": ["BV1juM36LEg6"],
  "firstPartOnly": true,
  "maxDanmakuPerVideo": 0,
  "maxItems": 500
}
````

**Mode: Popular (trending feed)** — the actor discovers videos for you.

```json
{
  "mode": "popular",
  "popularPages": 1,
  "pageSize": 20,
  "maxItems": 500
}
```

| Field | Type | Description |
|-------|------|-------------|
| `mode` | string | `video` (use the videos you provide) or `popular` (discover trending videos first). Default `video`. |
| `videos` | array of strings | *Video mode.* Bilibili BV ids (e.g. `BV1juM36LEg6`) or full video links (`https://www.bilibili.com/video/BV1juM36LEg6`). The id is read from a link automatically. |
| `firstPartOnly` | boolean | Many videos have several parts. `true` (default) pulls comments from only the first part; `false` pulls from every part. |
| `popularPages` | integer | *Popular mode.* How many pages of the trending feed to walk (default 1). |
| `pageSize` | integer | *Popular mode.* Videos per trending page (default 20, max 50). |
| `maxDanmakuPerVideo` | integer | Cap on comments per video. `0` = no cap. |
| `maxItems` | integer | Overall cap on rows for the whole run. `0` = no cap. |
| `proxyConfiguration` | object | Optional. Not required; residential proxy support is available if an address gets rate-limited. |

### Output

One row per comment (the sample below is a real trending video, Chinese text as posted):

```json
{
  "_input": "popular:BV1juM36LEg6",
  "_scrapedAt": "2026-07-09T11:08:42Z",
  "_source": "S1-danmaku-xml",
  "bvid": "BV1juM36LEg6",
  "aid": 116883726602536,
  "cid": 39758924412,
  "videoTitle": "再干！",
  "part": "再干！",
  "page": 1,
  "progressSec": 662.138,
  "mode": 5,
  "fontsize": 25,
  "color": 15772458,
  "colorHex": "#F0AB2A",
  "sendTime": 1783588689,
  "sendTimeISO": "2026-07-09T09:18:09Z",
  "pool": 0,
  "userHash": "68e23934",
  "dmid": "2151656896334603776",
  "text": "第二天小杨因左脚先踏进办公室而奖励1000"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `bvid` | string | The video's public id. |
| `aid` | integer | The video's numeric id. |
| `cid` | integer | Id of the specific video part these comments belong to. |
| `videoTitle` | string | Title of the video. |
| `part` | string | Title of the part (for multi-part videos). |
| `page` | integer | Part number within the video. |
| `progressSec` | number | **Where in the video the comment appears**, in seconds. |
| `mode` | integer | On-screen style: 1–3 scrolling, 4 bottom, 5 top, 6 reverse, 7 advanced, 8 code, 9 special. |
| `fontsize` | integer | Comment font size. |
| `color` / `colorHex` | integer / string | Comment colour, as a number and as a hex code (`#F0AB2A`). |
| `sendTime` / `sendTimeISO` | integer / string | When the comment was posted (Unix seconds and ISO date). |
| `pool` | integer | Comment pool: 0 normal, 1 subtitle, 2 special. |
| `userHash` | string | Anonymized tag for the sender (not a real identity). |
| `dmid` | string | Unique id of the comment. |
| `text` | string | The comment itself. |

On failure (e.g. a removed or invalid video) the actor writes an error row instead of skipping silently:

```json
{ "_input": "...", "_scrapedAt": "...", "_error": "api_code_-404", "_errorDetail": "...", "bvid": "..." }
```

### Notes & limits

- For very busy videos Bilibili itself serves a **capped sample of roughly 3,600 comments** per part — that is a source-side limit, not the actor.
- No account is required. A proxy is optional; turn on residential proxy support only if an address gets rate-limited.
- Chinese-language comments are returned exactly as posted (Unicode).

# Actor input Schema

## `mode` (type: `string`):

How to choose which videos to scrape. 'video' pulls danmaku for the BV ids / URLs you provide. 'popular' first discovers trending videos from Bilibili's popular feed, then pulls their danmaku.

## `videos` (type: `array`):

Used when mode = 'video'. Each item is a Bilibili BV id (e.g. BV1juM36LEg6) OR a full video URL (e.g. https://www.bilibili.com/video/BV1juM36LEg6). The BV id is extracted automatically.

## `firstPartOnly` (type: `boolean`):

Many Bilibili videos contain several parts, each with its own cid. When true, only the first part's danmaku is fetched. When false, danmaku from every part is fetched.

## `popularPages` (type: `integer`):

Used when mode = 'popular'. Number of pages of the trending feed to walk when discovering video ids.

## `pageSize` (type: `integer`):

Used when mode = 'popular'. How many videos per page of the trending feed (max 50).

## `maxDanmakuPerVideo` (type: `integer`):

Cap on the number of danmaku comments emitted per video (counted across all fetched parts). 0 = no cap. Note: the list.so endpoint itself returns a capped sample (~3600) for very busy videos.

## `maxItems` (type: `integer`):

Global cap on the total number of danmaku records pushed to the dataset across all videos. 0 = no cap.

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

Optional. The API worked from a normal IP in recon (no WAF), so a proxy is NOT required. If an IP gets soft rate-limited, RESIDENTIAL (ideally a CN-friendly location) can help.

## Actor input object example

```json
{
  "mode": "video",
  "videos": [
    "BV1juM36LEg6"
  ],
  "firstPartOnly": true,
  "popularPages": 1,
  "pageSize": 20,
  "maxDanmakuPerVideo": 0,
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "videos": [
        "BV1juM36LEg6"
    ],
    "maxItems": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("xtracto/bilibili-danmaku").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 = {
    "videos": ["BV1juM36LEg6"],
    "maxItems": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("xtracto/bilibili-danmaku").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 '{
  "videos": [
    "BV1juM36LEg6"
  ],
  "maxItems": 500
}' |
apify call xtracto/bilibili-danmaku --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Bilibili Danmaku (Bullet Comment) Extractor",
        "description": "Extract timed danmaku (bullet comments) from Bilibili videos: resolve a BV id to its cid then pull every comment with its in-video timestamp, color, mode and send-time. By video or trending feed. No login.",
        "version": "0.1",
        "x-build-id": "vokEJjJwQVzkYwUlB"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/xtracto~bilibili-danmaku/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-xtracto-bilibili-danmaku",
                "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/xtracto~bilibili-danmaku/runs": {
            "post": {
                "operationId": "runs-sync-xtracto-bilibili-danmaku",
                "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/xtracto~bilibili-danmaku/run-sync": {
            "post": {
                "operationId": "run-sync-xtracto-bilibili-danmaku",
                "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": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "video",
                            "popular"
                        ],
                        "type": "string",
                        "description": "How to choose which videos to scrape. 'video' pulls danmaku for the BV ids / URLs you provide. 'popular' first discovers trending videos from Bilibili's popular feed, then pulls their danmaku.",
                        "default": "video"
                    },
                    "videos": {
                        "title": "Videos (BV ids or URLs)",
                        "type": "array",
                        "description": "Used when mode = 'video'. Each item is a Bilibili BV id (e.g. BV1juM36LEg6) OR a full video URL (e.g. https://www.bilibili.com/video/BV1juM36LEg6). The BV id is extracted automatically.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "firstPartOnly": {
                        "title": "First part only (multi-part videos)",
                        "type": "boolean",
                        "description": "Many Bilibili videos contain several parts, each with its own cid. When true, only the first part's danmaku is fetched. When false, danmaku from every part is fetched.",
                        "default": true
                    },
                    "popularPages": {
                        "title": "Popular pages",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Used when mode = 'popular'. Number of pages of the trending feed to walk when discovering video ids.",
                        "default": 1
                    },
                    "pageSize": {
                        "title": "Popular page size",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Used when mode = 'popular'. How many videos per page of the trending feed (max 50).",
                        "default": 20
                    },
                    "maxDanmakuPerVideo": {
                        "title": "Max danmaku per video (0 = all)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Cap on the number of danmaku comments emitted per video (counted across all fetched parts). 0 = no cap. Note: the list.so endpoint itself returns a capped sample (~3600) for very busy videos.",
                        "default": 0
                    },
                    "maxItems": {
                        "title": "Max items (0 = all)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Global cap on the total number of danmaku records pushed to the dataset across all videos. 0 = no cap.",
                        "default": 0
                    },
                    "proxyConfiguration": {
                        "title": "Proxy",
                        "type": "object",
                        "description": "Optional. The API worked from a normal IP in recon (no WAF), so a proxy is NOT required. If an IP gets soft rate-limited, RESIDENTIAL (ideally a CN-friendly location) can help.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
