# Bilibili Scraper — Video Search & Video Details (No Login) (`cemented_jest/bilibili-scraper`) Actor

Search Bilibili videos by keyword (up to 1,000 per keyword) or fetch full metadata for BV ids and video URLs. Plain HTTP first, browser fallback, documented output schema, no login. Pay per result.

- **URL**: https://apify.com/cemented_jest/bilibili-scraper.md
- **Developed by:** [CRNO](https://apify.com/cemented_jest) (community)
- **Categories:** Social media
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 video search results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bilibili Scraper: Video Search & Video Details (No Login)

Search Bilibili videos by keyword (up to 1,000 per keyword, five sort orders) or collect available public metadata for a video from its BV id, av id or URL.
No login, no cookies: normal runs use plain HTTP, and a real browser is only the last fallback. 24 documented fields per video, `null` when the site does not provide a value.
Result fees apply to saved videos. A separate memory-based Actor start fee also applies, including runs with no results. See Pricing below.

Sample item (`video` mode, description shortened for display):

```json
{
  "bvid": "BV1c4411e77t",
  "aid": 52080698,
  "title": "【Python教程】《零基础入门学习Python》最新版（完结撒花🎉）",
  "description": "本系列教程是《零基础入门学习Python》最新版教程，希望大家喜欢。\n\n课后资源&作业 >>> BV1BJ41157PJ...",
  "url": "https://www.bilibili.com/video/BV1c4411e77t",
  "cover": "https://i2.hdslb.com/bfs/archive/383c952565090276d1908c1f50d416d0b43c8429.jpg",
  "durationSec": 63328,
  "publishedAt": "2019-05-10T22:33:01.000Z",
  "uploaderMid": 314076440,
  "uploaderName": "鱼C-小甲鱼",
  "uploaderUrl": "https://space.bilibili.com/314076440",
  "viewCount": 13846215,
  "likeCount": 283242,
  "coinCount": 227351,
  "favoriteCount": 683388,
  "shareCount": 119020,
  "commentCount": 10904,
  "danmakuCount": 144479,
  "tags": ["Python教学", "零基础Python入门", "Python3教程", "零基础入门学习Python第二版", "Python程序设计", "Python爬虫", "小甲鱼Python", "Python开发", "Python教程", "小甲鱼编程教学"],
  "categoryName": null,
  "position": 1,
  "sourceKey": "av52080698",
  "mode": "video",
  "scrapedAt": "2026-11-04T05:50:54.504Z"
}
```

### Collection methods and limits

| What | What you get |
|---|---|
| No account needed | Search and video pages are public. No login, no cookies, no signature handling on your side. |
| HTTP first | Measured: about 21-25 KB of proxy traffic per search page (about 37 videos) and about 90 KB per video document. A browser is started only if both HTTP paths fail. |
| Three acquisition paths | Search API, then the server-rendered search page, then a browser. The path used for every page is recorded in the `RUN_SUMMARY` record. The API gives the most complete and stable lists; fallbacks may recover some blocked requests, but access is not guaranteed. |
| Exact, documented schema | 24 keys, exact types, the same keys in both modes, `null` (or `[]`) when a value is unavailable. |
| Null is not success | A video without id, title, uploader id, play count or publish date is never written to the dataset and never charged. |
| Chinese as served | Titles, descriptions and tags come back in the original Chinese (zh-CN), not machine-translated. |
| Honest window information | Bilibili exposes at most about 1,000 results per keyword and pages overlap. `RUN_SUMMARY` reports `numResults`, `numPages`, the real count and `windowLimited`. |
| Drift detection | `RUN_SUMMARY` carries a hash of the raw key structure, so a site layout change shows up as a changed hash. |

### Modes

- **`search`**: `keywords` (1-50), `order` and `maxItems` (1-1,000 per keyword). Each keyword is searched on its own; duplicates inside a keyword are removed; `position` is the rank among the videos you receive.
- **`video`**: `videoUrls` (1-1,000): BV ids (`BV1c4411e77t`), av ids (`av52080698`) and `bilibili.com/video/...` URLs in any mix. The same video given twice (for example as BV id and as av id) is returned and charged once.
  Videos that are deleted, invisible or refused get a documented reason in `RUN_SUMMARY.failed` (for example `video-not-found`) and are not charged.

Sample item (`search` mode; `coinCount` and `shareCount` are only available in `video` mode, description shortened for display):

```json
{
  "bvid": "BV1vGxF6sEsz",
  "aid": 117523710218309,
  "title": "【全500集】 目前2026最全最细的Python零基础全套教程，（包含爬虫+数据分析+AI）一周逼自己学完！从入门到精通，看完这一套就够了！",
  "description": "本套教程为2026最新录制，非常适合毫无编程经验的零基础小白，用来学习提升自己...",
  "url": "https://www.bilibili.com/video/BV1vGxF6sEsz",
  "cover": "https://i2.hdslb.com/bfs/archive/40e04b3f9a0eff07d50f6a40b4fa08baad80522a.jpg",
  "durationSec": 370415,
  "publishedAt": "2026-10-29T10:31:14.000Z",
  "uploaderMid": 3546801739139971,
  "uploaderName": "Python入门零基础教程君",
  "uploaderUrl": "https://space.bilibili.com/3546801739139971",
  "viewCount": 13644,
  "likeCount": 615,
  "coinCount": null,
  "favoriteCount": 1541,
  "shareCount": null,
  "commentCount": 127,
  "danmakuCount": 327,
  "tags": ["人工智能", "编程", "计算机", "Python安装", "Python教程", "Python基础", "Python爬虫", "Python入门", "Python", "数据分析"],
  "categoryName": "计算机技术",
  "position": 3,
  "sourceKey": "python 教程",
  "mode": "search",
  "scrapedAt": "2026-11-04T05:48:29.546Z"
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `mode` | `search` or `video` | `search` | |
| `keywords` | string\[] | `[]` | Required in search mode, 1-50 entries. Chinese keywords work best. |
| `order` | `relevance`, `pubdate`, `click`, `dm`, `stow` | `relevance` | Newest first, most played, most danmaku, most favorited. Checked on 2026-11-04 for one keyword with 42-50 results each: no order violation for any of the four sorted orders. |
| `maxItems` | integer 1-1,000 | 100 | Per keyword. |
| `videoUrls` | string\[] | `[]` | Required in video mode, 1-1,000 entries. |
| `maxConcurrency` | integer 1-10 | 3 | Parallel keywords or videos. Requests are also spaced out. |
| `proxyConfiguration` | proxy | Apify residential, US exit | Datacenter proxies were refused by video pages (HTTP 412) in the test, so video mode needs residential IPs. |
| `debug` | boolean | `false` | Verbose log; bodies of unparseable responses are saved as `DEBUG_*` records. |

Invalid input fails immediately with an `Input:` message (for example `Input: maxItems must be an integer between 1 and 1000 (got 2000).`) and no video-result fee is charged. The Actor start fee may still apply.

#### Input examples

1. 100 relevant videos for one keyword

```json
{ "mode": "search", "keywords": ["python 教程"], "maxItems": 100 }
```

2. Newest videos for several keywords

```json
{ "mode": "search", "keywords": ["耳机 评测", "机械键盘"], "order": "pubdate", "maxItems": 50 }
```

3. As many videos as the site exposes, most played first

```json
{ "mode": "search", "keywords": ["原神"], "order": "click", "maxItems": 1000 }
```

4. Full metadata (including coin and share counts and all tags) for known videos

```json
{ "mode": "video", "videoUrls": ["BV1c4411e77t", "av52080698", "https://www.bilibili.com/video/BV1xx411c7mD/"] }
```

5. Explicit proxy and concurrency

```json
{ "mode": "search", "keywords": ["教程"], "maxItems": 200, "maxConcurrency": 5,
  "proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US" } }
