# Bilibili Scraper: Videos, Danmaku, Comments (`agnes.developer.queen/bilibili-scraper`) Actor

Bilibili scraper for video search, details, popular, danmaku, comments, subtitle transcripts and profiles. Charged only for rows delivered. $12 per 1,000 video rows.

- **URL**: https://apify.com/agnes.developer.queen/bilibili-scraper.md
- **Developed by:** [Agnes Maina](https://apify.com/agnes.developer.queen) (community)
- **Categories:** Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 75.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 videos

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

## Bilibili Scraper: videos, danmaku, comments, transcripts

[![Run on Apify](https://apify.com/actor-badge?actor=agnes.developer.queen/bilibili-scraper)](https://apify.com/agnes.developer.queen/bilibili-scraper)

This Bilibili scraper reads video search results, video details, the popular list, danmaku (the timed comments displayed over a video), comments, subtitle transcripts, an uploader's videos and channel profiles. You pay for rows you receive: $0.012 per video row, which is $12 per 1,000 videos, and every billable event fires only after a successful dataset push.

Limits before you run it: `maxItems` caps items at 1,000 per query or per user, `maxDanmakuPerVideo` at 10,000 and `maxCommentsPerVideo` at 5,000. Which modes need a cookie you supply yourself: `comments` and `transcript`. Without one, comments stop at the top 3 ordinary replies per video plus a pinned reply, with `totalComments` still reporting the true total, and `transcript` returns the subtitle language list only, with `hasText` false, so it costs nothing per row. The other six modes, `search`, `video_detail`, `popular`, `danmaku`, `user_videos` and `profile`, need no cookie, and profile mode ignores it.

### What you get

One row type per mode, and each one has its own price:

| Mode | Rows you get | Billed as |
| --- | --- | --- |
| `search` | Video search rows per `queries` entry, with coins and shares from signed video detail | one `video` event per row |
| `popular` | The popular video list, `maxItems` in total | one `video` event per row |
| `user_videos` | An uploader's videos, `maxItems` per mid | one `video` event per row |
| `video_detail` | Detail and statistics for each BV ID or video URL | one `video-detail` event per row |
| `danmaku` | Timed on-screen comments per video, up to `maxDanmakuPerVideo` across all pages | one `danmaku-video` event per video that delivered a row |
| `comments` | Comments per video, up to `maxCommentsPerVideo` including second-level replies, each row with `totalComments` | one `comment` event per delivered row with non-empty text |
| `transcript` | Subtitle language rows per part and language, with `hasText` saying whether text came with it | one `transcript` event per row with text |
| `profile` | Uploader profiles, `maxItems` across all mids, missing fields null | one `profile` event per non-empty row |

Rows are keyed by bvid for video modes and by mid for profile mode, which is what delta mode tracks. Each run writes `RUN_SUMMARY` with pushed rows, charged events and errors.

### Try it in 30 seconds

1. Open the [Actor page in the Apify Store](https://apify.com/agnes.developer.queen/bilibili-scraper) and click **Try for free**.
2. Pick `mode`, fill the input it needs, click **Start** and open the dataset.

`BV1VeHQ6tEaS` is a video ID and `946974` is an uploader's numeric mid. Apify Proxy (datacenter) is the default; these examples switch it off only to keep the input short.

| What you want | Input |
| --- | --- |
| Search for videos | `{"mode":"search","queries":["Genshin Impact"],"maxItems":20,"proxyConfiguration":{"useApifyProxy":false}}` |
| Video detail and statistics | `{"mode":"video_detail","videoUrls":["BV1VeHQ6tEaS"],"proxyConfiguration":{"useApifyProxy":false}}` |
| The popular list | `{"mode":"popular","maxItems":20,"proxyConfiguration":{"useApifyProxy":false}}` |
| Danmaku for a video | `{"mode":"danmaku","videoUrls":["BV1VeHQ6tEaS"],"maxDanmakuPerVideo":100,"proxyConfiguration":{"useApifyProxy":false}}` |
| Comments for a video | `{"mode":"comments","videoUrls":["BV1VeHQ6tEaS"],"maxCommentsPerVideo":50,"proxyConfiguration":{"useApifyProxy":false}}` |
| Subtitle languages, or text with a cookie | `{"mode":"transcript","videoUrls":["BV1VeHQ6tEaS"],"proxyConfiguration":{"useApifyProxy":false}}` |
| An uploader's videos | `{"mode":"user_videos","userIds":["946974"],"maxItems":5,"proxyConfiguration":{"useApifyProxy":false}}` |
| Uploader profiles | `{"mode":"profile","userIds":["946974","https://space.bilibili.com/8295295"],"maxItems":2,"proxyConfiguration":{"useApifyProxy":false}}` |

### Pricing

| Event | Price | What counts |
| --- | ---: | --- |
| `video` | $0.012 | One search, popular or user_videos row |
| `video-detail` | $0.02 | One video_detail row |
| `comment` | $0.008 | One delivered comment with non-empty text |
| `danmaku-video` | $0.01 | One video that delivers at least one danmaku row, whatever the count |
| `transcript` | $0.01 | One delivered transcript row with text, per part and language |
| `profile` | $0.012 | One non-empty profile row |

That is $12 per 1,000 video rows, $0.01 per danmaku set and $0.01 per transcript with text. The current pricing has no start event. Language-only transcript rows are free, so a run with no cookie and no subtitle text has no event charges.

### Input

| Field | Type | Default | Limits and behavior |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search`, `video_detail`, `popular`, `danmaku`, `comments`, `transcript`, `user_videos` or `profile` |
| `queries` | array | one prefilled query | One Bilibili video search per entry |
| `videoUrls` | array | none | BV IDs or bilibili.com/video/BV URLs |
| `userIds` | array | none | Numeric mid, space.bilibili.com/<mid>, or https://space.bilibili.com/<mid>, for `user_videos` and `profile` |
| `maxItems` | integer | 100 | 1 through 1000. Maximum per query or user, total for popular, or videos processed after dedupe and delta for `videoUrls` modes. Profile caps delivered rows across all mids |
| `maxDanmakuPerVideo` | integer | 5000 | 1 through 10000 danmaku rows per video across all pages |
| `maxCommentsPerVideo` | integer | 100 | 1 through 5000 comments per video, including second-level replies |
| `publishedAfter` | string | none | Inclusive start date, `YYYY-MM-DD` in UTC, for `search` and `user_videos` |
| `publishedBefore` | string | none | Inclusive end date, `YYYY-MM-DD` in UTC, for `search` and `user_videos` |
| `cookie` | string | none | Secret input. A bare SESSDATA value or a full browser cookie string. It unlocks comments past the visitor limit of 3 and subtitle text, and is never logged or stored |
| `deltaMode` | boolean | false | Skip a mode and bvid pair delivered by an earlier run before detail fetching, or a profile and mid pair before card fetching, and never bill it again |
| `proxyConfiguration` | object | `{"useApifyProxy":true}` | Datacenter Apify Proxy by default. Custom proxy URLs and disabled proxy are honored |

#### Delta mode

`deltaMode` true skips items already delivered by an earlier run, tracked in the named key-value store `bilibili-scraper-seen`. Already delivered means the prior push and charge succeeded and the marker was saved. For danmaku, comments and transcript, the whole video's intended rows must finish before the marker is saved. Failed videos and zero-row results get no marker and are retried next run. A crash between a push and its marker can repeat rows, because the push, the charge and the marker are not one transaction. Completed language-only transcript markers also skip later cookie enabled delta runs, so turn delta off to fetch text that only a cookie unlocks.

### Use cases

- Track what a channel publishes with `user_videos` and `maxItems`.
- Follow the discussion under one video with `comments`, then read `totalComments` against the delivered rows.
- Read the timed comment track for a video with `danmaku`, billed per video.
- Collect subtitle text where a cookie unlocks it, through `transcript`.
- Build an uploader list from `profile` rows across several mids.

### Integrations

Run it from the Apify API and read the dataset back in the same call:

```bash
curl -X POST "https://api.apify.com/v2/acts/agnes.developer.queen~bilibili-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","queries":["Genshin Impact"],"maxItems":20}'
```

The same call works from Python, Node.js, Make, n8n or Zapier. AI assistants and MCP clients can call the Actor through the Apify MCP server:

```
https://mcp.apify.com/?tools=agnes.developer.queen/bilibili-scraper
```

### FAQ

#### Do I need a login or a cookie?

No Bilibili account is needed. A SESSDATA session cookie you supply yourself is only needed to go past 3 ordinary comments per video and to get subtitle text. The other six modes run without one, and `profile` ignores it.

#### What is danmaku?

Danmaku are the timed comments displayed over a video as it plays. The actor bills one `danmaku-video` event per video that produced a row, not per row, so a video with 5,000 danmaku rows costs the same as one with a single row.

#### Can I get subtitles or transcripts?

Without a cookie, `transcript` returns the language list only, with `hasText` false, and a video with no listed languages produces no rows. Add a valid login session in `cookie` to unlock comment pagination and subtitle text, billed per transcript row with text.

#### How do I get only new videos?

Set `deltaMode` to true and run on a schedule. Video modes key on mode plus bvid, profile keys on mode plus mid. Items that failed or produced no rows are retried next run.

#### What does maxItems apply to?

It caps items per query or per user, the popular list in total, and videos processed after dedupe and delta in the `videoUrls` modes. For `profile` it caps delivered rows across all mids. The hard cap is 1,000.

#### Can I filter by upload date?

Yes, with `publishedAfter` and `publishedBefore` as `YYYY-MM-DD` dates in UTC. Both are inclusive, for `search` and `user_videos`.

#### What happens when a request fails?

A transport error on a video is recorded in `RUN_SUMMARY.errors`, leaves that video unmarked and is retried on the next delta run, with no row charged. `user_videos` also retries risk-control responses such as -352 with a fresh visitor session before falling back to an author-filtered search.

#### Why was a row charged?

Every billable event fires only after a successful dataset push, so a charge means the row is in your dataset. An empty result or a failed request costs nothing.

### Related Actors

- [Meta Ad Library Scraper](https://apify.com/agnes.developer.queen/meta-ad-library-scraper) pulls public ad data from Meta.
- [Google Ads Transparency Scraper](https://apify.com/agnes.developer.queen/google-ads-transparency-scraper) does the same for Google Ads, on the same pay per delivered row model.

### Support

Open an issue on the [Issues tab of the Store page](https://apify.com/agnes.developer.queen/bilibili-scraper/issues) with the run ID, your input and `RUN_SUMMARY`.

# Actor input Schema

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

Choose search, video_detail, popular, danmaku, comments, transcript, user_videos, or profile.

## `queries` (type: `array`):

One Bilibili video search per entry.

## `videoUrls` (type: `array`):

BV IDs or bilibili.com/video/BV URLs.

## `userIds` (type: `array`):

Numeric mid, space.bilibili.com/<mid>, or https://space.bilibili.com/<mid> for user_videos and profile.

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

Maximum per query or user, total for popular, or videos processed after dedupe and delta for videoUrls modes. Profile caps delivered rows across all mids after delta. Hard cap 1000.

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

Maximum comments per video, including second-level replies (1 through 5000).

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

Maximum danmaku rows per video across all pages (1 through 10000).

## `publishedAfter` (type: `string`):

Inclusive search and user_videos start date, YYYY-MM-DD in UTC.

## `publishedBefore` (type: `string`):

Inclusive search and user_videos end date, YYYY-MM-DD in UTC.

## `cookie` (type: `string`):

Optional bare SESSDATA value or full browser cookie string. Unlocks comments past the visitor limit of 3 and transcript text. Never logged or stored.

## `deltaMode` (type: `boolean`):

Skip a mode/bvid pair from an earlier run before detail fetching, or a profile/mid pair before card fetching, and never bill it again.

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

Datacenter Apify Proxy by default. Custom proxy URLs and disabled proxy are honored.

## Actor input object example

```json
{
  "mode": "search",
  "queries": [
    "原神"
  ],
  "maxItems": 20,
  "maxCommentsPerVideo": 100,
  "maxDanmakuPerVideo": 5000,
  "deltaMode": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `rows` (type: `string`):

All delivered rows for this run (videos, danmaku, comments, transcript segments, profiles), as JSON.

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

RUN_SUMMARY record: rows pushed per mode, charged event counts, errors and budget state.

# 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 = {
    "queries": [
        "原神"
    ],
    "maxItems": 20
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "queries": ["原神"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("agnes.developer.queen/bilibili-scraper").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 '{
  "queries": [
    "原神"
  ],
  "maxItems": 20
}' |
apify call agnes.developer.queen/bilibili-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agnes.developer.queen/bilibili-scraper"
        }
    }
}
```

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/VN691neZwyR3ZYaBV/builds/nWIM17ncRMEYqRkVm/openapi.json
