Introduction
What bilibili's web API looks like, the parts that make it awkward, and the model bili uses to make it feel small.
bilibili is one of the largest video and community sites in the world: videos, creators, comments, bullet-chat, dynamics, anime seasons, audio, columns, and live streams, all behind a web frontend that talks to a JSON API. That API is public in the sense that your browser uses it every time you load a page, but it is not built to be called by hand.
bili closes that gap. It is a single binary that treats bilibili the way curl
treats a web server: you ask for something by its id or URL, it fetches exactly
that, and it hands you a clean structured record.
What the API looks like
Almost every endpoint returns the same envelope:
{ "code": 0, "message": "0", "ttl": 1, "data": { ... } }
A non-zero code is an error, and bili maps the common ones to clear messages
(not found, rate limited, login required, risk control). The bangumi endpoints
put the payload in result instead of data, and bili handles that for you.
A code of 0 is where it gets interesting, because on this API it does not
mean success. Two endpoints answer with the success code, the success message,
and nothing inside, which is a refusal wearing a success code and is
indistinguishable from an empty result to a client that reads the code and
starts decoding. bili sorts every response into one of seven states before it
decodes a byte, using a recorded table of which endpoints carry a payload when
they answer, so a refusal is reported as a refusal rather than handed to you as
an empty list. That distinction is what the exit codes are for.
The parts that make it awkward
Calling the API by hand runs into four things bili does for you:
- The anonymous session. bilibili expects a set of fingerprint cookies
(
buvid3,buvid4, and friends) before it will answer most read endpoints. bili mints them on first use, so the common case needs no login. - WBI signing. Many endpoints require a signed
w_ridquery parameter derived from a pair of rotating keys. bili fetches the keys and signs every request that needs it. - id forms. The same thing has several spellings: a video is both an
avnumber and aBVstring; a season isss, an episodeep, a media pagemd. bili accepts any of them, plus full URLs, and normalizes before calling. - danmaku. Bullet-chat is delivered as protobuf, not JSON. bili decodes it into plain rows with timing, mode, color, and text.
- A refusal that looks like an answer. See above. It is the reason this tool has eight exit codes instead of two.
What needs a login
A handful of endpoints are gated by bilibili's anti-bot system for anonymous
callers (single-dynamic detail is the main one). When you hit one, bili says so
and tells you to supply a logged-in cookie with --cookie or BILI_COOKIE.
Everything in the quick start works anonymously.
What bili is not
bili stays narrow. It does not log in for you, post, vote, or download
DRM-protected streams; streams returns the playable URLs the API exposes, no
more. Ordinary public audio downloads are handed to BBDown, which is not
bundled and is looked up on PATH. Everything else here is about reading public
data and shaping it, and that narrow scope is what keeps it a single small
binary with no database, no daemon, and no setup.
Next: install it, then take the quick start.