```

### Output fields

Every item has the following 24 keys. The table below describes their meaning and availability.

| Field | Type | Example | Nullable | Notes |
|---|---|---|---|---|
| `bvid` | string | `BV1c4411e77t` | no | Video id. |
| `aid` | integer | `52080698` | yes | Legacy numeric id. |
| `title` | string | `【Python教程】...` | no | Highlight markup removed, HTML entities decoded. |
| `description` | string | `本系列教程是...` | yes | As served; can be an empty string. |
| `url` | string | `https://www.bilibili.com/video/BV1c4411e77t` | no | Canonical URL. |
| `cover` | string | `https://i2.hdslb.com/bfs/archive/....jpg` | yes | Always https. |
| `durationSec` | integer | `63328` | yes | Seconds. |
| `publishedAt` | string | `2019-05-10T22:33:01.000Z` | no | ISO-8601 UTC. |
| `uploaderMid` | integer | `314076440` | no | Uploader member id. |
| `uploaderName` | string | `鱼C-小甲鱼` | yes | |
| `uploaderUrl` | string | `https://space.bilibili.com/314076440` | yes | |
| `viewCount` | integer | `13846215` | no | Plays. |
| `likeCount` | integer | `283242` | yes | |
| `coinCount` | integer | `227351` | yes | **Video mode only**; `null` in search mode. |
| `favoriteCount` | integer | `683388` | yes | |
| `shareCount` | integer | `119020` | yes | **Video mode only**; `null` in search mode. |
| `commentCount` | integer | `10904` | yes | Number of comments (the comments themselves are not collected). |
| `danmakuCount` | integer | `144479` | yes | Bullet comments. |
| `tags` | string\[] | `["Python教学"]` | no (`[]`) | Complete in video mode, partial in search mode. |
| `categoryName` | string | `计算机技术` | yes | **Search mode only**; `null` in video mode (the video page serves an empty category name). |
| `position` | integer | `3` | no | Rank within the `sourceKey`; always 1 in video mode. |
| `sourceKey` | string | `python 教程` | no | The keyword, or the BV id / av id / URL as you gave it. |
| `mode` | string | `search` | no | `search` or `video`. |
| `scrapedAt` | string | `2026-11-04T05:38:09.022Z` | no | ISO-8601 UTC. |

