# Bilibili Video Metrics Tracker - Views, Likes, Coins, Changes (`magenta_courser/bilibili-video-tracker`) Actor

Paste Bilibili video links and get views, likes, coins, favorites, shares and comments per video, with the change since your last run. Exact integers from Bilibili's public API, no login, one flat row per link.

- **URL**: https://apify.com/magenta_courser/bilibili-video-tracker.md
- **Developed by:** [SUNGHWAN CHO](https://apify.com/magenta_courser) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Video Metrics Tracker

Paste Bilibili video links and get views, likes, coins, favorites, shares and comments per video, with the change since your last run. One flat row per link, every count an **exact integer** straight from Bilibili's public API (no "1.2万" rounding), plus the uploader's follower count. Run it daily on a schedule and each row tells you how much every number moved since the last run.

No browser, no login, no account. Only the public JSON that bilibili.com itself loads for any visitor.

### What you can do with it

- **Campaign and sponsorship reporting**: track the videos you paid for and report exact views, likes, coins (投币), favorites (收藏), shares and comments per day.
- **Creator monitoring**: watch your own or competitors' uploads and see which ones are still growing.
- **Influencer vetting**: uploader follower count next to each video's engagement.
- **Content research**: exact engagement numbers for any list of public videos, ready for a spreadsheet or an LLM agent.

### Input

| Field | What it does |
|---|---|
| `videoUrls` | Video links or ids, one per line. Accepted: `https://www.bilibili.com/video/BV1CUso6AELi/`, mobile links, `https://b23.tv/xxxxxxx` short links, `https://www.bilibili.com/video/av170001/`, bare `BV1CUso6AELi` or `av170001`. Up to 10,000 per run. **Required.** |
| `includeOwnerStats` | Add `ownerFollowerCount` (one extra request per unique uploader, cached within the run). Default on. |
| `compareWithLastRun` | Add `previous*`, `*Change` and `isFirstSeen` fields compared with the last run that used the same history store. Default on. |
| `outputMode` | `all` (default) or `changesOnly` - save and pay only for videos whose numbers moved since the last run (first-seen videos are always saved). |
| `historyStoreName` | Named key-value store in your account that keeps the last numbers per video. One name per watch list. Default `bilibili-video-history`. |
| `proxyConfiguration` | Apify datacenter proxy is enough (default). After a Bilibili block the retry goes through residential proxy when your plan has it. |
| `maxRequests`, `maxRunMinutes` | Safety caps per run. |

Example:

```json
{
  "videoUrls": [
    "https://www.bilibili.com/video/BV1CUso6AELi/",
    "https://b23.tv/BV14txF65Efd",
    "av170001"
  ],
  "outputMode": "all"
}
```

### Output

Exactly one row per video link, even when the video could not be read (`status` tells you why, and such rows are free). Every row has every field; values Bilibili did not give are `null`.

```json
{
  "bvid": "BV1CUso6AELi",
  "aid": 117523072682594,
  "inputUrl": "https://www.bilibili.com/video/BV1CUso6AELi/",
  "status": "ok",
  "statusMessage": null,
  "title": "【独家】《罗小黑战记》第41话 开始学习吧小黑同学【10月国创】",
  "description": "每周五12:00，B站独播！",
  "category": null,
  "categoryId": 153,
  "publishedAt": "2026-10-30T04:00:02.000Z",
  "durationSec": 687,
  "ownerId": 98627270,
  "ownerName": "哔哩哔哩国创",
  "ownerUrl": "https://space.bilibili.com/98627270",
  "ownerFollowerCount": 7840660,
  "viewCount": 4091337,
  "likeCount": 104268,
  "coinCount": 92765,
  "favoriteCount": 30558,
  "shareCount": 24766,
  "commentCount": 13738,
  "danmakuCount": 119145,
  "url": "https://www.bilibili.com/video/BV1CUso6AELi/",
  "thumbnailUrl": "https://i1.hdslb.com/bfs/archive/f2b4cdbae66af01348838b963b9d5040402000e2.png",
  "scrapedAt": "2026-10-31T10:00:00.000Z",
  "previousScrapedAt": "2026-10-30T10:00:00.000Z",
  "previousViewCount": 3900120,
  "viewChange": 191217,
  "previousLikeCount": 101000,
  "likeChange": 3268,
  "previousCoinCount": 90000,
  "coinChange": 2765,
  "previousFavoriteCount": 30000,
  "favoriteChange": 558,
  "previousShareCount": 24000,
  "shareChange": 766,
  "previousCommentCount": 13000,
  "commentChange": 738,
  "previousDanmakuCount": 118000,
  "danmakuChange": 1145,
  "previousOwnerFollowerCount": 7838000,
  "ownerFollowerChange": 2660,
  "isFirstSeen": false,
  "source": "bilibili",
  "schemaVersion": 1
}
```

Field notes:

- `status`: `ok` (data row, charged), `not_found` (deleted, private or never existed), `blocked` (Bilibili refused every proxy session tried), `error` (unreadable input, or the run stopped before this video). `statusMessage` says what to do next.
- All counts are the exact integers Bilibili's API reports (`stat.view`, `stat.like`, `stat.coin`, `stat.favorite`, `stat.share`, `stat.reply`, `stat.danmaku`). `commentCount` is Bilibili's `reply` count (top-level comments plus replies).
- `*Change` = this run minus the previous run for the same video; `null` on the first run or when one side is missing. `isFirstSeen` is `true` the first time a video is seen in this history store.
- `categoryId` is Bilibili's partition id (分区 `tid`, e.g. 153 = 国创 / Chinese animation). `category` is the partition name when the API sends it; at the moment the public endpoint leaves the name empty for most videos, so expect `null` there and use `categoryId`.
- `publishedAt` is UTC (Bilibili publishes in Beijing time, UTC+8).
- Duplicate links to the same video are merged into one row. A b23.tv short link costs one extra request to read its redirect.
- A run summary (counts per status, saved rows, request/retry/block counters, stop reason) is saved as the `RUN_SUMMARY` record in the run's key-value store. It is not billed.

### Pricing

Pay per event: you pay for each `ok` video row saved (`video`). Rows with status `not_found`, `blocked` or `error`, and the run summary, are free. With `outputMode: changesOnly`, unchanged videos are not saved and not charged. The run stops cleanly at your "Maximum cost per run".

### Daily tracking

1. Put your watch list in `videoUrls` and run once (first run: `isFirstSeen: true`, no changes yet).
2. Schedule the same input daily. Every row now has `previousScrapedAt` and the exact differences.
3. For alerting or cheaper monitoring set `outputMode: changesOnly`: on Bilibili almost every public video moves a little each day, so this mostly filters out dead videos; the main saving is on long watch lists of old uploads.
4. Use a different `historyStoreName` for each separate watch list so they do not overwrite each other.

### How it works

For each link the Actor calls Bilibili's public video endpoint (`/x/web-interface/view`) and, once per uploader, the public follower endpoint (`/x/relation/stat`). Each proxy session first asks Bilibili's own public endpoint (`/x/frontend/finger/spi`) for the anonymous visitor id (`buvid3`) that every visitor of bilibili.com receives, and sends it with its requests - without it Bilibili answers HTTP 412 to datacenter IPs. No account, no login cookie, no browser, no signing, no fingerprint forging. Requests are spaced (about 0.4-0.8 s apart) and a session is replaced after 25 calls or any refusal.

### Limits (by design)

- Video links only. No keyword search, no rankings, no comment text, no danmaku text, no uploader video lists, no login-only data.
- Follower count comes from the public relation endpoint; it is the uploader's total, not per video.
- Bilibili bans an IP for a few minutes after a burst of requests. The Actor spaces requests, rotates datacenter proxy sessions, and retries once through residential proxy when available; if every attempt is refused the row gets `status: blocked` (free) and the run stops after 6 blocked videos in a row. Run again a few minutes later.
- Up to 10,000 links per run; the history store keeps up to 50,000 videos for 6 months after they were last seen.

### Legal

Unofficial. Not affiliated with or endorsed by Bilibili (上海宽娱数码科技有限公司). Reads only public video statistics that bilibili.com shows to any visitor; no personal data beyond the uploader's public display name and id. You are responsible for complying with Bilibili's terms of service and the laws that apply to you.

# Actor input Schema

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

Bilibili video links or ids, one per line: https://www.bilibili.com/video/BV1CUso6AELi/, https://b23.tv/xxxxx short links, av-number URLs, or bare ids like BV1CUso6AELi or av170001. Up to 10,000 per run.

## `includeOwnerStats` (type: `boolean`):

Add ownerFollowerCount (and its change) to each row. One extra request per unique uploader, cached within the run.

## `compareWithLastRun` (type: `boolean`):

Add previous\* and \*Change fields to each row, compared with the last run that used the same history store. The previous values are kept in a named key-value store in your account, so scheduled daily runs line up automatically.

## `outputMode` (type: `string`):

"changesOnly" saves (and charges) only videos seen for the first time or whose views, likes, coins, favorites, shares, comments, danmaku or uploader followers changed. The first run saves everything. Rows with status not_found, blocked or error are always saved (free). Needs "Compare with last run".

## `historyStoreName` (type: `string`):

Named key-value store that keeps the last numbers per video. Use a different name for each separate watch list.

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

Apify datacenter proxy is enough (tested: 0 blocks). Each proxy session gets a public visitor id from Bilibili and is replaced after 25 calls or any refusal; after a refusal the retry goes through residential proxy when your plan has it.

## `maxRequests` (type: `integer`):

Safety cap on HTTP requests to Bilibili in one run, retries included. One video needs 1 request (+1 per b23.tv link, +1 per unique uploader).

## `maxRunMinutes` (type: `integer`):

Stop starting new requests after this many minutes; videos not checked get status error. 0 = only the run timeout applies.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.bilibili.com/video/BV1CUso6AELi/",
    "BV14txF65Efd"
  ],
  "includeOwnerStats": true,
  "compareWithLastRun": true,
  "outputMode": "all",
  "historyStoreName": "bilibili-video-history",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxRequests": 3000,
  "maxRunMinutes": 0
}
```

# Actor output Schema

## `videos` (type: `string`):

One row per video link: status, title, uploader, exact view/like/coin/favorite/share/comment/danmaku counts, uploader followers, and the change since the last run.

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

Counts per status (ok, not_found, blocked, error), saved rows, request and block counters, stop reason. Not billed.

# 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 = {
    "videoUrls": [
        "https://www.bilibili.com/video/BV1CUso6AELi/",
        "BV14txF65Efd"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_courser/bilibili-video-tracker").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 = {
    "videoUrls": [
        "https://www.bilibili.com/video/BV1CUso6AELi/",
        "BV14txF65Efd",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_courser/bilibili-video-tracker").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 '{
  "videoUrls": [
    "https://www.bilibili.com/video/BV1CUso6AELi/",
    "BV14txF65Efd"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call magenta_courser/bilibili-video-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,magenta_courser/bilibili-video-tracker"
        }
    }
}
```

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/6qtemf9eHQrmFTXSY/builds/Wi7F0URSGw6Tr36ox/openapi.json
