# Bilibili API in Python: videos, comments, popular board (2026)

> Search Bilibili, read views, coins and danmaku counts by BV ID, page through comments and read the popular board from Python, without wbi signing or cookies. Code and cost.

Canonical page: https://okdata.online/guides/bilibili-api-python/

Four endpoints: Bilibili video search, video detail by BV ID, video comments and the popular board, each a plain GET with an API key, no wbi signature and no buvid3 cookie. The worked example below is 7 calls and costs $0.014; a failed call is not charged.

Updated 2026-11-12

Most Python code for Bilibili calls the site's own web endpoints, and it works until it does not. Search wants a wbi signature and a fresh buvid3 cookie, the keys rotate, and a script that ran for weeks starts getting -412 back. The two community projects most of that code was built on, the bilibili-API-collect docs and the bilibili-api Python library, are both archived on GitHub and no longer maintained, so fixes for the next change will not come from there. If you have hit the -412 wall, [Bilibili's search keeps returning -412](https://okdata.online/answers/bilibili-search-api-412-error/) explains what is going on.

This guide does the same jobs with an API key: search, read a video's numbers by BV ID, page through comments, and read the popular board. Each call is an HTTPS GET; there is no signing code, cookie or login to maintain, and `requests` is the only library. You end with a CSV of videos and their stats and a CSV of comments.

## Step by step

Set the key once: `export OKDATA_API_KEY=okd_...`. Every answer has `code` (`"ok"` on success); lists come in `data.items` with a `page`, single objects in `data.item`.

A note on IDs: every Bilibili video has a BV ID, the `BV1…` part of its link, as in `https://www.bilibili.com/video/BV1Gc411K7wD`. That is the `id` all four endpoints use. The older numeric AV ID still exists and comes back as `ext.aid`. [Getting views, coins and danmaku for a BV ID](https://okdata.online/answers/bilibili-bv-id-video-stats/) lists where each number lives.

### Step 1: search videos

[Bilibili video search](https://okdata.online/apis/bilibili/videos/search/) takes `query` and `sort`: `relevance` (the default), `latest` or `most_viewed`. One call returns about 20 videos for $0.002. Each has the BV ID in `id`, `title`, the description in `text`, `published_at`, the uploader's name and numeric ID (mid) under `author`, tags in `hashtags`, and `metrics.views`, `metrics.likes`, `metrics.comments` and `metrics.favorites`. With `view=full` you also get `ext.danmaku`, `ext.duration` and `ext.section`.

```bash
curl -G "https://api.okdata.online/v1/bilibili/videos/search" -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'query=相机推荐' --data-urlencode 'sort=most_viewed' --data-urlencode 'view=full'
```

### Step 2: read a video's full stats

[Bilibili video detail](https://okdata.online/apis/bilibili/videos/get/) takes a BV ID as `id` or the full link as `url`, for $0.002. Detail adds what search leaves out: `metrics.shares`, and with `view=full`, `ext.coins`, `ext.danmaku`, `ext.aid`, `ext.cid`, `ext.duration_s` and `ext.section`. Coins are worth a column of their own: viewers have to spend them, so they say more about a video than likes do.

```bash
curl -G "https://api.okdata.online/v1/bilibili/videos/get" -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'id=BV1Gc411K7wD' --data-urlencode 'view=full'
```

### Step 3: page through comments

[Bilibili video comments](https://okdata.online/apis/bilibili/videos/comments/list/) takes the BV ID as `id` and `sort`: `hot` (the default) or `latest`. About 20 comments come back per page for $0.002, each with `text`, `author.name`, `author.id` (the commenter's mid), `likes`, `replies`, `published_at` and `ip_location` (the IP属地 label, without the prefix). For the next page, send `page.next_cursor` back as `cursor`.

```bash
curl -G "https://api.okdata.online/v1/bilibili/videos/comments/list" -H "Authorization: Bearer $OKDATA_API_KEY" \
  --data-urlencode 'id=BV1Gc411K7wD' --data-urlencode 'sort=hot'
```

### Step 4: read the popular board

[Bilibili popular board](https://okdata.online/apis/bilibili/trends/list/) takes no parameters and returns the current 综合热门 list, about 50 rows a call, for $0.002. Each row is a trend: `rank`, `name` (the video title), `heat` (its view count) and `url` (the video link, with the BV ID at the end). The board also pages with `cursor` if you want more than one screen.

```bash
curl -G "https://api.okdata.online/v1/bilibili/trends/list" -H "Authorization: Bearer $OKDATA_API_KEY"
```

### Step 5: the full script

The helper checks `body["code"]`, skips deleted videos, and retries rate limits and busy upstreams with the same `Idempotency-Key`, so a retry is never charged twice. Paging goes through one generator with a page cap.

```python
import csv
import os
import time
import uuid

import requests

API = "https://api.okdata.online"
KEY = os.environ["OKDATA_API_KEY"]
QUERY = "相机推荐"
TOP_VIDEOS = 2      # detail calls
COMMENT_PAGES = 3   # comment pages under the first video; each page is one call


def call(path, **params):
    headers = {"Authorization": f"Bearer {KEY}", "Idempotency-Key": str(uuid.uuid4())}
    for attempt in range(4):
        body = requests.get(API + path, headers=headers, params=params, timeout=60).json()
        if body["code"] == "ok":
            return body
        if body["code"] == "not_found":
            return None  # deleted or private: not charged
        if body.get("retry") in ("yes", "same_key"):
            time.sleep(body.get("retry_after_seconds") or 2 ** attempt)
            continue
        raise RuntimeError(f"{body['code']}: {body['message']} (request {body['request_id']})")
    raise RuntimeError(f"{path}: still failing after 4 tries")


def paged(path, max_pages, **params):
    cursor = None
    for _ in range(max_pages):
        body = call(path, **params, **({"cursor": cursor} if cursor else {}))
        if body is None:
            return
        yield from body["data"]["items"]
        if not body["page"]["has_more"]:
            return
        cursor = body["page"]["next_cursor"]


board = call("/v1/bilibili/trends/list")["data"]["items"]
for row in board[:5]:
    print(row["rank"], row["heat"], row["name"], row["url"])

hits = call("/v1/bilibili/videos/search", query=QUERY, sort="most_viewed")["data"]["items"]
videos = []
for hit in hits[:TOP_VIDEOS]:
    body = call("/v1/bilibili/videos/get", id=hit["id"], view="full")
    if body:
        videos.append(body["data"]["item"])

with open("bilibili_videos.csv", "w", newline="", encoding="utf-8-sig") as f:
    out = csv.writer(f)
    out.writerow(["bvid", "title", "uploader", "views", "likes", "coins", "favorites", "shares", "danmaku", "published_at"])
    for v in videos:
        m, ext = v["metrics"], v["ext"]
        out.writerow([v["id"], v["title"], v["author"]["name"], m["views"], m["likes"], ext.get("coins"),
                      m["favorites"], m["shares"], ext.get("danmaku"), v["published_at"]])

if videos:
    first = videos[0]["id"]
    with open("bilibili_comments.csv", "w", newline="", encoding="utf-8-sig") as f:
        out = csv.writer(f)
        out.writerow(["bvid", "comment_id", "text", "user", "likes", "replies", "ip_location", "published_at"])
        for c in paged("/v1/bilibili/videos/comments/list", COMMENT_PAGES, id=first, sort="hot"):
            out.writerow([first, c["id"], c["text"], c["author"]["name"], c["likes"], c["replies"], c["ip_location"], c["published_at"]])
```

That is the worked example: 7 calls. For tracking, the detail call is the one that adds up: refreshing 500 BV IDs every morning for a month is 15,000 calls, $30.00.

## Good to know

- **Search depth.** Bilibili's own search stops after a certain number of pages, and so does this endpoint. For a broad topic, run several narrower keywords.
- **Nulls.** Search results have no share count (`metrics.shares` is null there; detail has it). Uploader follower counts are null on posts, and there is no Bilibili account endpoint in the catalog yet. `meta.unavailable` names each null field.
- **Comments and replies.** You get each comment's reply count, not the reply text. The danmaku themselves are not available either, only their count.
- **Multi-part videos.** All parts share one BV ID; the counts cover the whole video and `ext.cid` is the first part's.
- **Snapshots.** Numbers are read at call time. Keep `meta.fetched_at` with them if you track growth.
- **Free cases.** A deleted video answers `not_found`; that, rate limits and every other error cost nothing.

To see the board without code, open the [Bilibili hot list tool](https://okdata.online/tools/hot-search/bilibili/). For a study of a topic across platforms, [content research](https://okdata.online/solutions/content-research/) shows how search, detail and comments fit together.

## What the example costs

One search for 相机推荐 (camera recommendations) by views, the full detail of the top 2 videos, 3 pages of hot comments under the first, and one read of the popular board

| Endpoint | Calls | Price | Subtotal |
|---|---|---|---|
| [Bilibili video search](https://okdata.online/apis/bilibili/videos/search/) | 1 | $0.002 | $0.002 |
| [Bilibili video detail](https://okdata.online/apis/bilibili/videos/get/) | 2 | $0.002 | $0.004 |
| [Bilibili video comments](https://okdata.online/apis/bilibili/videos/comments/list/) | 3 | $0.002 | $0.006 |
| [Bilibili hot searches](https://okdata.online/apis/bilibili/trends/list/) | 1 | $0.002 | $0.002 |
| Total | 7 |  | $0.014 |

Prices in force when this page was built. A failed call costs nothing.

## In your AI (MCP)

Connect the MCP server once, then say what you want. The agent finds these endpoints, reads their prices and calls them on your balance.

Say: Search Bilibili for 相机推荐 sorted by views. For the top 2 videos give me views, likes, coins, favorites, shares and danmaku count, then the top comments under the first one. Also show what is on the popular board right now.

```json
{
  "endpoint": "bilibili.videos.search",
  "params": {
    "query": "Claude Code 教程"
  },
  "idempotency_key": "<a new UUID>"
}
```

MCP: `https://api.okdata.online/mcp`

## Endpoints in this guide

- [Bilibili video search](https://okdata.online/apis/bilibili/videos/search/) `bilibili.videos.search`: $0.002 per call · about 20 videos per page
- [Bilibili video detail](https://okdata.online/apis/bilibili/videos/get/) `bilibili.videos.get`: $0.002 per call
- [Bilibili video comments](https://okdata.online/apis/bilibili/videos/comments/list/) `bilibili.videos.comments.list`: $0.002 per call · about 20 comments per page
- [Bilibili hot searches](https://okdata.online/apis/bilibili/trends/list/) `bilibili.trends.list`: $0.002 per call · about 50 board entries per page

## More about Bilibili

- [Bilibili API](https://okdata.online/apis/bilibili/)
- [What is a Bilibili mid (UID), and where do I get it?](https://okdata.online/answers/bilibili-mid-uid/)
- [How do I scrape the comments on a Bilibili video?](https://okdata.online/answers/bilibili-video-comments-api/)
- [How do Bilibili BV and AV numbers relate, and which one should I store?](https://okdata.online/answers/bilibili-bv-vs-av-number/)
- [How do I find Bilibili creators (UP主) in a niche for sponsorships?](https://okdata.online/answers/bilibili-find-up-creators-for-sponsorship/)
- [How do I get notified when new Bilibili videos mention my product?](https://okdata.online/answers/bilibili-new-videos-keyword-alert/)
- [Is there an API for Bilibili's popular (热门) board?](https://okdata.online/answers/bilibili-popular-board-api/)
- [Comments API](https://okdata.online/roles/posts-comments-list/)
- [Trending topics API](https://okdata.online/roles/trends-list/)
- [Post detail API](https://okdata.online/roles/posts-get/)
- [Post search API](https://okdata.online/roles/posts-search/)
- [Bilibili hot searches: today’s list](https://okdata.online/tools/hot-search/bilibili/)

## Sources

- [bilibili-API-collect repository (GitHub, archived)](https://github.com/SocialSisterYi/bilibili-API-collect) (read 2026-11-12)
- [bilibili-api Python library repository (GitHub, archived)](https://github.com/Nemo2011/bilibili-api) (read 2026-11-12)
