# Bilibili Video & Search Scraper (`bovi/bilibili-scraper`) Actor

Collect public Bilibili video, comment, danmaku, and search data. Get dataset rows; searchVideo card play and danmaku counts derive from rounded labels and are approximate. Run an operation with an input; no account or API key. Free-plan pricing: $0.002 per row plus $0.00005 per Actor Start event.

- **URL**: https://apify.com/bovi/bilibili-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Social media, Marketing
- **Features:** Limited permissions
- **Stats:** 1 total users, 1 monthly users, 94.6% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 bilibili video & search scrapers

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

Scrape public video, creator, relation, comment, danmaku, subtitle, and keyword-search data from bilibili.com with one operation per run. Free plan pricing: $0.002 per dataset row plus $0.00005 per `apify-actor-start` event.

- Search public video cards by keyword with titles, URLs, creators, counts, durations, and publication dates.
- Collect creator profiles, relation statistics, and creator videos for creator research.
- Extract comments and danmaku for audience-feedback analysis.
- Collect video details and subtitle metadata for cataloging.
- Run without signing in or an API key.

### What you get

Each run uses one operation. Rows contain source-backed values and normalized aliases; unavailable fields are omitted.

| Field family | Fields | Type | What it contains |
|---|---|---|---|
| Run metadata | `_operation`, `_fetchedAt`, `status` | string or ISO 8601 string | Operation, UTC time, and row status |
| Pagination and provenance | `_page`, `_sourceKeyword`, `_sourceAid`, `_sourceCid` | integer or string | Page number and source IDs |
| Video identity | `title`, `url`, `videoTitle`, `videoDesc`, `bvid`, `aid`, `cid` | string | Titles, descriptions, IDs, and canonical URL |
| Video page assets | `videoPageUrl`, `coverUrl` | string | Video and cover URLs |
| Timing and publication | `durationSec`, `durationStr`, `publishedAt`, `publishedDate`, `createdAt` | integer or string | Duration and publication or creation values |
| Category and tags | `videoTypeId`, `videoTypeName`, `tags` | integer, string, or array of strings | Categories and page tags |
| Video metrics | `playCount`, `playCountDisplay`, `likeCount`, `coinCount`, `favoriteCount`, `shareCount`, `commentCount`, `danmuCount`, `danmuCountDisplay` | integer or string | Video counts and display labels |
| Video parts | `videoPageCount`, `videoPages` | integer or array | Listed parts and part data |
| Creator profile | `userId`, `userName`, `userPageUrl`, `avatarUrl`, `bio`, `sex`, `school`, `profession`, `level`, `userVipStatus`, `verification` | string or integer | Creator or commenter identity and profile fields |
| Creator relations | `followerCount`, `followingCount`, `whisperCount`, `archiveCount` | integer | Relation and archive statistics |
| Comment identity | `commentId`, `parentCommentId`, `rootCommentId`, `userLevel` | string or integer | Comment and author identifiers |
| Comment content | `commentText`, `replyCount`, `commentTime` | string or integer | Text, replies, and creation time |
| Danmaku data | `danmuId`, `danmuText`, `timeOffsetSec`, `mode`, `fontSize`, `color`, `pool`, `userHash` | string, number, or integer | Text, timing, display values, and source hash |
| Subtitle metadata | `subtitleId`, `subtitleLanguage`, `subtitleName`, `subtitleType`, `subtitleUrl` | string or integer | Subtitle IDs, languages, type, and URL |

#### Fields by operation

Every operation returns the run metadata fields plus the families below, when the source provides them.

| Operation | Field families returned |
|---|---|
| `searchVideo` | Pagination and provenance, video identity and assets (`title`, `url`, `bvid`, `coverUrl`), creator `userId`, `userName`, `userPageUrl`, play and danmaku counts with display labels, duration, `publishedDate` |
| `videoDetail` | Video identity, `aid`, `cid`, description, cover, duration, `publishedAt`, `createdAt`, category, the seven video counts, video parts, `tags`, creator profile and relation fields from the page |
| `userDetail` | Creator profile fields, `publishedAt`, `archiveCount`, `followerCount`, `followingCount`, `whisperCount` |
| `userRelationStat` | `userId`, `url`, `userPageUrl`, `followerCount`, `followingCount`, `whisperCount` |
| `userVideos` | Video identity, `aid`, description, cover, duration, `publishedAt`, `playCount`, `commentCount`, `videoTypeName`, creator identity |
| `videoComments` | Comment identity and content, `likeCount`, `replyCount`, `commentTime`, commenter `userId`, `userName`, `userPageUrl`, `userLevel`, `userVipStatus`, video `aid` and URLs |
| `videoDanmu` | Danmaku data, `createdAt`, `aid`, `cid`, `_sourceAid`, `_sourceCid`, URLs |
| `videoCaption` | Video identity (`bvid`, `aid`, `cid`, `videoTitle`), URLs, subtitle metadata |

