- Clearer wording on uncharged no-speech rows: a video whose audio turns out to be music-only or silent now says exactly that in
statusReason, in plain language. The same videos are recognised as before, and they are still never charged.
One cap for the whole run, a new retryable column, and a channel that is only blocked where we stood.
Max videos for the whole run caps the bill across every channel. Max videos per channel multiplies — ten channels at the default 50 was 500 transcripts — and there was no single number to hold that down. The new maxItems is that number (default 500, so nothing that works today delivers less). Channel reports, skipped videos and uncharged misses are free and never count against it, and when a run stops on this limit the status line says so and names how many videos were left.
- Every row now says whether re-running it is worth your time. The new
retryable column is true when YouTube refused us this time and the same input is worth another run, and false when the answer will not change. It always agrees with the sentence in statusReason, so your code can branch on the column instead of parsing the note.
- "This channel is not available in your country" is no longer read as a dead channel. That message is about where the run fetched from, not about the channel. It now returns the new
channel_region_blocked status, uncharged, asking you to re-run — runs can go out from different locations, and another one may be allowed to list it.
- A video is no longer reported as removed because YouTube's service did not answer, and a caption track that exists but is empty now goes to speech-to-text instead of answering "please re-run" forever. A caption fetch that was actually blocked is still a re-run, exactly as in 1.0.11.
- A resumed run cannot bill you twice for the same transcript. If it cannot confirm what was already charged it now charges nothing and leaves the settlement for later, instead of guessing from a count that may be stale — and if the run's own delivery record comes back missing rows it has already charged for, the run stops cleanly rather than re-delivering and re-charging them.
- Store links to the rest of the steadyfetch shelf now point at the actors that are live.
A channel that could not be read is no longer reported as a channel that does not exist.
- Videos already listed are never thrown away. If a page of a channel's video listing came back unreadable partway through, the run said "this channel could not be found on YouTube" and discarded every video it had already listed. Videos already found are now always transcribed, and the channel report says the listing was cut short.
- "This channel could not be found" is now reserved for channels YouTube says are gone. A listing page that simply could not be read is reported as a temporary problem to re-run.
- A failed handle lookup no longer blames your link. When the lookup that turns a handle or channel URL into a channel ID failed, the run answered either "this channel could not be found" or "this link points to a single video or a playlist, not a channel". A failed lookup now says so and asks you to re-run; those two messages are kept for links that really are wrong.
- A blocked caption fetch is now honest, and free. When YouTube listed a video's captions but then refused to serve them, the run reported "this video has no captions" — and with speech-to-text on, the default, it transcribed the audio instead and charged the speech minutes. Such a refusal is now recognised, retried on a fresh connection, and if the captions still cannot be served the video returns an uncharged row asking you to re-run. A video that genuinely has no caption track still goes to speech-to-text exactly as before.
A typo in an input field name now gets a helpful pointer instead of sample rows.
- A misspelled field name is answered with the field you meant. Sending your channels under a
name this actor does not have —
channelUrls instead of channels, say — or filling in only the
options and no channels, used to come back as the uncharged sample row, which reads as though the
run had worked. Those runs now return one uncharged row that names the field the actor did not
recognise, names the one it does, and shows the shape to send. Runs that do carry channels are
unchanged, and a run with nothing set at all still returns the sample row.
- A speech-to-text outage on our side no longer makes the rest of the run pay for it. If the transcription service refuses this actor's access mid-run, caption-less videos now come back immediately as uncharged
asr_unavailable rows that say plainly the problem is ours — instead of each one downloading its audio first and then failing one by one. Videos with captions keep delivering normally, channel listing is unaffected, and ordinary rate-limit blips keep their usual retries.
- A "not found" answer from YouTube's video service is now final for that video. It ships as an uncharged
not_found row instead of being retried as if it were a temporary block — which also kept those retries from slowing down the rest of the channel.
- Single video links are counted honestly, and the dedicated actor is named. A video link pasted into
Channels is still transcribed directly as a convenience — one transcript, charged exactly as before — but the run summary now says how many transcripts came from single video links rather than channel work, and points to steadyfetch/youtube-transcript-scraper, the purpose-built actor for one-off videos. The Channels field description says the same.
- The "still processing" answer now shows whose claim it is. When YouTube declines a video with "We're processing this video. Check back later.", the row relays it as a quote —
YouTube says: "We're processing this video. Check back later." — the same attribution rows for removed videos already carry. YouTube gives that answer even for some long-unavailable videos, so the row now makes clear the claim is YouTube's; the verdict stays temporary, uncharged, and worth a re-run.
Review-fix build: a row's charged column now always matches what the run could actually bill.
- Fixed: a run that could not bill — an unmonetized copy of the actor, or a price that had not
taken effect yet — still marked delivered transcripts as
charged: true and counted those
charges in the run summary, even though nothing reached your invoice. Such a run now delivers the
transcripts free and marks every row charged: false. The dataset reconciles against your invoice
in every case, not just the normal one.
- Fixed: the same run also budgeted itself as though it had unlimited billable room, so the
speech-to-text minute limit and the cost cap you set were not applied to it. On a whole-channel
run that is the difference between a bounded job and one that walks every upload — both caps now
hold on every run.
- If this actor's billing is ever mis-set in a way that would charge you for the rows this listing
promises carry no result fee — videos we could not transcribe, channels we could not list, the
free sample — the run now fetches nothing, delivers nothing and charges nothing, and says plainly
that the fault is ours. A pricing mistake on our side can no longer put a charge on a free row.
A long video with no captions now gets an honest, permanent answer instead of being told to try
again — and the README links a real example dataset.
- Fixed: a caption-less video longer than a few minutes came back as a temporary problem that a
re-run would clear. It will not clear. Past roughly a few minutes YouTube stops releasing a
video's audio to anything but its own player, so speech-to-text cannot reach enough of it for a
complete transcript — and a partial transcript is never sold as a whole one. Those videos now
come back as
audio_too_long_for_speech: permanent, uncharged, and saying so plainly rather
than sending you to re-run something that can never succeed. On a channel run that matters more
than on a single video: one wrong verdict used to repeat across every caption-less upload in
the channel. Videos that have captions are unaffected at any length, Shorts and short
caption-less videos transcribe as before.
- Changed: when YouTube refuses a video's audio, the run now works out whether it is refusing that
particular video or simply throttling this run, and reports the two differently. A throttle is
still reported as temporary and still worth re-running.
- The README now links a live example dataset — a real
@mkbhd run, four transcripts exactly as
delivered, so you can read the output before you run anything.
Migration-safe billing: a run that is moved to another server, or that you resurrect, no longer
repeats work you already paid for.
- Fixed: when Apify moved a run to another server mid-way — or when you resurrected a finished run —
the run started again from the top. Videos it had already transcribed were transcribed again,
written to the dataset a second time, and charged a second time. Every delivered row now
carries an invisible marker, so a resumed run skips what it already delivered: no duplicate rows,
no duplicate charges, and the sample row of an empty run stays a single row.
- Fixed: if the move landed between a transcript being written and its charge being recorded, the
row said it had been charged when it had not. That one missing charge is now settled when the run
resumes, so
charged: true always matches your invoice — and it can never be settled twice.
- Fixed: channels that could not be listed, videos that could not be transcribed, and bad pastes
each shipped a second identical row after a move. They are now reported exactly once.
- Fixed:
Max speech-to-text minutes restarted after a move, so a moved run could spend up to twice
the limit you set. The limit now covers the whole run, whatever happens to it.
- Fixed: the actor's own one-hour run ceiling also restarted after a move, quietly doubling the
compute a single run could use. It is now measured from when the run started.
- When a run is moved, it now stops claiming new videos immediately, finishes the one in flight, and
says in its status line how many were left for the resumed run.
- Safety rail: if a run cannot read back its own results and has already been charged, it stops
instead of guessing — nothing further is charged, and the status line says why.
Review-fix build: channel-listing honesty, speech-minute accounting, long-video speech detection,
the oversized-audio limit, subtitle timestamps and the run deadline.
- Fixed: when YouTube refused to serve a channel's video listing, the run reported that channel as
having no public videos — a permanent verdict for a temporary problem. A listing that fails is now
reported as temporary, says to re-run, and is still not charged.
- Fixed: when only part of a channel's listing failed — the Videos tab refused while Shorts and live
recordings still answered — the run now delivers every video it did find and records the partial
listing in its channel report, instead of contradicting itself with a "no videos" row.
- Fixed: a video that had to be retried reserved its speech-to-text minutes again on every attempt.
That ate the run's minute budget, so later videos — and sometimes the retry itself — were skipped
for a limit that had not really been reached. A video now holds its minutes once, and gives all of
them back if it never delivers.
- Fixed: a long video with real but thinly spread speech (a four-hour stream carrying twenty minutes
of talking) was discarded as "no speech" after the speech-to-text had already run. Speech density
is now judged over at most ten minutes, so sparse-but-real speech is delivered. Music-only and
silent uploads are still caught, and still cost nothing.
- Fixed: a video whose audio stream does not declare its size (live replays, some still-processing
uploads) was downloaded whole before the per-video data limit could refuse it. The download now
stops at the limit and the video is reported as skipped. Nothing is charged either way.
- Fixed: an SRT or VTT timestamp landing a hair under a whole second rendered a four-digit
millisecond field (
00:00:59,1000) that no subtitle player accepts. It now carries into the next
second.
- Fixed: an unreadable platform timeout could switch off both the platform deadline and our own
one-hour cap. The run's own cap now always applies, so a run still stops cleanly.
- Fixed: on a run with no pricing attached, rows said they had been charged when nothing had been.
Rows now report
charged: false and an empty chargeEvents ledger unless a charge was really made.
- README: output shown first, pricing stated up front, and a plain list of what can fail and why
none of it is charged.
Paste a YouTube channel and get every video's transcript in one run.
- Accepts a channel URL, an
@handle, a /channel/UC… ID, a /c/ or /user/ URL, or a bare
channel ID, in any casing. A single video link works too and transcribes just that video.
- Enumerates the channel's Videos, Shorts and live recordings and de-duplicates them into one
list. Listing the channel adds no separate fee — you are charged only for transcripts delivered.
Max videos per channel caps how many of the newest videos are transcribed.
- Captions first: manual captions are preferred over auto-generated ones, and you can ask for a
specific language (regional variants like
pt-BR match pt).
- Built-in speech-to-text for videos that have no captions, so caption-less videos still return
real text. Each row says which route produced it.
- Output as JSON with timestamped segments, plain text, SRT or VTT.
- Charged once per delivered transcript, plus speech-to-text minutes only when speech-to-text
actually ran. A captioned video never charges a minute.
- Nothing is charged for a channel that cannot be found, a channel with no public videos, or a
video that is private, removed, age-restricted, region-blocked, still live, silent, music-only,
or that YouTube refused to serve. A temporary refusal is reported as temporary and says to
re-run; only a clean answer from YouTube is reported as permanent.
Max videos and Max speech-to-text minutes are hard limits: the run stops cleanly and every
skipped row names the limit that stopped it.
- Every row carries
charged, statusReason and chargeEvents, plus the channel it came from
and its position, so the invoice reconciles from the dataset itself.
- A run with no channels returns one uncharged sample row showing the exact output shape.