# Bilibili Scraper — Videos, Comments, Danmaku & Stats (`yu_ovo/bilibili-scraper`) Actor

Extract Bilibili (B站) data by keyword, video URL or uploader: plays, likes, coins, favorites, tags and dates. Add danmaku (弹幕) with exact timestamps and colour - no login. Comments beyond 3 per video need a one-time QR login.

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

## Pricing

from $10.00 / 1,000 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 — videos, comments, danmaku, UP主 stats and tags

Extract data from **Bilibili (哔哩哔哩 / B站)** videos, search results, comment threads and **danmaku (弹幕)**. Use **keywords** or **video URLs** to scrape titles, uploaders, plays, danmaku, likes, coins, favorites, shares, exact comment counts, tags, descriptions and top comments. Export as JSON, CSV, Excel or XML, run it via API, schedule it, or connect it to any AI agent through the Apify MCP server.

**Videos, stats, tags and search: no login, no cookies, no API key, no `wbi` signature to renew.**

**Danmaku (弹幕) need no login either** — the timed bullet comments come back for free, with the exact second each one appears.

**Comments are the exception.** Bilibili hands out only **3 comments per video** to anonymous requests — that is a platform rule, not a limit of this Actor. To get more, scan a QR code with the free Bilibili mobile app once; the session is remembered and every later run is hands-free. See [How to log in](#how-to-log-in-to-bilibili-scan-a-qr---no-devtools).

#### What does Bilibili Scraper do?

- Search **chosen keywords** on Bilibili and collect every video result
- Scrape **specific videos** by URL or BVID — no search needed
- Scrape **every video from an uploader (UP主)** — works best with your own Bilibili cookie, see the FAQ
- Get **full engagement stats** per video: plays, danmaku, likes, coins, favorites, shares, comment count
- Collect **top comments** per video with author, text, likes and reply count
- Collect **danmaku (弹幕)** with the exact second, colour and mode — **no login needed**
- Pull **tags and the full description** from each video page
- Sort results by **relevance, most played, newest, most danmaku or most favorited**
- De-duplicate automatically across keywords and URL lists
- Export in multiple formats: **JSON, CSV, Excel, XML**
- Connect to any AI agent through the **Apify MCP server**, or call it from the **Python and JavaScript SDKs**, **REST API** or **webhooks**
- **No login and no cookies** for video, search and uploader data — everything runs from the browser session the Actor opens itself. Comments beyond 3 per video need a one-time scan.

#### What data can you get from Bilibili?

<table>
<tr>
<td>👤 Uploader name</td>
<td>🆔 Uploader ID</td>
<td>🔗 Channel URL</td>
</tr>
<tr>
<td>▶️ Plays</td>
<td>💬 Danmaku</td>
<td>❤️ Likes</td>
</tr>
<tr>
<td>🪙 Coins</td>
<td>⭐ Favorites</td>
<td>🔁 Shares</td>
</tr>
<tr>
<td>📝 Full description</td>
<td>🏷️ Tags</td>
<td>🖼️ Cover image URL</td>
</tr>
<tr>
<td>⏱️ Duration (text + seconds)</td>
<td>📅 Publish date + exact timestamp</td>
<td>🆔 BVID + numeric AID</td>
</tr>
<tr>
<td>💬 Total comment count</td>
<td>🗣️ Top comments with likes</td>
<td>🔍 Keyword that found it</td>
</tr>
</table>

#### How to scrape Bilibili with Bilibili Scraper?

Five steps, no coding:

1. [Create](https://console.apify.com/sign-up) a free Apify account with your email.
2. Open [Bilibili Scraper](https://apify.com/).
3. Add the **keywords** you want to search, or paste **video URLs**.
4. Click **Start** and wait for the run to finish.
5. Download your data as **JSON, CSV, Excel or XML** from the Output tab.

#### Input

Two ways in — use either or both:

- **keywords:** search Bilibili and collect the video results
- **videoUrls:** scrape these exact videos by URL or BVID

```json
{
  "keywords": ["ai", "原神"],
  "videoUrls": ["https://www.bilibili.com/video/BV1VZWc6dECG/"],
  "searchType": "video",
  "sortBy": "most_played",
  "maxItemsPerKeyword": 100,
  "maxPagesPerKeyword": 10,
  "includeStats": true,
  "includeComments": false,
  "maxCommentsPerVideo": 20,
  "commentSort": "hot"
}
```

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array | — | Search terms. One run can take many |
| `videoUrls` | array | — | Full URLs or bare BVIDs |
| `userUrls` | array | — | Uploader space URLs or numeric IDs |
| `bilibiliCookie` | string | — | Optional. Paste a cookie only if you do not want to scan |
| `loginTimeoutSeconds` | integer | `0` | How long to keep a login QR available (0 = never ask; the code refreshes itself every ~3 min) |
| `resetStoredSession` | boolean | `false` | Delete the saved login and ask for a fresh QR |
| `failWithoutLogin` | boolean | `false` | If the scan never happens, stop instead of returning partial data |
| `searchType` | string | `video` | `video` or `all` |
| `sortBy` | string | `relevance` | `relevance`, `most_played`, `newest`, `most_danmaku`, `most_favorited` |
| `maxItemsPerKeyword` | integer | `100` | Videos per keyword (1–5000) |
| `maxPagesPerKeyword` | integer | `10` | Result pages per keyword (≈40 videos each) |
| `includeStats` | boolean | `true` | Attach likes, coins, favorites, shares, comment count, description, tags |
| `includeComments` | boolean | `false` | Also collect top-level comments (3 per video without a login) |
| `maxCommentsPerVideo` | integer | `20` | Comments per video (0–200). Anonymous Bilibili returns **3** no matter what this says — log in for more |
| `includeDanmaku` | boolean | `false` | Also collect danmaku (弹幕). **No login required** |
| `maxDanmakuPerVideo` | integer | `200` | Danmaku per video (0–5000). Each danmaku is one billed result row |
| `commentSort` | string | `hot` | `hot` (most liked) or `new` (newest) |

At least one of `keywords`, `videoUrls` or `userUrls` is required.

#### Output

Results land in a dataset. Video, comment and danmaku rows share it — split them on `record_type`.

**Danmaku row**

```json
{
  "record_type": "danmaku",
  "bvid": "BV1VZWc6dECG",
  "title": "【Re:从零开始的AI学习】第一季总集篇",
  "text": "不要 Fear Of Missing Out (FOMO)！",
  "time_in_video": 2754.324,
  "mode": 1,
  "font_size": 25,
  "color": "#ffffff",
  "sent_at": "2026-10-24T18:12:45+00:00",
  "dmid": "2225301461351363840"
}
```

**You don't have to pick one or the other.** Turn on `includeComments` and a single run returns
the videos *and* their comments. Three ready-made table views are included:

| View | Shows | Use it when |
|---|---|---|
| **Overview** | video rows only | you just want the videos |
| **Comments** | comment rows only | you just want the comments |
| **Videos + comments (one table)** | both, in one grid | you want to read a video next to what people said under it |
| **Danmaku (弹幕)** | danmaku rows only | you are studying reactions at a specific moment |

Every comment row carries its parent video's `title`, `play_count`, `comment_count` and `url`,
so it stands on its own in a spreadsheet, a pivot table or a database — no join needed. Only
`message` is empty on video rows, because a video has no comment text.

**Video row**

```json
{
  "record_type": "video",
  "bvid": "BV1VZWc6dECG",
  "aid": 116893619560671,
  "url": "https://www.bilibili.com/video/BV1VZWc6dECG/",
  "title": "【Re:从零开始的AI学习】第一季总集篇｜从入门到用好 AI",
  "author": "卡普迪姆",
  "author_mid": 312249633,
  "author_url": "https://space.bilibili.com/312249633",
  "cover_url": "https://i0.hdslb.com/bfs/archive/0bc4fd83ef1159aa3bc3454ec0a477e161191f.jpg",
  "play_count": 940040,
  "danmaku_count": 4316,
  "like_count": 72654,
  "coin_count": 21456,
  "favorite_count": 88120,
  "share_count": 7304,
  "comment_count": 1892,
  "duration_text": "54:57",
  "duration_seconds": 3297,
  "publish_date": "2026-10-13",
  "publish_datetime": "2026-10-13T00:57:21+00:00",
  "description": "从零开始学AI……",
  "tags": "AI开源先锋计划, 科普, AI, 人工智能, B 站大学, 大模型, Agent",
  "keyword": "ai",
  "scraped_at": "2026-10-25T05:02:11.883Z"
}
```

**Comment row**

```json
{
  "record_type": "comment",
  "bvid": "BV1VZWc6dECG",
  "aid": 116893619560671,
  "comment_id": 2345678901,
  "parent_id": 0,
  "author": "老崔有话说",
  "author_mid": 12345678,
  "message": "讲得太清楚了，收藏了慢慢看",
  "like_count": 15,
  "reply_count": 3,
  "publish_date": "2026-10-24T14:23:56+00:00",
  "keyword": "ai",
  "scraped_at": "2026-10-25T05:02:11.883Z"
}
```

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

Pay per result — **only for rows actually written to the dataset**. A run that finds nothing costs you almost nothing.

- **$5 per 1,000 results** ($0.005 per row) and a negligible per-run start fee.
- Every row counts as one result: 20 videos plus 200 comments is 220 results.
- Apify additionally bills the run's own compute time (one 2 GB browser). Measured, a 25-video run cost **$0.0036** in compute — a fraction of a cent, not a hidden fee.
- Cap spend yourself with `maxItemsPerKeyword` or the run's maximum-cost setting. Comments are charged as ordinary rows, so decide deliberately before switching them on.

#### Integrate with your stack

- **REST API** — start a run and read the dataset with any HTTP client
- **Python / JavaScript SDKs** — `pip install apify-client` / `npm i apify-client`
- **MCP server** — point any AI agent at `https://mcp.apify.com` and let it call this Actor
- **Webhooks and schedules** — run it daily and diff on `bvid` to catch new uploads
- **No-code** — Google Sheets, Zapier, Make and the other Apify integrations

#### Why a real browser

Bilibili renders search results with JavaScript and rotates an internal API signature (`wbi`) that breaks lightweight scrapers within weeks. This Actor opens Bilibili's homepage so the site issues its own cookies, reads the rendered result cards, then calls Bilibili's view and reply endpoints **through that same browser session**. No reverse-engineered signature, no `xsec_token` chasing.

#### Bilibili scraper comparison

| | This Actor | Typical Bilibili scrapers |
|---|---|---|
| Login required | **Videos: never. Comments: one QR scan, reused on every later run** | Sometimes, with no explanation |
| Search by keyword | **Yes** | Yes |
| Scrape exact video URLs | **Yes** | Rarely |
| Likes / coins / favorites / shares | **Exact integers** | Usually missing |
| Comments | **Optional, priced per row** | Text only, if at all |
| Danmaku (弹幕) | **Yes, with timestamps and colour — no login** | Almost never |
| Tags and description | **Yes** | Rarely |
| Play count | **Integer (`940040`)** | Rounded label (`94.0万`) |
| De-duplication | **Automatic** | Rarely |
| Price | **$5 / 1,000 results** — the same as the category leader | $5 – $20 / 1,000 |

#### How to log in to Bilibili (scan a QR - no DevTools)

A login is needed for uploader (`userUrls`) scraping, and for comments beyond 3 per video.

**You never have to hunt for a cookie.** The Actor draws a QR code and puts directions in three places, so you cannot miss it.

1. Start the run. The run's **status line** immediately changes to:

   > `SCAN TO LOG IN: open the Output tab and click 'Login QR code', then scan it with the Bilibili app`

   That line is visible on the run page and in the Runs list — no clicking needed to read it.

2. Open the **Output tab** and click **Login QR code**. Three routes reach the same picture:

   | Where | What you see | Good for |
   |---|---|---|
   | **The run's Output tab** | An output called **Login QR code** | The route the status line tells you to take |
   | **The run log** | A ready-to-click link ending in `records/LOGIN_QR` | Fastest if you are already reading the log |
   | **The run's Storage tab** → **Key-value store** | A record named **`LOGIN_QR`** | If you closed the other two |

3. Scan the picture with the **Bilibili mobile app** and tap confirm.

4. The run continues on its own — no restart needed.

> The link changes every run, so always take it from the run you are watching.

**You only scan once.** After a successful scan the session is saved to a private key-value store named `bilibili-session` in your own Apify account, and every later run reuses it automatically — no scan, no cookie.

The Actor waits up to `loginTimeoutSeconds` for the scan. The default is `0` (never block); set it to `180` to be shown a QR. If nobody scans, it carries on anonymously, says so in the log and in `SUMMARY`, and — when `failWithoutLogin` is on — stops instead of returning partial data.

If the saved login ever stops working (you changed your password, logged out on the phone, or Bilibili expired it), the next run detects that, says so, and shows a fresh QR code. To force a re-login at any time, tick **Forget the saved Bilibili login** (`resetStoredSession`).

**Why a QR and not a password?** The Actor never sees, asks for, or stores your password. The session it receives is the same one your phone uses, and you can kill it any time by logging out in the app.

##### Advanced: paste a cookie yourself instead

If you prefer to run unattended with no scan, you can supply the cookie up front. Get it from your own browser:

1. Log in at [bilibili.com](https://www.bilibili.com) in Chrome, press **F12**.
2. Open the **Application** tab — it is often hidden behind the **»** at the right end of the tab row.
3. Under **Storage → Cookies**, click **`https://www.bilibili.com`**.
4. Double-click the **`SESSDATA`** row's `value` cell, then Ctrl+C (⌘C on Mac).

```
┌─ DevTools ───────────────────────────────────────────────────────────────┐
│                                                                          │
│  Elements  Console  Sources  Network  »  [Application]                   │
│                                           ↑ click here                   │
│                                                                          │
│  ▼ Storage                                                               │
│      Cookies  ▸                                                          │
│          https://www.bilibili.com   ← click this                         │
│                                                                          │
│      name        value                     domain                        │
│      ───────────────────────────────────────────────────                 │
│      _uuid       8B2A…                     .bilibili.com                 │
│      buvid3      F1C2…                     .bilibili.com                 │
│      SESSDATA    7f3c9a4e2b1d8c05…         .bilibili.com  ← this one     │
│      bili_jct    9d41…                     .bilibili.com                 │
│      DedeUserID  2058285…                  .bilibili.com                 │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘
```

5. Paste it into `bilibiliCookie` with the field name attached:

```
✅ SESSDATA=<paste-your-value-here>
❌ <paste-your-value-here>                (no field name)
❌ "SESSDATA=<paste-your-value-here>"     (quoted)
```

**If this feels like too much, skip it.** Use `keywords` instead — searching for the creator's name usually surfaces most of their videos, and it needs no login at all.

#### FAQ

**Do I need a Bilibili account?**

Depends on what you want:

| You want | Account needed |
|---|---|
| Videos, search, uploader lists, plays, likes, coins, favorites, tags | **No.** Nothing to sign up for, nothing to renew |
| More than **3 comments per video** | **Yes** — one free scan, remembered forever |

Bilibili caps anonymous comment access at 3 per video. That is their rule, and no amount of retrying, proxying or endpoint-hopping changes it — only a logged-in session does.

**What if my keyword has no matches?**

Bilibili does not return an empty page — it falls back to fuzzy matches and recommended videos. Searching a nonsense term still produced five rows in testing, two of which happened to contain part of the query. That is Bilibili's own ranking, and those rows are billed like any other result. Filter on `title` or `play_count` in your spreadsheet if you need to drop them.

**Can I scrape just a few specific videos?**
Yes — put the URLs or BVIDs in `videoUrls` and leave `keywords` empty. Stats are always fetched in that mode.

**Why do stats differ from the search card?**
The search card shows a rounded cached label (`94.0万`); the stat block is the live counter from the video itself. Stats are accurate.

**How many comments can I get?**

**Without logging in: 3 per video.** That is a hard Bilibili limit, not a setting. Asking for 200 comments per video changes nothing while the run is anonymous — Bilibili answers `HTTP 200` with a `reply` count of thousands and then hands back exactly three replies.

To get more:

- **Log in.** Set `QR login wait (seconds)` above 0 (for example `300`) and scan the QR code with the Bilibili app. A logged-in session is what lifts the ceiling, up to `maxCommentsPerVideo` per video.
- **Or paste a cookie** into `bilibiliCookie` if you already have one.

Once logged in, Bilibili serves **20 comments per request** (`?ps=21` and above is rejected with `ps out of bounds`), and the Actor pages through them until it reaches `maxCommentsPerVideo` or hits Bilibili's rate limit. Per video the Actor reads at most **60 pages = 1,200 comments**; top-level comments only, `reply_count` tells you how many replies each has.

**Why the number looks smaller than the video's comment count.** Bilibili's comment count adds top-level comments *and* their replies together. Measured on two videos with a logged-in session: 30 top-level + 50 replies = the 80 Bilibili displays, and 23 + 29 = the 52 displayed. The Actor returned all 30 and all 23 — i.e. **every top-level comment**, not a truncated sample. Use `reply_count` per row if you need to know how deep a thread is.

Two more things worth knowing:

- Bilibili meters its comment endpoints separately, and the paged endpoint starts answering `HTTP 412` after a few hundred requests in a run.
- If both endpoints get rate-limited, comments stop for that run and `SUMMARY.commentNote` explains why. **Video rows are never affected** — comment scraping is deliberately independent of video scraping.
- When a run comes back with ≤3 comments per video, `SUMMARY.commentNote` tells you the anonymous cap is the reason, so you are never left guessing.
- **If you skip the scan**, the default behaviour is to carry on anonymously and return what it can. Set `failWithoutLogin` to `true` if you would rather the run stop with an error than hand you partial data.

If you do not have a Bilibili account, use this Actor for **video and stats data** (which needs no login at all) and treat comments as a bonus of 3 top comments per video.

**Can I scrape all videos from an uploader (UP主)?**

Yes — put their space URL (`https://space.bilibili.com/123456`) or bare numeric ID in `userUrls`. The run shows a QR code for you to scan with the Bilibili app; see [How to log in](#how-to-log-in-to-bilibili-scan-a-qr---no-devtools) above.

**This mode needs that login.** Anonymous requests to Bilibili's uploader endpoint get `-799` (too frequent) or `-352` (risk check), which is why a scan is required here and not for keyword search.

**Large and official channels work too.** Bilibili throttles the legacy uploader endpoint hard for big accounts — a 20-million-follower official channel answers `-799` (too frequent) to every retry on it. This Actor therefore calls the signed endpoint the Bilibili web app itself uses (`x/space/wbi/arc/search`), and only falls back to the legacy one. Measured: that same official channel returned **50 videos in 74 s**, then **80 videos across two pages in 39 s**, all unique BVIDs.

If both endpoints ever refuse a channel, the run does not pretend it is empty: it logs an error, sets `SUMMARY.uploadersFailed` to the affected IDs, and explains why in `SUMMARY.uploaderNote`.

**Why not just solve the captcha?**

Because the captcha is not the real gate. Blocked requests come back as server-side risk control (`-799` request-too-frequent, `-352` risk check failed) — no image puzzle is ever shown for those. A logged-in session is what actually changes the outcome.

**Can I scrape danmaku (弹幕)?**

Yes, and unlike comments it needs **no login at all**. Turn on `includeDanmaku` and the Actor reads the video's danmaku XML directly, so you get the text, the exact second it appears (`time_in_video`), its colour, its mode and when it was sent. Set `maxDanmakuPerVideo` to control volume — a popular video can have thousands, and each danmaku is billed as one result row, so 200 is a sensible starting point.

**Can I get replies to comments?**
Not currently — top-level comments only. `reply_count` tells you how many replies each has.

**Is scraping Bilibili allowed?**
This Actor collects only publicly visible data. You are responsible for complying with Bilibili's terms of service and applicable law in your jurisdiction.

#### Something not working? Two ways to send us the run

Every run keeps a full log in the Apify console. Pick whichever is easier:

**Option A - send the log file (best)**

1. Open **Actors → Bilibili Scraper → Runs** and click the run that failed.
2. Open the **Log** tab (there is a download icon at its top right).
3. Save the `.txt` and send that file.

**Option B - send the diagnostics link plus the log tail**

1. Open the run's **Output** tab. The **Diagnostics** output is a signed link that opens **without any token** — paste that link as-is, or copy the whole `DIAGNOSTICS` JSON from the key-value store. It has the run ID, the input that was used, the row counts and the error flags.
2. Copy the last ~50 lines of the **Log** tab. The log itself is private to your account, so it cannot be linked — only copied or downloaded.

> The signed links in the Output tab expose your scraped data to anyone holding the URL. Share them deliberately.

Either way we get everything needed. You do not have to send screenshots.

> **Note on privacy:** the run log is private to your Apify account — the API URL for it needs your token. That is why the log has to be downloaded or copied rather than linked. `DIAGNOSTICS` never contains your `bilibiliCookie`.

##### What the usual symptoms mean

| Symptom | What actually happened | What to do |
|---|---|---|
| 0 videos | The keyword returned nothing, or everything was already seen | Try a different keyword; the log says "N new videos" per page |
| Videos fine, **3 comments per video** | That is Bilibili's anonymous limit — not a bug | Set `QR login wait` above 0 and scan the QR to get more |
| Videos fine, **0 comments** | Bilibili's comment quota ran out (HTTP 412) | Run comments in smaller batches, or accept the top-3 fallback |
| Uploader mode returns nothing | Nobody scanned the QR in time | Raise `loginTimeoutSeconds`, or paste a `bilibiliCookie` |
| Log says "stored session is no longer valid" | Bilibili expired your login | It shows a fresh QR — scan it again |
| Run fails immediately | Input problem | The log names the exact field |

#### Keywords

bilibili scraper, b站 scraper, 哔哩哔哩, bilibili danmaku, 弹幕, bullet comments, 弹幕爬虫, bilibili search scraper, bilibili video scraper, bilibili comments scraper, bilibili api, bilibili data, bilibili up主, bilibili likes, bilibili stats, bilibili tags, bilibili dataset, chinese video platform data, bilibili analytics, bilibili export csv, bilibili json, bilibili up主 videos, bilibili uploader scraper, bilibili creator videos

# Changelog

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

# Actor input Schema

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

Terms to search on Bilibili. Each keyword is scraped separately; duplicates across keywords are dropped.

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

Scrape these exact videos instead of searching. Accepts full URLs (https://www.bilibili.com/video/BV...) or bare BVIDs. Combine with keywords or use alone.

## `userUrls` (type: `array`):

Scrape every video from these Bilibili uploaders (UP主). Accepts space URLs (https://space.bilibili.com/123456) or bare numeric IDs. Requires a login - Bilibili answers -799/-352 to anonymous requests for this endpoint, so set loginTimeoutSeconds above 0 and scan the QR.

## `searchType` (type: `string`):

Which Bilibili search tab to use. 'video' is fastest and returns one row per video.

## `sortBy` (type: `string`):

How Bilibili should order the results before you scrape them.

## `maxItemsPerKeyword` (type: `integer`):

Stop after this many new videos per keyword - or per uploader when you use userUrls. This is the main lever on run cost.

## `maxPagesPerKeyword` (type: `integer`):

Hard cap on how many result pages to open per keyword or per uploader (each search page holds roughly 40 videos, each uploader page 50). Raising it costs nothing when maxItemsPerKeyword is reached first.

## `includeStats` (type: `boolean`):

Also read each video's own page data to attach exact likes, coins, favorites, shares, comment count, full description, tags and category. One extra request per video.

## `includeComments` (type: `boolean`):

Also collect top comments for each video. Adds one row per comment and increases run time and cost. Anonymous Bilibili returns 3 comments per video; log in for more.

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

How many top-level comments to collect for each video when comments are enabled. Important: Bilibili only serves 3 comments per video to anonymous requests. To go beyond 3 you must be logged in - set 'QR login wait' above 0 and scan the code.

## `commentSort` (type: `string`):

Order comments by popularity or by time.

## `includeDanmaku` (type: `boolean`):

Also collect the danmaku (bullet comments) of each video: the text, the exact second it appears, its colour and mode. Unlike normal comments, danmaku need NO login. Each danmaku is one dataset row, so it is charged like any other result.

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

How many danmaku to keep per video. Popular videos have thousands; Bilibili serves the current pool in one XML document. Each danmaku is billed as a normal result row, so raise this deliberately.

## `bilibiliCookie` (type: `string`):

Optional. Your own logged-in Bilibili cookie, e.g. SESSDATA=xxx. You usually do not need this - leave it empty and the Actor will show a QR code to scan instead.

## `loginTimeoutSeconds` (type: `integer`):

How long to keep a login QR available. The code refreshes itself every ~3 minutes, so a long wait is safe. Uploader (UP主) scraping needs a login and always shows a QR. Comments also need a login to return more than 3 per video: set this above 0 to be shown a QR. 0 = stay anonymous and never block. If the scan never happens the run continues anonymously and says so, unless failWithoutLogin is on.

## `resetStoredSession` (type: `boolean`):

Tick to delete the session saved by a previous run, so the next uploader run asks for a fresh QR scan. Use this if you switched accounts or the saved login stopped working.

## `failWithoutLogin` (type: `boolean`):

ON: if a QR login is requested but not completed, the run fails with a clear message instead of returning partial data. Use it when the promise is 'all comments'. OFF (default): the run continues anonymously and returns whatever Bilibili allows (3 comments per video), and says so in SUMMARY.commentNote.

## Actor input object example

```json
{
  "keywords": [],
  "videoUrls": [],
  "userUrls": [],
  "searchType": "video",
  "sortBy": "relevance",
  "maxItemsPerKeyword": 100,
  "maxPagesPerKeyword": 10,
  "includeStats": true,
  "includeComments": false,
  "maxCommentsPerVideo": 20,
  "commentSort": "hot",
  "includeDanmaku": false,
  "maxDanmakuPerVideo": 200,
  "loginTimeoutSeconds": 0,
  "resetStoredSession": false,
  "failWithoutLogin": false
}
```

# Actor output Schema

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

One row per video (record_type=video) and, when includeComments is on, one row per comment (record_type=comment). Split them on record_type.

## `loginQr` (type: `string`):

If a run needs a Bilibili login, a QR code is generated here. Open this link, scan the picture with the Bilibili mobile app and tap confirm - the run continues on its own. Not produced when no login was needed.

## `diagnostics` (type: `string`):

Run ID, the input that was used, row counts and error flags (comment quota, charge limit, reused session). Useful for support and for AI agents chaining this Actor.

# 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": [],
    "videoUrls": [],
    "userUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("yu_ovo/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": [],
    "videoUrls": [],
    "userUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("yu_ovo/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": [],
  "videoUrls": [],
  "userUrls": []
}' |
apify call yu_ovo/bilibili-scraper --silent --output-dataset

```

## MCP server setup

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