`publishedAt`, `createdAt`, and `commentTime` are Unix seconds. `searchVideo` `playCount` and `danmuCount` are approximate: the Actor derives them from the rounded labels on search cards (for example `2079.9万`), and the exact label is kept in `playCountDisplay` and `danmuCountDisplay`. In `videoDetail` the counts come from the video page's stat block.

### Sample output

This shortened sample omits duplicate video-title and URL fields, plus cover and profile URL fields. All shown values are copied from one real dataset record.

```json
{
  "_operation": "searchVideo",
  "_fetchedAt": "2026-10-15T01:25:24.113157+00:00",
  "status": "success",
  "_page": 1,
  "title": "【全748集】目前B站最全最细的Python零基础全套教程，2026最新版，包含所有干货！七天就能从小白到大神！少走99%的弯路！存下吧！很难找全的！",
  "url": "https://www.bilibili.com/video/BV1rpWjevEip/",
  "bvid": "BV1rpWjevEip",
  "_sourceKeyword": "python",
  "userId": "3546597933714079",
  "userName": "Python官方课程",
  "playCountDisplay": "2079.9万",
  "playCount": 20799000,
  "danmuCountDisplay": "13.3万",
  "danmuCount": 133000,
  "durationStr": "39:58:14",
  "durationSec": 143894,
  "publishedDate": "2024年8月22日"
}
```

### Pricing

Billing uses pay-per-event pricing. A dataset row is billed once as one scraped result with all fields.

| Charge event | Billing unit | Price per event (Free plan) | Condition |
|---|---|---:|---|
| `apify-actor-start` | One Actor Start event | $0.00005 (Free plan) | Charged when the Actor starts running |
| `apify-default-dataset-item` | One dataset row | $0.002 (Free plan) | One scraped result with all fields |

On the Free plan, 1,000 row events cost $2, and 10,000 cost $20; 1,000 start events cost $0.05.

2,499 x $0.002 = $4.998 on the Free plan, so the $5 monthly Free credit covers about 2,499 rows. At the default `maxResults` of 50, the Free credit covers 49 full runs (2,450 dataset rows) with one `apify-actor-start` event each; additional start events reduce that count.

Only the events listed above are charged. This Actor does not use the Pay per event + usage option, so compute, proxy traffic, and storage are not billed separately.

### How to use

1. Choose one operation: `videoDetail`, `userDetail`, `userRelationStat`, `userVideos`, `videoComments`, `videoDanmu`, `videoCaption`, or `searchVideo`.
2. Enter the matching identifier: `keyword` for `searchVideo`; `bvid` for `videoDetail` or `videoCaption`; `userId` for creator operations; `aid` for comments; and both `aid` and `cid` for danmaku.
3. Set `maxResults` for the row cap and `maxPages` for search, creator-video, or comment pagination.
4. Select an optional Apify proxy configuration.
5. Start the run and inspect the resulting dataset rows.
6. Repeat the run through Apify Schedules; daily is a starting point for tracking keyword search results or creator videos.

