Bilibili (哔哩哔哩) Video Transcript & Subtitle Extractor avatar

Bilibili (哔哩哔哩) Video Transcript & Subtitle Extractor

Pricing

from $1.99 / 1,000 transcripts

Go to Apify Store
Bilibili (哔哩哔哩) Video Transcript & Subtitle Extractor

Bilibili (哔哩哔哩) Video Transcript & Subtitle Extractor

Turn any Bilibili (哔哩哔哩) video link or BV number into clean, LLM-ready text with timestamps.

Pricing

from $1.99 / 1,000 transcripts

Rating

0.0

(0)

Developer

Lantern Lab

Lantern Lab

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

3

Monthly active users

3 days ago

Last modified

Share

Bilibili Video Transcript & Subtitle Extractor

Hand it a Bilibili (哔哩哔哩) video link, BV number or b23.tv share link and get the spoken words back as clean text you can paste straight into a model, with a timestamp on every line. When Bilibili publishes a caption track for the video, that track is used. When it does not — which, signed out, is nearly always — the Actor downloads the public audio and transcribes it with open-source SenseVoice-Small running inside the Actor itself. No Bilibili login, no cookies, no account of your own, and nothing sent to a transcription service. $1.99 per 1,000 transcripts, plus $0.001 for each 10 seconds of speech transcribed, plus $0.005 per run — a ten-minute talk costs about $0.067. See Pricing.

Input:

{
"videoUrls": ["https://www.bilibili.com/video/BV1z9nt6aE5H/"],
"allowAsr": true,
"maxDurationSec": 600
}

Output — one row per video part. text is the whole transcript; segments carries the same words with timings. Sample from a real run, transcript trimmed:

{
"recordType": "transcript",
"platform": "bilibili",
"videoId": "BV1DSnK6REdy",
"bvid": "BV1DSnK6REdy",
"aid": 117475559611144,
"cid": 42910354047,
"part": 1,
"partCount": 1,
"url": "https://www.bilibili.com/video/BV1DSnK6REdy/",
"title": "电脑总是卡顿掉帧?原来是ACE 根本没有彻底卸载干净!",
"authorName": "宇浩玩数码",
"publishedAt": "2026-10-20T22:20:03.000Z",
"durationSec": 52,
"viewCount": 2191286,
"language": "zh",
"transcriptSource": "asr",
"asrModel": "sensevoice-small",
"text": "只要你电脑上玩过藤子杰的任何一款游戏,那么你以后在玩别的游戏的时候,都会卡到怀疑人生。我也是今天才发现的…",
"wordCount": 299,
"segments": [
{ "start": 0.2, "end": 12.44, "text": "只要你电脑上玩过藤子杰的任何一款游戏,那么你以后在玩别的游戏的时候,都会卡到怀疑人生。" },
{ "start": 12.74, "end": 17.01, "text": "我不能忍受的是给你看一下,他居然把自节的反作弊捆绑到steam上了。" }
],
"segmentCount": 5,
"availableCaptionLanguages": [],
"audioSec": 51.04,
"speechSec": 49.49,
"asrBlocksCharged": 5,
"partial": false,
"partialReason": null,
"collectedAt": "2026-10-24T10:50:00.882Z"
}