#### RUN_SUMMARY (key-value store)

Written at the end of every run: counts (`itemsPushed`, `itemsRejected`, `itemsDuplicate`, `blockedEvents`, `retries`), pages per path (`pathPages`: `api`, `ssr`, `browser`, `http`),
a record for every page (`pages`: key, page, path, rows, new items, attempts, milliseconds), per keyword or video (`perKey`: status, `numResults`, `numPages`, `pagesize`, `firstPageCode`,
`windowLimited`, `endReason`, `rawKeyHash`), `nullRateByField`, `rawKeyHash` (listing and detail), `failed` (key, url, reason, retries) and `chargeLimitReached`.

### Use cases

- Track what is trending for a topic: search with `order: pubdate` or `click` on a schedule and compare `viewCount` between runs.
- Build a dataset of Chinese video titles, descriptions and tags for research or model training (public metadata only).
- Look up a list of known videos: statistics (plays, likes, coins, favorites, shares, comments, danmaku) and tags in one pass.
- Creator and content research: find the uploaders (`uploaderMid`) behind the videos that rank for your keywords.

### Pricing

| Event | Price | When it applies |
| --- | --- | --- |
| `video-result` | $0.004 per saved row ($4 per 1,000) | Keyword-search videos saved to the dataset. |
| `video-detail` | $0.008 per saved row ($8 per 1,000) | Video-detail records saved to the dataset. |
| `apify-actor-start` | $0.00005 per start event | Apify counts one event per GB of allocated memory, with a minimum of one event. |

At the current default 4 GB, the start fee is $0.00020; 100 search results therefore cost $0.40020 in Actor events. Use the Pricing tab for current prices and check the memory allocation before running. There is no additional automatic dataset-item fee. Platform compute and Apify proxy usage during the run are included; later storage, API use or integrations follow your account plan.

The start fee can apply even when input is invalid, requests are blocked or no results are returned. Dropped videos have no result fee. Rows already saved remain billable if the run later fails; inspect `RUN_SUMMARY` and the dataset before retrying. Set a maximum charge limit per run. Budget checks and writes are serialized across workers. A run with existing output cannot be resumed or resurrected; start a new run and treat it as a separate, billable collection.

### FAQ

**Will it get blocked?** Sometimes the search API refuses a request even with the right headers. In the verification runs of 2026-11-04 the API answered normally until about 15:30 KST (after roughly 55 API requests from the test machine in the previous hour, including a 31-page burst), then answered `code 0` with no results and only a risk-control voucher, first for some and then for every request from that machine's IP, and for 5 of 7 requests through a US residential exit.
The Actor treats that "soft block", HTTP 412 and the JSON codes -352 / -403 as a block: it retries on a fresh proxy session (a new exit IP), then switches to the server-rendered search page, then to a browser. The runs during the soft-block period still completed from the server-rendered page. Every retry and switch is logged and counted in `RUN_SUMMARY` (`blockedEvents`, `retries`, `pathPages`).
Video documents: 30 requests through the US residential exit, one HTTP 412, which succeeded on the automatic retry. A run never crashes because of one keyword; if no item at all is produced it ends with an error and a clear message.

**How fast is it?** Measured on 2026-11-04 through a US residential exit with the default concurrency of 3: 100 search results in 12.6 s (and 35.1 s in a later run: the speed of the residential exit varies a lot), 977 results for one keyword in 63 s, 12 video documents in 32 s (22.7 s in a later run).
From a home connection without a proxy (KR) the same 100 results took 1.8 s.