```json
{
  "operation": "searchVideo",
  "keyword": "python",
  "maxResults": 10,
  "maxPages": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

Export the dataset as JSON or CSV, or read it through the Apify dataset API.

### Input parameters

| Parameter | What it does | Default |
|---|---|---|
| `operation` | Selects the collection mode: `videoDetail`, `userDetail`, `userRelationStat`, `userVideos`, `videoComments`, `videoDanmu`, `videoCaption`, or `searchVideo`. | `searchVideo` |
| `keyword` | Search term for `searchVideo`; Chinese, English, and mixed terms are accepted. | Not set |
| `bvid` | Bilibili BV identifier for `videoDetail` and `videoCaption`; expects `BV` plus 10 letters or digits. | Not set |
| `aid` | Positive numeric video ID for `videoComments` and `videoDanmu`. | Not set |
| `cid` | Positive numeric video-part ID for `videoDanmu`. | Not set |
| `userId` | Positive numeric creator ID for `userDetail`, `userRelationStat`, and `userVideos`. | Not set |
| `maxResults` | Maximum dataset rows; the schema accepts 1 through 1000. | `50` |
| `maxPages` | Page limit for search, creator videos, and comments; the schema accepts 1 through 50. | `5` |
| `proxyConfiguration` | Optional proxy settings. The input form prefills the Apify proxy with the `RESIDENTIAL` group (`{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}`). If you omit the field or set `useApifyProxy` to false, requests go out over a direct connection, as in the example input above. | Prefilled: Apify proxy, `RESIDENTIAL` |

### Limits and edge cases

Records are built before output writing. After collection, rows are pushed one at a time; a failure before writing leaves no rows, while an abort or timeout during writing leaves rows already written.

| Condition | What the buyer sees | What to do |
|---|---|---|
| `maxResults` is reached | No more than the selected row cap is written. | Increase `maxResults` within the schema range. |
| A paginated operation reaches `maxPages` | Search, creator-video, or comment collection stops at the configured limit. | Increase `maxPages` up to 50 and adjust `maxResults`. |
| Pagination reaches the source end or returns no page rows | Fewer rows than requested, or no rows, may be written. | Check the keyword or identifier and try again. |
| Bilibili returns a challenge page or HTTP 403, 406, or 412 | The request is retried up to three attempts and is never treated as data. If it still fails, the run fails; `userDetail` can fall back to the public space page when the profile or relation API errors. | Try an optional Apify proxy configuration or rerun later. |
| A video detail page has no usable `videoData`, a creator response has no creator name, or relation statistics have no usable data | The affected operation fails. | Verify the Bilibili identifier and rerun. |
| Danmaku returns a non-XML response or unparsable XML | The danmaku operation fails. | Verify `aid` and `cid`, then rerun. |

The schema accepts `maxResults` from 1 through 1000. The execution path also returns zero rows when it receives `maxResults` equal to 0, although that value is outside the schema range.

### FAQ

#### How much does it cost to scrape Bilibili?

The Actor charges $0.002 per dataset row on the Free plan; see Pricing for the full calculation.

#### Do I need a Bilibili account or API key?

No. The Actor does not sign in and does not require a Bilibili account or API key. You only need an Apify account to run it.

#### Why did my run return no rows?

A keyword may have no matching rows, or creator-video and comment collection may reach the source end. Danmaku and subtitle metadata operations may also return no rows when the public source provides no corresponding records. Check the operation, identifier, and pagination settings before rerunning.

#### What happens if Bilibili returns an anti-bot challenge?

The Actor detects challenge pages, retries, and does not write the page as a collected record; if retries fail, the run fails. Try an optional Apify proxy configuration or run the collection again later.

#### Is it legal to scrape Bilibili public data?

The Actor reads public Bilibili page data and public endpoints. Whether a particular use is allowed depends on Bilibili’s terms of use and applicable law for your use case; the Actor does not provide a legal guarantee.

### Related actors

- [Douyin Scraper](https://apify.com/bovi/douyin-scraper) — related Chinese social-video data collection for hot trends and videos.
- [Bluesky Scraper](https://apify.com/bovi/bluesky-scraper) — collects Bluesky posts, profiles, and followers.
- [Facebook Comments Scraper](https://apify.com/bovi/facebook-comments-scraper) — related public comment collection for Facebook.

# Actor input Schema

## `operation` (type: `string`):

Choose one operation for this run.

## `keyword` (type: `string`):

Keyword for searchVideo. Chinese, English, and mixed terms are accepted.

## `bvid` (type: `string`):

Bilibili BV identifier for videoDetail and videoCaption.

## `aid` (type: `string`):

Numeric video ID for videoComments and videoDanmu.

## `cid` (type: `string`):

Numeric part ID for videoDanmu.

## `userId` (type: `string`):

Numeric UP主 ID for userDetail, userRelationStat, and userVideos.

## `maxResults` (type: `integer`):

Maximum number of dataset rows this run may write.

## `maxPages` (type: `integer`):

Page limit for search, creator video lists, and comments.

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

Optional Apify proxy settings. An omitted or disabled proxy uses a direct connection.

## Actor input object example

```json
{
  "operation": "searchVideo",
  "keyword": "python",
  "bvid": "BV1rpWjevEip",
  "aid": "113006243481679",
  "cid": "36010133667",
  "userId": "3546597933714079",
  "maxResults": 50,
  "maxPages": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `output` (type: `string`):

Structured rows collected from public Bilibili pages and endpoints.

# 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 = {
    "operation": "searchVideo",
    "keyword": "python",
    "bvid": "BV1rpWjevEip",
    "aid": "113006243481679",
    "cid": "36010133667",
    "userId": "3546597933714079",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("bovi/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 = {
    "operation": "searchVideo",
    "keyword": "python",
    "bvid": "BV1rpWjevEip",
    "aid": "113006243481679",
    "cid": "36010133667",
    "userId": "3546597933714079",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("bovi/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 '{
  "operation": "searchVideo",
  "keyword": "python",
  "bvid": "BV1rpWjevEip",
  "aid": "113006243481679",
  "cid": "36010133667",
  "userId": "3546597933714079",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call bovi/bilibili-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,bovi/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/AZLg4IExQIdlVbhP3/builds/uKo6S6ivhDyQIL70t/openapi.json