What you get

  • The transcript, twice over. text is one block of plain text, ready for a prompt. segments is the same content as { start, end, text }, so you can link to a moment.
  • Where the words came from. transcriptSource is platform_caption or asr, and asrModel names the model when it was recognised. You always know what you are reading.
  • What you are paying for, on the row. speechSec is the talking the model found and asrBlocksCharged is what that was billed as, so any invoice can be checked against the rows that produced it.
  • The video around it. title, authorName (the uploader's public display name), publishedAt, durationSec, viewCount, bvid, cid and part, so a transcript can be traced back to the exact part it came from.
  • Multi-part (分P) videos. Add ?p=3 to a link for one part, or turn on includeAllParts to get every part as its own row.
  • Honest gaps. A video with no transcript is still a row, with partial: true, a partialReason and a sentence in statusText saying what happened. A blocked request, a deleted video and a video with no speech are three different reasons and never look the same. The run never reports "nothing found" for collection it did not finish.
  • A run summary. The last dataset row, and the RUN_SUMMARY key, hold the totals: transcripts by source, every partial reason, audio recognised, requests sent to Bilibili and why the run stopped.

Input

FieldDefaultWhat it does
videoUrls—One video per line: full link, BV…, av… or a b23.tv link. ?p=3 picks a part.
preferPlatformCaptionstrueRead Bilibili's own caption track first when there is one.
allowAsrtrueTranscribe the audio when there is no caption track.
includeSegmentstrueInclude the time-coded segments array.
includeAllPartsfalseWrite a row for every part of a multi-part video.
maxDurationSec600Skip (and mark) a part longer than this instead of transcribing it.
maxItems50Upper bound on transcripts written and charged.
asrModelsensevoice-smallThe only model carried, running inside the Actor.
asrLanguageautoForce the spoken language, for example zh or en.
maxUpstreamRequestsderivedHard ceiling on requests to Bilibili. Left empty, it is worked out from your input.
proxyConfigurationApify ProxyBilibili refuses some datacenter addresses; the Actor retries once through a Chinese residential address by itself.

Limits worth knowing before you run it

  • Bilibili serves its caption list to signed-in callers only. This Actor signs in to nothing, so preferPlatformCaptions finds a track only on the rare video that exposes one. On a sample of 40 popular videos taken on 2026-10-24, none did. Plan on speech recognition, and on its price.
  • Speech recognition is not free and not instant. It costs CPU in proportion to how much talking there is, which is also how it is priced. maxDurationSec is there so a long video cannot surprise you; raise it deliberately.
  • Recognition makes mistakes, more of them on music, dialect, overlapping speakers and technical names. segments keeps the timeline, so any line can be checked against the video.
  • A video with no speech — a music video, a clip that is all sound effects — comes back with partialReason: "no_speech_detected", no text, and no charge. That includes the case where the model only looped one short syllable over the music. It is an answer about the video, not a failure of the run.
  • One awkward video cannot hold up your list. Recognition is stopped if it passes twice the length of the audio; that video is returned with asr_failed and is not charged, and the rest of the list carries on.
  • Public videos only. Nothing behind a login, a paywall or an age gate is collected.

Pricing

Pay per event: $1.99 per 1,000 transcripts, plus $0.001 for each 10 seconds of speech the model transcribes, plus $0.005 for each run. A video Bilibili already captioned costs no audio events at all.

EventPriceCharged for
result$0.00199 ($1.99 per 1,000)Each row that carries words.
asr-10s$0.001 per started 10 seconds of speechThe stretches of a video the model actually found a voice in, added up and rounded up per video — silence, intro music and music-only passages are not charged. Not charged for a platform caption, and not charged when the model found no words.
run-start$0.005, once per runThe signing keys and signed-out session every run sets up before it reads a video.

What that works out to, per video of someone talking from start to finish (a video that is half music costs about half the audio part):

Typical videoPrice
1 min$0.008
3 min$0.025
10 min$0.067
any length, Bilibili captioned it$0.002

Rows with no transcript — blocked, deleted, too long, no speech, a duplicate, over a limit — are written free. A run that collects nothing still pays the $0.005 session.

To cap a run, set Maximum cost per run in the run options. At the cap the Actor stops before the next video, marks what it did not reach partial with charge_limit_reached, and says so in RUN_SUMMARY.

Notes on how it works

Requests go to Bilibili's public web API, signed with the same WBI signature the website uses, with no cookie jar at all. A refused request is retried once with fresh signing keys and then once through a Chinese residential address; if it is still refused, the row says blocked rather than pretending the video has no transcript. The audio itself comes off Bilibili's CDN, which refuses nobody, so it is fetched straight off the Actor and only retried through your proxy if the CDN turns that down - downloadSec and asrSec on each row say where a slow video spent its time. Bilibili lists several mirrors per stream and some of them only answer viewers inside China, so the global ones are always tried first. The next video is fetched while the current one is being transcribed, so you are not charged for a machine waiting on a CDN. If Apify moves a run to another machine mid-collection, it carries on from the videos it had already written instead of collecting and charging for them twice. Speech recognition is SenseVoice-Small through sherpa-onnx on the CPU, with Silero VAD cutting the audio into utterances so every line keeps a real start and end — and so the quiet stretches are never transcribed or billed. The weights are baked into the Actor's image, so a run downloads no model and calls no third-party service.