**How many items per keyword?** Up to 1,000. Bilibili exposes at most about 1,000 results per keyword (24 pages of 42 rows) and pages overlap, so a broad keyword returns a bit fewer unique videos: 977 for `原神` through the API.
If the API is refusing and the Actor falls back to the server-rendered page, that page is re-ranked on every load and overlaps more: the one 24-page test returned 662 unique videos for `原神` (346 duplicates removed). `RUN_SUMMARY.perKey[].windowLimited`, `itemsDuplicate` and `pathPages` show which case you got.

**Which countries and proxies?** The default is Apify residential with a US exit and all verification used it. Datacenter proxies served search but were refused by video pages with HTTP 412 in the test.
Other residential countries are untested (experimental).

**Is the text in English?** No. Bilibili serves Chinese; the Actor asks for the Chinese version explicitly so titles, descriptions and tags are not machine-translated.

**Are comments, uploader pages or danmaku included?** No. Anonymous access to comments is capped by the site, so comments are not part of this Actor. Uploader-page collection is not supported.

**What happens to deleted or invisible videos?** They appear in `RUN_SUMMARY.failed` with a reason such as `video-not-found` and are not charged.

### Legal note

The Actor reads public web pages and public API responses without logging in. It collects video metadata only: no comments, no commenter data, no private account data. Public uploader names, IDs and URLs are included and can identify individuals.
Uploader name, id and URL are part of the public video record. You are responsible for using the data lawfully (copyright, platform terms, privacy and data-protection law in your jurisdiction).

### Changelog

2026-11-06: clarified start fees and partial-run billing, serialized budget checks and writes, and blocked restarts that already contain output. Historical measurements above describe specific test runs; they are not speed or availability guarantees.

# Changelog

This Actor's version history is a separate document: https://apify.com/cemented_jest/bilibili-scraper/changelog.md

# Actor input Schema

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

`search`: keywords in, matching videos out (event `video-result`). `video`: BV ids / av ids / video URLs in, full video metadata out, including coin and share counts (event `video-detail`).

## `keywords` (type: `array`):

Search phrases, 1 to 50, used when Mode is `search`. Chinese keywords return the best results. Each keyword is searched separately; `maxItems` applies per keyword.

## `order` (type: `string`):

`relevance` is the default ranking (no sort parameter). `pubdate` = newest first, `click` = most played, `dm` = most danmaku and `stow` = most favorited: each was checked with real requests on 2026-11-04 (one keyword, 42-50 results, no order violation).

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

How many videos to return for each keyword, 1 to 1,000. Bilibili serves at most about 1,000 results per keyword and the list overlaps between pages, so a broad keyword typically yields fewer than 1,000 unique videos; see `windowLimited` in the RUN_SUMMARY record.

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

BV ids (`BV1c4411e77t`), av ids (`av52080698`) or video URLs (`https://www.bilibili.com/video/BV1c4411e77t`), 1 to 1,000, used when Mode is `video`. Duplicates (the same video given twice, for example as BV and av) are returned and charged once.

## `maxConcurrency` (type: `integer`):

Keywords or videos processed in parallel, 1 to 10. Requests are also spaced out; raising this does not make single keywords faster.

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

Proxy used for all requests. Default: Apify residential proxy with a US exit, the configuration that was tested for both modes. Datacenter proxies are refused by Bilibili video pages (HTTP 412), so video mode needs residential IPs.

## `debug` (type: `boolean`):

Verbose logging, and the body of every response that could not be parsed is saved to the key-value store (DEBUG\_\* records).

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "python 教程"
  ],
  "order": "relevance",
  "maxItems": 100,
  "videoUrls": [
    "BV1c4411e77t",
    "https://www.bilibili.com/video/BV1xx411c7mD"
  ],
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "debug": false
}
```

# Actor output Schema

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

No description

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

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "python 教程"
    ],
    "videoUrls": [
        "BV1c4411e77t",
        "https://www.bilibili.com/video/BV1xx411c7mD"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cemented_jest/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 = {
    "keywords": ["python 教程"],
    "videoUrls": [
        "BV1c4411e77t",
        "https://www.bilibili.com/video/BV1xx411c7mD",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("cemented_jest/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 '{
  "keywords": [
    "python 教程"
  ],
  "videoUrls": [
    "BV1c4411e77t",
    "https://www.bilibili.com/video/BV1xx411c7mD"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call cemented_jest/bilibili-scraper --silent --output-dataset

```

## MCP server setup

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