Audio & video transcription: never charged for a link we can't reach, can't decode, or that has no speech. Any audio or video URL to text, SRT and VTT with timestamped segments — direct file links from any host, plus 14 tested sites for page links. About 90 languages, no API key of your own.
A big file from a slow host now gets the time it needs to download. Every download used to get the same two minutes, however big the file. Dropbox sends files to this actor at about 0.1 MB/s, so a 42 MB video ran out of time and came back as "please re-run", and running it again ran out of time the same way. A big file now gets as long as its size needs at that speed, within the time the run has left, and is transcribed and charged as usual. Files of 12 MB or less are unchanged.
When the run's timeout is too short for a big file, the row says so, with the numbers. If the time the run has left cannot fit a file at the speed its host sends it, the row now says how big the file is, how fast its host sends it, and the run timeout that would fit it, and the status line adds that running it again with the same input gives the same result. For a Dropbox link this is decided before anything is downloaded. Uncharged.
Google Drive share links now work. A Drive link as Drive's "Copy link" gives it (drive.google.com/file/d/…/view), and the open?id= and uc?id= forms, are now read as the file itself, big files included, and transcribed and charged like any other file. The row keeps the link you pasted, and the same file pasted in two of these forms is one item: the second time it is handed back from your account, not charged. A Drive file that is not shared with "Anyone with the link" now says so and how to change it, and a Drive folder link or a Docs, Sheets or Slides link is answered straight away with what to paste instead. Uncharged.
Box shared links now work. A link of the form app.box.com/s/… is now read as the file itself. When Box will not hand the file over from its shared link (direct downloads turned off, a folder, or a removed file), the row says so and what to change. Uncharged.
OneDrive, SharePoint, iCloud Drive and WeTransfer links are answered straight away. These services hand their files only to a browser, so this actor cannot download from their share links yet. Such a link used to be read as a web page and come back with a general note. It is now answered before anything is downloaded, names the service, and says what to paste instead: a direct link to the file, or a Dropbox or Google Drive share link set to "Anyone with the link". Uncharged.
Which rows are charged, the input fields and the output columns are unchanged.
1.0.115 — 2026-10-08
A Dropbox share link now works the way Dropbox's "Copy link" gives it. A link of the form dropbox.com/scl/fi/… or dropbox.com/s/… ending in dl=0, or with no dl at all, used to come back as "the host served a web page instead of the file", because that form opens Dropbox's preview page. It is now read as the file itself, and transcribed and charged like any other file. The row keeps the link you pasted.
A Dropbox link that is not open to everyone now says so, and what to change. When Dropbox answers a share link with its sign-in page (the link opens only for invited people or a team, or part of it was lost when it was copied) or with its password page, the row now says so, says that running it again will not change it, and says how to fix it: set the link to "Anyone with the link" with downloads allowed, then use "Copy link" again. Uncharged, as before.
A Dropbox folder link is answered straight away. A folder link downloads the whole folder as one zip file, so it is now returned before anything is downloaded, uncharged, with a note to paste each file's own link instead.
A Dropbox browser download address now points you to the share link. An address of the form ….dl.dropboxusercontent.com/cd/… is the one a browser gets while downloading a file; it works only for that browser and stops working after a while. When Dropbox refuses it, the row and the run's status line now say to paste the file's Dropbox share link instead, rather than to try a link from the file's own feed or player. Uncharged, as before.
Which rows are charged, the input fields and the output columns are unchanged.
1.0.114 — 2026-10-08
A short run timeout now works through your list, not just its first file. Since 1.0.109 a run with a timeout under about four minutes tried its first file and then returned every other file as an uncharged time-limit row, even with time to spare. Each file is now tried, one at a time, whenever the time left can still fit it, so a 40-second or two-minute timeout returns as many transcripts as fit. Files the run cannot reach still come back as uncharged rows, and the run timeout they name now counts the files queued ahead of them instead of a flat "at least 461 seconds". Runs with a timeout from four to ten minutes now give every file that chance too, instead of one attempt per run. Runs of ten minutes or more are unchanged.
A page link whose length is known is not started when a short run timeout cannot fit it. When a page link states its length, as SoundCloud, Archive.org, Loom, Twitch and Wistia pages do, and the time left under a short run timeout could not finish a file that long even at our fastest measured speed, the link is returned straight away, uncharged, before anything is downloaded, and its row names the run timeout that fits it. A file whose length is only known once it is downloaded is still tried.
"Raise the run timeout to …" no longer names less time than the run already had. When the last files were held back because too little time was left to start them, the suggestion counted only how fast the files went, so a 15-minute run could be told to raise its timeout to 14 minutes. It now adds the time a file must still have left when it starts.
A file over your "Max minutes per item" now says which setting on the status line, and a run of only such files says a re-run gives the same result. The status line names maxMinutesPerItem, and "1 minutes" now reads "1 minute". When every file in a run was refused for being over that limit, or for another reason that does not change, the line adds that running it again with the same input gives the same result.
Which rows are charged, the input fields and the output columns are unchanged.
1.0.113 — 2026-10-07
A ListenNotes episode link now transcribes. An episode link of the form listennotes.com/e/…/, and ListenNotes' own play link listennotes.com/e/p/…/, is now followed to the episode's audio file on its podcast host and transcribed and charged exactly as that file would be if you pasted it directly. The row's resolvedMediaUrl names that file. Pasting the ListenNotes link and then the audio link it leads to, in either order, is the same episode: the second time it is handed back from your account, not charged. ListenNotes podcast pages, search pages, the longer listennotes.com/podcasts/<show>/<episode>/ links and lnns.co short links are still refused straight away, uncharged, with the same note as before, because they open a robot check with no episode file behind it.
A run whose links were all refused now says that running it again will not change that. When every link in a run is refused for a reason that does not change, such as a site this actor cannot read, a player page with no file behind it, or a file that is gone, the run's status line now also says that running it again with the same input gives the same result. A run with any transcript, any link that may work on another try, or any item stopped by a setting or a time limit is unchanged. Nothing changes about the price, which rows are charged, the input fields or the output columns.
1.0.112 — 2026-10-06
A ListenNotes page link now gets a straight answer. ListenNotes is a podcast directory, and its podcast and episode pages answer automated reads with a robot check, so there is no file to read from them. A ListenNotes page link used to come back as "usually the host being busy … worth running again", which was not true. It is now refused straight away, without a request, the same way Spotify's player pages are: in "Media URLs" the row says to paste the episode's page on its podcast host or its direct audio link, and in "Podcast URLs" it says to paste the show's RSS address or its Apple Podcasts show link. Running it again will not change it, and nothing is charged. ListenNotes' own play links (listennotes.com/e/p/…), which lead to the audio file, are still read as files.
A link pasted in both "Media URLs" and "Podcast URLs" now gets one row, not two. If the link is a show this actor read episodes from, you get those episodes and no second row for the show link. Otherwise you get the link's own row from "Media URLs" — for example a single Apple Podcasts episode is transcribed there, with no extra row telling you to move it there. Nothing changes about which rows are charged, the input fields or the output columns.
1.0.111 — 2026-10-06
A run that stops at one of your caps now names the setting to raise. The status line used to say "maximum total charge reached — N items left, raise it" and "maximum total minutes reached — N items left, raise it"; it now says maxTotalChargeUsd reached — N items left, raise it and maxTotalMinutes reached — N items left, raise it, so the exact option to change is on the line itself, which is the screen an automation or AI agent reads. Nothing changes about the price, which rows are charged, the input fields or the output columns.
1.0.110 — 2026-10-06
A run may now take up to two hours by default. One run used to stop after one hour whatever its timeout said, so a long list of full-length episodes could come back part-done and had to be split into more runs by hand. A run now works for up to two hours, and its default timeout is two hours. Items the clock does not reach still come back as uncharged rows, the run finishes cleanly, and running the same input again carries on with nothing charged twice.
New "Max run time (seconds)" field. Set it to end a run sooner: anything from 90 to 7,200 seconds. Items still waiting when it is reached come back as uncharged rows that name this field, and the run's status line names it too, instead of the run timeout. A number outside that range still starts the run, at the nearest end of the range, with one uncharged row saying so. The run's own Timeout option still applies when it is shorter. Nothing changes about the price, which rows are charged or the output columns.
The page title now reads "Podcast Transcription Scraper — Speech to Text, Audio & Video". The product, the price and the output are unchanged.
You can now see a real run before you start one. The top of this page links to a sample dataset from one verified run — 3 rows, unedited — the same link that used to sit further down.
1.0.109 — 2026-10-06
A short run timeout now tries your file. A run whose timeout was under about three and a half minutes, for example 180 seconds for one file through the API, used to stop after a few seconds and return the file as an uncharged time-limit row without trying it. The first file is now tried whenever the time left can plausibly read it, so one short file needs a run timeout of only about half a minute. A file that cannot finish in time is stopped cleanly and not charged, and its row and the run's status line now name the run timeout and how many seconds a run needs for a file of that length, instead of "raise the run timeout, or split the list" with no number. More files in the same short run still need more time, and a one-file run is no longer told to split its list. Runs with a timeout of four minutes or more are otherwise unchanged. Nothing changes about the price, which rows are charged, the input fields or the output columns.
A run now uses its "Maximum cost per run" fully when several files are transcribed at once. Near that maximum, a file could come back with some of its minutes uncharged, or be returned as an uncharged skip, while part of the maximum was still unused; the run now charges every minute it has room for, exactly as it would one file at a time, and a link to an episode already transcribed for your account is transcribed again, uncharged, instead of coming back as an empty row. Your maximum cost is never exceeded, and nothing changes about the price, which rows are charged, the input fields or the output columns.
1.0.108 — 2026-10-04
A run of long files no longer skips a file while others are still being transcribed. A run stops sending media for transcription once too much of it has come back with nothing to charge. Files still being transcribed were counted toward that point before they came back, so on a list of long recordings (about an hour each), a file could be returned as an uncharged "not transcribed" row even though every file in the run had speech. Only files that have actually come back with nothing to charge count now, so a list like that is transcribed to the end. A run whose media is mostly silent still stops early, as before.
The note on that uncharged row now says the run stopped after too much of its media came back with nothing to charge, such as silent or unreadable audio. Nothing changes about the price, which rows are charged, the input fields or the output columns.
1.0.107 — 2026-10-03
A private link that needs a sign-in now says so. A file link that sends us to a sign-in page (for example a file shared inside a team workspace) used to come back as "the link could not be reached… please re-run". It now says the link opens a sign-in page, that running it again will not change that, and what does: share the file with a public link, or download it and host it somewhere public. Uncharged, as before.
A chained row that is the other scraper's own error is shown as that error. When a row of your chained dataset is the source scraper's own "no data" answer (for example "Empty or private data for provided input"), the uncharged row now says the chained run itself returned no data for that link and quotes its reason. It used to say none of your chained rows carried an audio or video link.
The input form now says what repeat memory needs on a scoped API token. A run under a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission (or Actor runs set to Full access) to hand back items you already have without charging them. Without it, those items are charged again. The README already said this; the input form now does too.
Nothing changes about the price, which rows are charged, the input fields or the output columns.
1.0.106 — 2026-10-01
A long list of short clips with some silent ones now runs to the end. A run stops sending media for transcription when too much of it has no speech. On a long list of short clips that point came too early: a list where nearly a third of the clips were music-only or silent could stop part-way and return the rest as uncharged "not transcribed" rows while the run was otherwise going normally. The point now moves with every item the run sends for transcription, so a list like that is served to the end, and a run whose media is mostly silent still stops early, as before. Nothing about which items are charged changed: a clip with no speech is never charged, and a transcribed clip is charged per started minute of its audio, as before.
A run stopped part-way is now accounted for with what it had charged. The run's own internal record now notes what it has charged as it goes, so a run that is aborted or moved to another server mid-way is still accounted for on our side. Nothing changes about what is delivered or charged, the price, the input fields or the output columns.
1.0.105 — 2026-09-29
The input description now says how big one run can be. A chained dataset is read up to 10,000 rows per run, and one run works for up to one hour, which is this actor's own limit; a longer run timeout does not extend it. Items a run does not reach come back as uncharged rows, and running the same input again carries on: anything already transcribed is handed back uncharged. The "Dataset ID" field says the same thing in its first sentence. Nothing about which items are transcribed, delivered or charged changed, and the price, the input fields and the output columns are unchanged.
1.0.104 — 2026-09-29
Links sent as media_urls are now transcribed instead of refused. Some tools rewrite field names in snake_case, so "Media URLs" arrives as media_urls. That spelling, and the snake_case spelling of every other link field this actor already reads (video_urls, audio_url, podcast_urls and the rest), is now read exactly like urls: merged with anything already there, duplicates counted once, and one uncharged note row naming the field to use next time. No input field, output column, charged event or price changed.
A run moved to another server and then stopped by its time limit keeps its suggestion. When the run timeout stopped a run, the status line says how long all your items would need and whether to raise the timeout or split the list. On a run Apify had moved to another server part-way, that suggestion was dropped, because the items the first server finished were left out of the count. They are now counted, so the moved run gets the same suggestion as a run that was never moved.
The note about settings sent inside a wrapper names the wrapper you used. Settings sent inside payload, data or body are read like settings sent inside input. When none of the wrapped values were used (because you had set the same field at the top level), the note row still named input; it now names the key you actually sent.
Nothing about which items are transcribed, delivered or charged changed.
1.0.103 — 2026-09-28
A run Apify moves to another server now reports its results the same way as a run that was never moved. Before, links the first server could not transcribe were counted as delivered once the run resumed, so the run page could read "Transcribed 6 of 6 items" when only 2 were transcribed, and the audio minutes it named were only the resumed part's. Now those links are counted as the misses they were, and the minutes named are what the whole run charged. Nothing is fetched, delivered or charged twice, and which rows are charged is unchanged.
Two "could not fetch" rows now say what a re-run can and cannot change. A link refused on the one network route the run reached no longer says the refusal is temporary: it says a re-run tries both routes, and that if both refuse, running it again will not change it. A link that answered with a web page instead of the file says the same about a passing check versus a guarded file, and suggests a link from the file's own feed or player instead. Rows for a host that is busy, rate-limiting or not answering still say to re-run. Nothing changes about the price, the charged events, the input fields or the output columns.
1.0.102 — 2026-09-27
A page-less Facebook facebook.com/videos/<id> link now transcribes. The note for other Facebook links already said /videos/ links work here, yet a /videos/<id> link with no page name in front of the id was turned away by that same note. It is now read as the same video as facebook.com/watch/?v=<id>, so the two links for one video are one item, charged once.
A Facebook link pasted with extra wrapping around it now reads as the link itself: angle brackets from an email or chat app (<https://…>), Facebook's own outbound redirect (l.facebook.com/l.php?u=…), and the short fb.com address.
Items stopped by the run's own limit are listed once, even if Apify moves the run to another server. Before, a run moved mid-way could list the same "not started" item twice. Nothing about which items are started, delivered or charged changed.
The row for an item not sent for transcription after too much silent media is reworded. It now says the run stopped sending media for transcription, that nothing was charged, and that a re-run continues with nothing charged twice. Nothing changes about the price, the charged events, the input fields or the output columns.
1.0.101 — 2026-09-26
A run cut off before it finishes is now accounted for. The run now notes its own start in its internal record, so a run that ends without finishing — a hard abort, a crash or a timeout — is still accounted for on our side. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.
1.0.100 — 2026-09-26
A Facebook /watch/live/?v=<id> link now transcribes. That is the link Facebook shows under a live video, including once it has been saved as a replay, and this actor used to turn it away with a general note about which Facebook links work. It is now read as the same video as facebook.com/watch/?v=<id>, so the two links for one video are treated as one item and charged once. Facebook links that point at a single post (/share/p/, permalink.php, story.php), a facebook.com/watch/<page>/<id> link and a bare facebook.com/share/<code> link are still not read, but the uncharged row now names the kind of link you pasted and the link to paste instead. The price, the input fields, the output columns and the charged events are unchanged.
1.0.99 — 2026-09-24
Settings sent wrapped in an extra "input" object are now read. A run whose input arrives as {"input": {…}} — for example from run_input={"input": {...}} in the Python client — now reads those settings (links, a chained dataset, podcast feeds and every option) as if sent directly and adds one uncharged note row naming them. Before, the run stopped with a guidance row and transcribed nothing. A setting you also filled at the top level keeps your top-level value. Nothing changes about the price, the input fields or the output columns.
1.0.98 — 2026-09-24
A run that Apify restarts after it has already delivered keeps its own record of that delivery. If the restarted part of a run delivers nothing new, the run's internal record now keeps what the earlier part delivered and charged, with a note of the restart, instead of being rewritten as if nothing had been delivered; one line in the run log says so. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.
1.0.97 — 2026-09-23
A whole show or a whole scraper run is now the first thing this page and the input description offer. The top of the page shows the podcast feed call and the Dataset ID call before single links, with the run cap sized to a real job (a show's 10 newest 45-minute episodes are 450 audio minutes). A second short paragraph says how to follow a show: put the same feed input on an Apify Schedule, and each run charges only for episodes your account has not had.
"Media URLs" is no longer a required field. A call that sends only a podcast feed or a Dataset ID no longer has to carry an empty link list, and an explicit null in it is read as "no links", exactly like leaving it out. Pressing Start with nothing set still transcribes the 25-second sample clip, charged as one audio minute. Nothing changes about the price, which rows are charged, or the output columns.
1.0.96 — 2026-09-23
The cost section now says how to make a run faster. A run starts at 2 GB of memory with a 60-minute time limit, and that is unchanged. Setting Memory to 4 GB in the run options before you press Start gives the run twice the processing power, so long files are decoded sooner, and the per-minute price stays the same because platform usage is already inside it. No price, charged event, input field or output column changed.
1.0.95 — 2026-09-22
The run log no longer prints an internal usage summary at the end of a run, and a failed internal bookkeeping write no longer prints where it was being saved. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.
1.0.94 — 2026-09-22
A failed item is now described in our own words all the way down — nothing behind its row quotes the speech service. When the transcription service turns a request away — a rate limit, an outage page, a retired model — the error behind the row used to carry that service's own message, which can include its links and details of our account with it. The row itself already read "speech recognition did not complete" and still does; the message behind it is now ours as well. A verdict about your own file ("file is empty", "could not process file") still reads as before. No price, charged event, input field or output column changed.
1.0.93 — 2026-09-22
Progress lines now arrive at least every 30 seconds, including during transcription. The run promises a line at least every 30 seconds while it works, but the check behind that promise could leave up to 40 seconds of quiet, most visibly while a long file was being transcribed. It now speaks after 25 seconds of quiet and checks every 5 seconds, so no gap can run past the 30 seconds the run promises. The lines themselves are unchanged: they name steps and counts only, push no row and charge nothing. No price, charged event, input field or output column changed.
1.0.92 — 2026-09-22
A long item no longer runs in silence. While an item is being fetched, decoded or transcribed, this actor used to print nothing between the line naming your links and the summary at the end — on a one-hour episode that was four minutes and more of an empty log, which reads exactly like a run that has hung, and runs were stopped that were working. A run now opens with what it is about to do and promises a line at least every 30 seconds, and keeps that promise on the run page and in the log: which step each item is on and how far it has got where the step can measure it ("downloading (58 MB of 106 MB so far)", "decoding the audio (about 25 min decoded so far)", "transcribing 61 min of audio (40 s in)", or on a very long file "transcribing minute 60 of 240 (part 3 of 9)"), how many items are delivered so far, the time left on your run's limit, and the sentence "Nothing has stopped; the run continues until it finishes or your time limit ends it." The progress lines name steps and counts only — never your links or titles — and they push no row and charge nothing. No price, charged event, input field or output column changed.
1.0.91 — 2026-09-22
An uncharged note about your input no longer puts a field name in the URL column. Two of the rows this actor writes are about your INPUT rather than about an item: the one a links-less Start writes naming the settings it kept and ran the sample under, and the one naming the field your links arrived in when it is not this actor's own name (videoUrls, reelUrls, url, mediaUrls and the rest). Both of them listed those field names under "Your URL" — so maxTotalMinutes or videoUrls sat in the column that everywhere else in the dataset holds the link you pasted, and read as a broken address. Those two rows now leave the URL column empty and carry the names in their own inputField field, with an inputValue beside it for the value a note refuses. Only those two rows carry the pair: every transcript row, every uncharged miss and the two repeat-memory notes are byte-for-byte what they were, so nothing you export, join or chain grows a column. What the rows SAY is unchanged — both already named every field in full in their note, and both already spoke only for themselves ("This row is uncharged") rather than claiming nothing in your run was charged. No price, charged event, input field or existing output column changed.
1.0.90 — 2026-09-20
On a run where everything went wrong at once, the status line no longer overflows and loses its tail. The run page cuts the status message at 500 characters and replaces the rest with "…", and on a run that piled up misses, unreadable links, both total caps, the run timeout, a speech-service outage, a server move and a repeat-memory refusal, this actor's line ran to 930 characters — so the platform silently ate the end of it, which on this actor is the sentence saying your next run will be charged again for what this one just delivered. The line now keeps giving way until it fits: the per-reason breakdown collapses into one counted, uncharged clause ("31 items not delivered, none charged — each row says why") and the run names the one stop that ended it with the setting that moves it. Every number you can act on survives — what was transcribed, what it cost, what was not charged, what your account already had, and the cap or limit that bound. The reasons themselves are unchanged and still on each row and in OUTPUT.coverage. Nothing about the price, the charged event, the input form or any output column changed.
1.0.89 — 2026-09-20
When the run's time limit cuts a list short, the status line now tells you what ask fits. Until now it said how many items were left and "raise it" — true, and no help at all deciding what to raise it to, so the only way to find the number was to keep running the list and watching where it stopped. The run that was cut is the one place that number is known, so it now does the arithmetic for you, out loud: how many items it transcribed and in how long, roughly how long your whole list needs at that same rate, and then either the run timeout to raise — in minutes, with room to spare — or, when the list needs more than a single run's 60-minute ceiling can ever hold, how many runs to split it into and how many items to put in each. The same numbers ship on the run's OUTPUT record (fit) for anyone reading over the API. It appears only on a run that delivered some of your list and was cut by the clock: never on a run that finished your whole list, never on the caps you set yourself, and never on a speech-service outage, which is ours and not something a bigger timeout fixes. Nothing about the price, the charged event, the input form or any output column changed.
1.0.88 — 2026-09-20
Media sent as mediaUrls is now transcribed instead of refused. Some audio-transcription actors call their link list mediaUrls; this actor declares urls, and until now a run that arrived under the other name planned nothing and answered with a row naming a field you had never set. It is now read exactly as if you had typed it into "Media URLs", merged with anything already there and deduplicated, with one uncharged note row naming the field to use next time. The singular mediaUrl already worked; only the plural was missing. No input field, output column, charged event or price changed.
1.0.87 — 2026-09-20
When a link belongs on one of our other actors, the row now points at the right one — and stops pointing at one that would refuse you. YouTube and Instagram both write some of their own addresses with a WORD sitting where an ID normally sits: youtube.com/embed/videoseries?list=… is a playlist, youtube.com/embed/live_stream?channel=… is a channel, and instagram.com/reels/audio/<id>/ is Instagram's audio page — the reels made with one sound. Each word is exactly as long as a real ID, so the row read them as single videos or single reels and sent you to an actor whose field cannot take them: a second run, to be told no a second time. A live-stream embed now sends you to the channel actor, which really can read it; the playlist and audio pages get the plain answer instead of a dead address. Every real link routes exactly where it always did, including an Instagram account genuinely called audio. No price, input field, column or charged event changed.
1.0.86 — 2026-09-19
The whole "Maximum cost per run" you set is now spent on transcribed minutes. A capped run used to hold back about a tenth of your cap as a cushion against platform usage — but your cap pays for the minutes it transcribes and nothing else, so that tenth was cap you had asked to spend and did not get: a cap worth eleven minutes transcribed ten. A run now transcribes every minute its cap can pay for, and still ends the way it always has — on its own line naming your cap and what was left, never on a run the platform cuts short. Nothing about the price, the charged event, the input form or any output column changed.
1.0.85 — 2026-09-19
An item your run's time limit cannot finish is no longer started at all. A run used to keep back one minute before starting the next item — less than any single step an item needs, let alone all of them: fetching the file from its host, reading a page for its media link, decoding the audio, and the speech-to-text call itself. An item begun inside that last minute therefore had its file fetched and decoded, and then your run's limit ended it with no transcript to show for the time and transfer already spent. A run now checks that every step of an item fits in the time it has left before it starts one. When it does not, the item is handed straight back as the same uncharged row a run already used when it ran out of time: the run's own limit, and the advice to raise it or split the list — nothing is fetched, decoded or charged for it. One exception keeps a short run useful: a run that has delivered nothing yet still tries one item, provided there is enough time left to finish that item. Separately, decoding the audio now leaves room for the speech-to-text call that has to follow it, instead of being allowed to use every second the run had left. Nothing about the price, the charged event, the input form or any output column moved, and a run with time to spare behaves exactly as it did.
1.0.84 — 2026-09-16
When your run timeout is what stopped an item, the row now says so instead of blaming the file, the site or the speech service. 1.0.83 gave every long step — the download, the audio decode, each speech-to-text call — the smaller of its own limit and the time your run had left, so a run ends inside the timeout you set. That fix had a side the previous build did not cover: a step we cut short looks like a step that failed, so an item stopped by your own timeout was handed back as "the media could not be decoded" or "speech recognition did not complete, please re-run" — which reads as something worth paying for a second run to retry, when a longer timeout is what it actually needs. Those items now ship the same uncharged row a run already used when it ran out of time before starting an item: the run's own time limit, and the advice to raise it or split the list. Nothing else moved — a genuine refusal, a dead link, a file with no audio and a real speech-service outage all keep their own wording exactly as before, and a run with time to spare is untouched.
Three more steps now end inside your run timeout. The two short media checks that measure a file's audio track and its exact length, and the lookup that reads a page for its media link, still had time limits of their own only — the page lookup could hold a run for a minute and a half after your timeout had passed. All three are now bounded by the run as well, and a lookup we stop is ended outright rather than asked politely (asked politely, the tool it uses exits tidily and its half-finished answer reads as "this page has no video"). No input field, output column, charged event or price changed.
1.0.83 — 2026-09-15
A run now stops on the timeout you set, instead of being killed past it. Fetching a file from a slow host, decoding it, and each speech-recognition call each had a time limit of their own, measured from the moment that step started — none of them knew how much of your run's own timeout was left. An item that began near the end of the window could therefore keep working long after the run should have ended, and Apify killed the container mid-step: the run showed as timed out, and its status line, its _summary row and its final record never arrived. (Transcripts already delivered were still yours and still charged exactly as their rows said.) Every one of those steps is now given the SMALLER of its own limit and whatever the run has left, so the run ends inside your timeout, says on its status line that the run's own limit stopped it, and books the item it could not finish as an uncharged row. A run with time to spare is completely unchanged — every step still gets its full normal limit. Nothing about the price, the charged event, the input form or any output column moved.
1.0.82 — 2026-09-14
A Facebook or TikTok link only ever transcribes that site's OWN video. 1.0.81 added this rule for X (Twitter) post links; it now covers the Facebook and TikTok routes as well. Several of the accepted link shapes — an fb.watch or facebook.com/share/… link, a tiktok.com/t/ or vm.tiktok.com share link, a tiktok.com/v/<id> link — are shorteners that are followed to wherever they lead, and the lookup behind them will resolve whatever it finds at the end. If that turns out to be another site's video, the run now hands it back as an uncharged row naming the address to paste instead, rather than transcribing and charging for it. Nothing about the price, the charged event, the input form or any output column moved, and a Facebook or TikTok link that carries its own video is unaffected.
1.0.81 — 2026-09-14
X (Twitter) post links now transcribe. Paste x.com/<user>/status/<id> (or the twitter.com, mobile./m., i/status and i/web/status forms) and the post's own video comes back as text, timestamps and subtitle files, charged per audio minute like any other link. An X profile, search, hashtag page, Space or live broadcast has no single finished video to pick and comes back uncharged naming the shape that works, and a post whose video lives on another site is not followed from here.
1.0.80 — 2026-09-14
The other half of the scoped-token fix: a token that can READ your memory but not WRITE it now says so. 1.0.79 covered a token that could not open the "you never pay for the same item twice" memory at all. A token that carries key-value store Read but not Write — or Write but not Create on a store your account does not have yet — is quieter and costs the same: the run opens the memory, hands back every repeat correctly, and then remembers nothing it transcribed, so your next run is charged for the whole delivery all over again. The run now says so the moment the first write is refused — on its status line, in the run log, and on an uncharged row naming the permission to grant (key-value store Write and Create under Settings → API & Integrations, or Actor runs set to Full access). Everything you asked for is still transcribed and charged exactly as before, and nothing about the price, the charged event, the input form or any output column moved.
A run moved to another server no longer repeats its own warnings. The uncharged notes this actor writes about a run — the two token-scope warnings, the note about a Start with only settings set, and the note about a sibling's field name — were pushed a second time when Apify moved a run mid-way to another server. Each is now written once per run, however many servers the run passes through.
1.0.79 — 2026-09-14
A run started with a scoped API token no longer pays twice in silence. The "you never pay for the same item twice" memory is a key-value store in your own Apify account, and a run started over the API with a scoped token in restricted-access mode cannot open it unless that token has key-value store Read, Write and Create permission — so items you had already transcribed were fetched and charged again, with nothing on the run naming the cause. Such a run now says exactly what happened and exactly how to fix it: on the run's status line, in the run log, and on a new uncharged row placed first in the dataset (grant the token key-value store Read, Write and Create under Settings → API & Integrations, or set Actor runs to Full access, or start the run from the console). Charged rows still carry repeatCheck: "unavailable", and New items only now says when it could not filter for the same reason. The run itself is unchanged — everything you asked for is still transcribed, and nothing about the price, the charged event, the input form or any output column moved.
A transcript too big for one dataset row now still reaches you, instead of failing the item. Apify refuses any dataset row whose JSON is 9 MB or more, and a marathon recording with all four output formats ticked can get there. Such a row is now measured before it is sent: the largest artifacts (the timestamped segments, the SRT, the VTT, the plain text) are saved in full to the run's key-value store, and the row itself carries a note naming each one and the key to read it back from — open the run's Storage tab, Key-value store. Nothing is dropped, nothing is split across two rows, and the charge for the item is exactly what it always was: per audio minute transcribed. Normal-length transcripts are untouched and still arrive inline.
1.0.78 — 2026-09-13
Sending url instead of urls now works. A run that named its link in url, videoUrl, audioUrl or mediaUrl — the obvious singulars, and what an agent or a first API call usually reaches for — used to come back with nothing transcribed and an uncharged row asking for urls. Those names are read now, and so is podcastUrl for a whole show. Your links are transcribed as if they had arrived in Media URLs, with one uncharged row on the run naming the field to use next time; a link sent in both fields is still one item and one charge.
A field name we really do not read says so plainly. The uncharged row and the run's status line now name the field you set and the field this actor reads, instead of only ours.
Nothing charged or priced changed: same per-audio-minute event, same price, same input fields on the form.
1.0.77 — 2026-09-13
Subtitle files and timestamps now come back by default.Formats to return arrived with only Plain text ticked, so a run started without touching the form came back with srt: null and vtt: null and an empty segments — while the listing promises text, SRT, VTT and timestamped segments, and the README sample row showed them filled in. All four are now the default: paste a link, press Start, and the row carries the transcript, the timestamped segments and both ready-to-upload subtitle files.
It costs nothing extra. You are still charged per audio minute transcribed, exactly as before — the formats you ask for have never changed the price. A run that wants a smaller row can untick any of the four; Plain text is always returned.
Nothing else moved: no price, no charged event, no new input field, no new output column, and a run that already asked for its formats explicitly behaves exactly as before.
1.0.76 — 2026-09-13
The listing is now called Podcast Transcript Scraper — Audio & Video to Text, RSS & SRT. The actor takes podcast feeds as of this build, and the name now says so; "video to text" is still in it, and nothing about what the actor does, what it charges or what it delivers changed with the name.
Transcribe a whole podcast, not one episode at a time. A new Podcast feeds or show links field (podcastUrls) takes a show's RSS address — https://example.com/feed.xml — or its Apple Podcasts show link (podcasts.apple.com/…/id1200361736), finds the feed itself, and transcribes the newest episodes in one run. Episodes per show (maxEpisodesPerShow) decides how many; the default is 10, newest first, and a show that publishes fewer delivers all it has and says so. Single episodes and files are unchanged: they still go in Media URLs.
A re-run of the same feed only pays for what is new. Each episode is remembered by the feed's own id for it, so a second run of the same show hands back everything already transcribed for your account — repeat: true, not fetched, not charged — and transcribes only what has been published since. A weekly Task on one feed costs a week of episodes, not the back catalogue. New items only works on them too: tick it and the repeats are left out of the dataset entirely.
Four new output columns on every row: podcastShow, episodeTitle, episodeGuid and publishedAt. They carry what the feed said about the episode, and they are null on every row that did not come from one.
Never charged: a feed we cannot reach, a link that turns out to be a web page rather than a feed, an Apple show id the catalogue does not list, a feed with no downloadable audio, and an episode your account already had. Each is one uncharged row naming what was found and the next step, and a host that was simply busy says it is worth running again.
An Apple Podcasts show link pasted into Media URLs still comes back uncharged asking which episode you meant — it now names the new field to use instead.
Nothing else moved: no price, no charged event and no existing input field changed. Four output columns were added, as described above; every column that was already there behaves exactly as before.
1.0.75 — 2026-09-12
An aborted or migrated run now leaves its receipt from the first moment of the run, instead of only after start-up finishes. The record that says how a run ended was only put in place once the run had finished starting up — reading your input, opening its stores, working out what a previous attempt had already delivered and charged. A run stopped inside that window ended with nothing written about it at all, which mattered most on a re-started run, where the thing not written about was the earlier attempt's work. It is now in place from the run's first moment. Nothing else moved: no input field, output column, event or price changed, and a run that reaches its work behaves exactly as before.
1.0.74 — 2026-09-12
Asking for a bigger minutes limit than one run can do no longer stops the run before it starts. "Max minutes per item" refused anything above 100,000 and "Max total minutes" anything above 1,000,000 — the platform checked both before the actor was even started, so a bigger number (a round one typed by hand, or guessed by an AI agent calling through MCP) came back as an error with no run, no rows and nothing to read. The limits are unchanged and still real; they are now applied by the actor, and a bigger ask runs at the limit with the run's status line saying what you asked for and what was used. 0 still means "no limit" on both.
Nothing else moved: no price, no charged event, no input field and no output column changed.
1.0.73 — 2026-09-11
Run it on a schedule and get back only the new episodes. A new New items only switch (off by default, so nothing changes unless you tick it). Leave it off and the actor behaves exactly as it always has: an item your account already has comes back from the run that transcribed it, marked repeat: true, not charged — a re-run of the same list is a complete archive that only pays for what is new. Turn it on and those items are left out of the dataset entirely: nothing is fetched, nothing is charged, and the run's status line says how many were skipped — so a Task on a weekly Schedule hands your Google Sheet, Slack, n8n or webhook only the new transcripts. What you are charged is identical either way; this only decides what lands in the dataset.
Only an item this actor actually delivered a transcript for counts as one you already have. An item that came back without one — unreachable, no speech, or stopped by a cap — does not, so the next run tries it again rather than silently dropping it for 90 days.
The README now carries a Run it on a schedule section: Task → Schedule → integration, how often the data actually changes, and how chaining a scheduled scraper run behaves.
Input schema: the description now opens with the call that works, what a minute costs, the never-charged list, the 256 MB rule, the tested-sites list and where YouTube/Instagram/ad-library links go — the screen an AI agent reads. Media URLs leads with the value shape and one example and then names, in its first three sentences, the size bound and which links belong in another actor; the full tested-sites list stays above it on the form. A run that delivered nothing now says why on its status line, with the fix.
Nothing else moved: no price, no charged event, no output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.72 — 2026-09-11
TikTok video links are transcribed here now, like any other supported link. Paste tiktok.com/@<user>/video/<id>, a tiktok.com/t/<code> or vm.tiktok.com/<code> share link, or m.tiktok.com/v/<id>, and it is fetched and transcribed exactly as a podcast page or a direct file link is. Until this build every tiktok.com link came back as an uncharged row pointing at our TikTok ads actor, which only reads Creative Center ad links — so a pasted video was refused twice over, and the row closed by suggesting a direct media file link a TikTok buyer has no way to get. TikTok is now the thirteenth tested site on the "Which links work" list and on the input form.
TikTok Creative Center ad links are unchanged: ads.tiktok.com/business/creativecenter/topads/… still comes back as an uncharged row naming the TikTok ads actor and the field to paste it into, because that actor reads the ad's own creative and does it better. A TikTok profile, hashtag, sound or Discover page has no single video to pick, so it still comes back uncharged — and that row now leads with the link shapes that DO work here instead of sending you away.
Links sent in startUrls are read too. If you came here from one of our Instagram actors, or from any scraper that uses Apify's conventional Start URLs field, your list is read and transcribed exactly as if it had arrived in urls — both plain links and the { "url": … } crawler shape — with one extra uncharged row naming urls as the field to use next time. A creator handle in that list is not a link, so it gets the same honest uncharged row any non-link gets.
A row that carries both a platform video link and the media file itself now uses the file. Chaining a scraper whose rows hold, say, webVideoUrl beside downloadAddr takes the file: it is one fetch instead of a lookup and a fetch, and it cannot be refused by the platform. Nothing about what you are charged changes — it was, and is, one item and one charge per row.
A TikTok link that cannot be reached behaves exactly as every other link does: an uncharged row saying so, with a re-run suggested where a re-run can help. Nothing is ever charged for a link we could not fetch.
Nothing else moved: no price, no charged event, no input field and no output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.71 — 2026-09-11
Facebook reel and video links are transcribed here now, like any other supported link. Paste a reel (facebook.com/reel/<id>), a watch link (facebook.com/watch/?v=<id>), a /videos/<id> link or an fb.watch share link and it is fetched and transcribed exactly as a podcast page or a direct file link is. Until this build every facebook.com link came back as an uncharged row pointing at our Facebook ads actor, which only reads Ad Library links — so a pasted reel was refused twice over. Facebook is now the twelfth tested site on the "Which links work" list and on the input form.
Facebook Ad Library links are unchanged: facebook.com/ads/library/?id=… still comes back as an uncharged row naming the Facebook ads actor and the field to paste it into, because that actor reads the ad's own creative and does it better. A Facebook profile, a photo post or a page's Reels tab has no single video to pick, so it still comes back uncharged — and that row now leads with the link shapes that DO work here instead of sending you away.
A Facebook link that cannot be reached behaves exactly as every other link does: an uncharged row saying so, with a re-run suggested where a re-run can help. Nothing is ever charged for a link we could not fetch.
Nothing else moved: no price, no charged event, no input field and no output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.70 — 2026-09-11
Every run now tells you how much of your list came back. The run page reads Transcribed 7 of 12 items, and the run's OUTPUT carries the whole count — how many were transcribed, how many were refused, and the reason for each refusal (unsupported site, nothing to transcribe on the page, over the size limit, and so on). You no longer have to read the dataset row by row to work out what a run did and did not cover. They are counts and not percentages, deliberately: 75% covered reads the same over four links and over four hundred, and only one of those is worth acting on.
The 256 MB per-item size limit is now on the input form beside the URL field, instead of only turning up in the row that refuses an oversized file. A file over it still comes back as an uncharged row saying so, before any of it is transcribed — long meeting and conference recordings are the ones that cross it, and an audio-only version of the same session is usually far smaller.
The store listing leads with what buyers actually type. The actor is now called Audio & Video Transcriber — Speech to Text Transcription & SRT, and the top of the page carries the one-click MCP pin, this actor's id, the one required input field with a real example, and the field that caps what a run can spend. An AI agent reads only the first part of a listing, and until this build those sat below the cut. Every word about what you are charged is unchanged.
The private run-report this actor writes for our own support — counts only, never anything you typed — now also records whether a run that stopped at a cap you set had actually filled it, and the per-event prices the run was billed at.
Nothing else moved: no price, event or dataset column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.69 — 2026-09-10
A made-up closing line after the end of the audio no longer hides a real transcript — and is never delivered. Speech recognition sometimes invents a sign-off, most often "Thank you.", and stamps it with a time that falls past the end of the file. When that stamp landed far enough out, the whole transcript was judged unreliable and the file came back marked as having no speech: nothing delivered and nothing charged, even when there were 25 seconds of clear speech on it. It was found on a real customer run of our Facebook ads transcriber — a 30-second property ad with 25 seconds of clear voiceover that came back with nothing at all — and the same speech step runs here. From this build the invented line is removed before anything else is decided, so the real speech is delivered and charged exactly as usual — and the invented line is taken out of the transcript, the timed cues and the subtitle output too, so you are never handed, or billed for, a sentence the audio does not contain. The same invention was also slipping through quietly at the end of transcripts that DID deliver; it is gone from those as well. The length we measure for billing is unchanged.
Nothing else moved: no price, event, input field or output column changed, and only minutes actually transcribed are charged.
1.0.68 — 2026-09-10
A run that hits a busy host now waits a length that fits your run, instead of skipping the wait entirely. When a host refuses a file on every route, the run pauses and walks the whole ladder again from a fresh route. Until now that pause was one fixed 90–180 second block: if your run's time limit could not hold a whole one, the run took no pause at all and you got an uncharged "please re-run" row with almost all of your time unspent. The pause is now sized to the time your run actually has left, and the first one is short — about 30 to 60 seconds — so a host that frees up quickly costs you far less waiting. A short run and the untouched Start form now get a real second walk where they used to get none.
The waiting is also sized to what is still worth recovering, measured from the audio minutes this run has actually billed — while a run that has not delivered anything yet always gets one pause whatever its size, because the first thing you see should not be an empty result.
Nothing else moved: no price, event, input field or output column changed, and only minutes actually transcribed are charged.
1.0.67 — 2026-09-09
Two runs started at the same time no longer erase each other's memory. The account's repeat memory is now merged on every write, so an item one run delivered stays remembered, and a later re-run hands it back instead of charging it again.
Rows that say a link is gone now say what to do about it. A media link taken from a scraped page or an earlier run's row is not a permanent address: the file is often still published while that link is not. An unreachable row now names that — conditionally, never as a claim we cannot back — and where the link carries its own expiry stamp the row says whether it had lapsed, and when. A link whose stamp has NOT lapsed is told so, so nobody re-scrapes for nothing. The same wave: a page that answers "not found" says how to check it, a file with no audio track says a re-run cannot help and what can, and a link that returned a web page names the link that would have worked.
The input form now names the sites that work and the ones that go elsewhere, before you start. The Media URLs field lists all eleven tested page sites (Apple Podcasts included — it was missing) and says that YouTube, Instagram, TikTok, Facebook, LinkedIn and Google Ads Transparency Center links belong in their own steadyfetch actor, with the row naming the actor and the field. The README's "Which links work" section opens with the same pointer. A run that would have come back all-uncharged unsupported_site is now avoidable before it is started, and if you already hold the direct media file link for one of those platforms, it still works here.
Nothing else moved: no price, event, input field or output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.66 — 2026-09-09
Apple Podcasts episode pages now transcribe, and they are on the tested list. Paste an episode link — the kind with ?i= and an episode id in it — and the actor reads the podcast's own audio address straight out of the page and transcribes it, exactly as it already did for Libsyn, Buzzsprout, Megaphone, Acast and Spotify for Creators. This has been quietly working for a while; it is now measured, kept as a test fixture and advertised, which is the only reason to rely on it. A show page is a different thing and still comes back uncharged: it lists every episode in the feed, so rather than guessing which one you meant, the row says so and asks for the episode link. Apple Music (music.apple.com) is unchanged — it streams only inside the app and there is no file to fetch.
You can no longer be charged twice for one episode that reached us two different ways. An episode fetched through its page and the very same episode pasted as its own audio address were remembered as two different things, so the second run transcribed and billed it again. They are now one item: the file itself is what your account remembers, whichever link brought it. Everything remembered before still counts — nothing was forgotten to make this work — and the change can only ever charge you less, never more.
A transcript can no longer be handed back for the wrong file. Where a link resolved through a general-purpose reader rather than a site we have measured, the only name that reader gives a file is its filename — so two unrelated recordings both called 1.mp3 looked like the same recording, and the second could have come back with the first one's transcript. It cannot now: a filename on its own is never treated as an identity, and a stored transcript is never returned unless the file it was made from matches the file you asked about.
The field name from our other transcript actors is accepted. If you were pointed here from our YouTube, Instagram, TikTok, Facebook, LinkedIn or Google Ads actor and sent your links in videoUrls, reelUrls or adLibraryUrls, those links are now read and transcribed as usual instead of the run reporting that you set none. One extra uncharged row names urls, this actor's own field, so you can switch when convenient. Nothing was renamed: urls is unchanged and every existing input keeps working exactly as before.
An over-sized file now suggests something you can actually act on. The row already said the file was over the per-item limit; it now points at the audio-only version that long meeting and conference recordings usually publish alongside the video, which is far smaller, before falling back to splitting the file.
Nothing else moved: no price, event, input field or output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.65 — 2026-09-08
A link that had simply expired no longer reads as a website blocking you. When a file came back refused on every route, the row said the host was protecting it from direct download and that re-running was unlikely to help. That is right for a site that gates its files, and wrong for the commonest case by far: a signed link from a scraper — Instagram, Facebook, TikTok and the like — that quietly aged out between the scrape and the transcription run. Those links carry their own expiry stamp, so the row now reads it. If the stamp had passed, the row says the link expired, says when, and tells you the one thing that fixes it: re-run the scraper that produced the links and transcribe the fresh ones straight away (chaining this actor onto that run avoids it entirely). A signed link whose stamp we cannot read names both possibilities and both fixes rather than picking one. A plain link with no signature reads exactly as it did before. Nothing was charged for these rows before and nothing is now.
Nothing else moved: no price, event, input field or output column changed, and you are still charged only for the audio minutes actually transcribed.
1.0.64 — 2026-09-07
A music-only or silent recording is no longer charged as a transcript. When a recording carried no speech, the transcriber sometimes invented a short caption for it — "Outro Music", "Música", "The End" — and that invented caption was delivered and billed as if it were the recording's own words. Short recordings were where it happened: below fifteen seconds the only thing standing between an invented caption and your bill was a list of exact phrases, and anything not on the list went straight through. A recording whose transcript is nothing but a sound or a card is now recognised as such at any length and ships as an uncharged no-speech row — no result fee, exactly as the store page says.
A short recording that really does speak is still delivered. Speech is now weighed against the part of the recording the words actually cover rather than the whole of its length, so a six-second recording carrying a three-word line reads as speech and not as silence. Short spoken recordings that used to come back as "no speech" are delivered.
Nothing else moved: no price, event, input field or output column changed.
1.0.63 — 2026-09-06
"txt" now means plain text. "Formats to return" takes text, segments, srt and vtt, and anything else stopped the whole run before a single item was started — nothing transcribed, nothing charged. That is still right for a value we cannot place: returning only the formats we do recognise would hand you a different set of files than you asked for, and you would pay twice to get once what you asked for. It was wrong for a value that can only mean one thing. The everyday ways of writing those same four are now simply understood — txt, plaintext and plain for text, subrip for srt, webvtt for vtt, segment for timestamped segments, and the labels the dropdown itself shows — in any capitalisation, with or without a leading dot, exactly as spacing and capitalisation have always been. Nothing is announced, because nothing about your result changed: you get the files you asked for. A value that could honestly mean two of them — "subtitles", "timestamps" — still stops the run, and now says which ones it could have meant instead of choosing for you. A value nothing could mean still stops the run in the same words it always used, and one that is a letter away from a real one is named back.
A chained run no longer charges twice for an item your account already has. Every row carries both the link you gave us and the address the file was actually served from. Chaining one run into the next followed the served address instead of your link — and your link is what your account remembers an item by. On most items the two are the same and the second run hands the transcript straight back, uncharged, which is what it did. When the host serves the file from a different address than the one you passed, the second run did not recognise it as something already transcribed for you, so it transcribed and charged for it again. A chained run now follows the link you gave, so an item you have already paid for comes back uncharged whichever way it reaches us.
Nothing else moved: no price, event, output column or status changed, waiting is still uncharged, and only delivered transcripts are charged. The only accounting that moved is a repeat that used to be missed, so this build charges less, never more.
1.0.62 — 2026-09-06
A host we cannot reach directly is no longer reported as a host that does not exist. Some media servers publish only one kind of internet address, and it is a kind our machines cannot dial straight out. The run used to demand the other kind, get "no such address" back, and file your link as a permanent mistake — so the row blamed the link, and the run's own retry through a different route never happened, because "does not exist" is not something a different route can fix. The run now prefers the address it can dial instead of demanding it, and a host it still cannot reach directly is retried through another route like any other refusal.
The "too large" row now reads like the other two. A file that ran past the 256 MB per-item limit while it was still downloading came back with a raw byte count and nothing else — no size in MB, no note that nothing was charged, no suggestion. It now names the limit in MB, says nothing was charged, and tells you what to try instead: an audio-only version, or the file split up. That is what the rows for files whose size is known up front have always said.
Nothing else moved: no price, event, output column or charge changed, waiting is still uncharged, and only delivered transcripts are charged.
1.0.61 — 2026-09-06
A block of links pasted into one row is now read as the list you meant, whatever separates them. The links box takes one link per row. Pasting a whole block of links into a single row was read as one very long address instead of the separate links — so a paste of fifty links became a single item, and because most media hosts sign their addresses, that one item was refused by the host and nothing was transcribed. A pasted block is now split back into its individual links whether they are separated by spaces, line breaks, tabs, commas, semicolons, pipes or nothing at all, so a column copied straight out of a spreadsheet works. Each link is then looked up, transcribed and charged on its own, exactly as if you had pasted them one per row.
A link that carries another link inside it is still one link. A player or redirect address holding a second address in its query or its path is left whole, not broken in two.
Text pasted around a link no longer breaks it. A number, a bullet or a note sitting beside a link is ignored and the link itself is used. A row holding no link at all still comes back as a single uncharged row saying so, rather than one row per word.
Nothing else moved: no price, event, output column or charge changed, waiting is still uncharged, and only delivered transcripts are charged.
1.0.59 — 2026-09-05
A busy site is now waited out before the lookup gives up. Every page link goes through two steps: looking the page up to find the media, then fetching it. The fetch already waited a busy host out for minutes; the lookup did not — one try, one retry two seconds later, and then "the site refused this lookup temporarily, please re-run", about three minutes into an hour you had paid for. Because the lookup comes first, a refusal there also meant the patient fetch below it never ran at all. A run that still holds time now pauses (a minute and a half to three minutes, varied so parallel runs do not retry in step) and looks the page up again, sharing one waiting budget of at most eight minutes with the fetch so a storm on one item cannot use up the wait for the rest.
Everything that is an answer still comes back at once: a removed video, a paywall, a live stream, a page with no media, a link that is not media at all, and your own time limit.
Nothing you see changed otherwise: no price, event, output column or charge moved, waiting is uncharged, and only delivered transcripts are charged.
1.0.58 — 2026-09-05
The run record now adds up. At the end of every run the items you sent are accounted for one by one: transcribed, or answered with a named uncharged reason, or explained by the setting that stopped the run — and those three always add back to the number of items you asked for. Four things could break that sum before. A run that stopped early never wrote down why, so items it did not reach were unexplained. An uncharged miss was counted once as a miss and a second time under the kind of miss it was. A link stopped by a setting we could not accept was counted twice the same way. And items an earlier part of a moved run had already delivered were missing from the count altogether.
A run that ends on one of your own settings now says which one. Your total-minutes cap, your maximum total charge, the run timeout, a move to another server, an unusable pricing setup and a resumed run that could not read back its own earlier rows are each written down under their own name, so a short run can be told from a run that simply finished the list.
Items left behind when the platform moves a run are counted. They already came back as uncharged rows; now they are also counted as what they are — items this run planned and handed to the container that continued it.
Nothing you see changed: no row, price, event, status or output column moved, and no run is charged differently.
1.0.57 — 2026-09-05
Podcast episode pages on Libsyn, Megaphone, Buzzsprout and Acast are read for the episode's own audio file, and are now tested that way. All four have been on the tested-sites list from the first build, but only their direct audio addresses were ever measured — a page link on them fell to the same general "read any page once" path an untested site gets. Each is now a tested page surface with a real public episode page kept as a fixture, so a page link is read for the file it plays and transcribed as usual, and the list can no longer promise more than the actor does. Their audio addresses keep working exactly as before, and now take the direct route without reading a page first.
A Megaphone episode page used to come back empty. Its page names the same audio file twice, once under each spelling of the web address, and the two read as two different files — so the run answered "this page lists 2 audio or video files and does not say which one you meant" and charged nothing. One file written two ways is now one file, on any page that does that.
The tested-sites table says what it means. S3 and CloudFront links were listed under "page links on tested sites"; they are direct file links, which work from any host, and they are named as that instead. The page-link list is ten sites, and every one of them is checked against a real saved episode page each time this actor is built.
Uncharged rows are told apart in the run record. An item over your size or minute limit, a file with no audio track, this run's own no-speech allowance and the two ways transcription itself can stop each carry their own reason now, instead of landing in one total. Nothing a buyer sees changed: no row, price, event or output column moved.
1.0.56 — 2026-09-05
A host that is simply busy is now waited out instead of reported. When a file could not be fetched on any network route this run had — a rate limit, a server error, a dropped connection, a challenge page in front of the file — the run gave up in seconds and asked you to re-run, while nearly all of the time you had paid for went unused, and a re-run started moments later usually met the same answer. A run that still has time now waits for that to clear (a minute and a half to three minutes, varied so parallel runs do not retry in step), then tries the whole route ladder again. Everything that is an answer rather than weather comes back at once, exactly as before: a link that is gone, a page instead of a file, a live stream, a paywalled player, a file with no audio, and a host that answered 401, 403 or 451 from every route — no waiting can change those.
The default sample can now use that waiting. Pressing Start with no links (or with only settings set) transcribes a fixed sample clip under its own time limit, and that limit was shorter than a blocked fetch plus the wait plus a second try — so on the run most people see first, the waiting above could never have happened. The limit is now sized to hold all three and the time needed to write the result: seven minutes at its very worst, while a sample that is served still finishes in seconds.
The default sample always runs live. It is no longer answered from the record of items your account already has, and its clip is not added to that record: a bare Start fetches, decodes and transcribes the sample clip and charges for it like any run, every time. Pressing Start twice therefore pays for one audio minute twice — cents — and in exchange the sample always proves the actor is working end to end. Any run that names its own links, dataset or rows is unchanged: items your account already has still come back from the run that delivered them, uncharged.
No row, price, event or output column changed.
1.0.55 — 2026-09-05
Spotify for Creators episode pages transcribe again — including the current creators.spotify.com links. A page link on Spotify for Creators (formerly Spotify for Podcasters / Anchor) came back as an uncharged "the link returned a web page" row instead of a transcript: the page lookup this actor used no longer read those pages, and the site's current address was not on the list at all. The episode page is now read directly for the audio file it plays — on creators.spotify.com, podcasters.spotify.com and anchor.fm alike — and that file is transcribed as usual. Nothing was charged for the rows that came back empty.
Any other page link is now read once for the media file it plainly embeds. If the page carries exactly one audio or video file — a podcast host's episode page, for example — that file is transcribed. A page with none, or one listing several files without saying which, comes back as an uncharged row that names what to paste instead: the direct file link, which works from any host, or a page on a tested site. The tested-site list itself is unchanged.
Spotify's own player pages say so plainly. An open.spotify.com episode or show link streams only inside Spotify's app, so there is no file to transcribe. It now comes back, before anything is fetched, as an uncharged drm_or_paywall row saying exactly that and where to paste instead. Amazon Music and Apple Music player pages are answered the same way.
Empty rows now say why, in their own words. A page on a site outside the tested list is reported as unsupported_site with the working route, never as "not a media file", and the run's outcome record keeps the kinds of uncharged miss apart.
A file refused on every network route is now called what it is. When a host answers 401, 403 or 451 for a direct file, this actor retries it through a second network route before giving up. If the second route was refused too, the uncharged unreachable row used to say the refusal was temporary and to re-run — but a hotlink check or a geo gate answers every direct fetch the same way, so a re-run walked into the same answer. That row now says the host is refusing the file to us on every route this run has, suggests a link from the host's own feed or player, and carries retryable: false. A refusal seen on only one route (the run ran out of time before the second, or a throttle) keeps the temporary wording and retryable: true.
The speech-to-text service's own pacing is honoured. When the transcription service asks this actor to wait a given number of seconds before retrying, the retry now waits that long (capped at a minute) instead of a fixed short pause, so a rate-limited item is retried when it can succeed rather than into the same limit.
1.0.54 — 2026-09-05
A media item near the run's time limit now stops cleanly instead of being cut off mid-transcription. Looking up the media, downloading it and transcribing it each take time; if the run does not have enough left, that work is not started — the item comes back as an uncharged "please re-run" row (you can also raise the run timeout) rather than a run cut short. A run that reaches its limit ends with the transcripts it already has.
1.0.53 — 2026-09-04
Narrowing one option no longer costs you the sample run. Press Start with nothing set and this actor transcribes its fixed 25-second public-domain clip so you see real output. Change one thing first — a format, a minutes cap — leave Media URLs empty, and you used to get a single row asking for links instead of any output at all: the buyer who touched nothing was served better than the buyer who narrowed an option. Now your settings are kept and the same clip runs under them, charged like any run. One extra uncharged row names which settings were yours and how to run your own media.
One option still answers with guidance instead of a transcript, and says why. The sample clip is an English recording, and Language hint tells the model what to decode, not what to filter — set to another language it would invent words that are not in the clip and charge you for them. So a hint naming another language keeps its uncharged row, and the row names the field, the reason and both ways out. auto and en run the sample as usual, and a hint this actor cannot read at all is still simply ignored with a note.
Two other things still answer with guidance, unchanged: a field name this actor does not recognise, and a Dataset ID or Dataset items you set and left blank — your list, answered with a sample, would read as if it had worked.
A row on such a run no longer opens by saying no links were given — it says which half was missing.
An unrecognised value in Formats to return still refuses the run before anything is fetched, exactly as before.
1.0.52 — 2026-09-04
A format this actor does not recognise no longer swallows your list. Setting "Formats to return" to a value the actor cannot deliver refuses the run — that part is unchanged, because returning only the formats we recognise would hand you a different set of files than you asked for. What changed is the answer: you used to get one row for the whole run, however many links you had pasted. Now the row that names the value comes first, and then every link you sent gets its own row saying it was not transcribed and which field stopped it. Nothing is fetched, nothing is transcribed and nothing is charged, exactly as before.
Every refusal now names the input field it is about — "Media URLs", "Formats to return", "Dataset ID" or "Dataset items" — in the run's own record as well as in the row you read. That is how a field that is turning people away gets noticed and fixed quickly instead of looking like a scatter of unrelated typos.
The run record now reports what you asked for, not what survived. A run where every item came back from your account's repeat memory, or where a field stopped all of them, used to report an ask of zero and could pass unnoticed. It now reports the number of items you sent.
The closing line no longer calls a good link unusable. It now keeps three facts apart: entries that were not readable links, links that were fine until a setting stopped the run, and a setting or chained dataset answered with its own uncharged row.
A few row and page outcomes this listing called "free" are named "uncharged", which is what they are — running any actor uses platform time you pay for. No facts, prices or charges changed.
1.0.49 — 2026-09-04
An item already transcribed for your account is never charged a second time. Re-running the same links used to fetch, decode, transcribe and charge for every one of them again — on this actor the most expensive repeat in the store, because a long file is many charged audio minutes. The run now recognises an item your account already has and hands it back from the run that delivered it: repeat: true, firstSeenAt, firstSeenRunId, charged: false, nothing fetched and nothing sent to speech recognition. The status line counts them and OUTPUT.repeats holds the number. Your account keeps this memory in the key-value store media-transcriber-watch-account; delete it to forget everything.
It works across link shapes. A page link is also remembered under the site's own id for the media, so the same track pasted as a share link today and as its full permalink tomorrow is one item and one charge.
If the memory cannot be read, the run still runs. It transcribes and charges as usual and says on the status line and the charged rows that the repeat check was unavailable.
Every row now carries isNew, firstSeenAt, repeat and firstSeenRunId.
A repeat comes back even when our speech service is down or your cost cap is spent — it was already paid for, so nothing stands between you and the row.
A handed-back row now carries the same hidden delivery key every other row carries, so a run the platform moves to another server mid-way cannot write it twice.
The status line is shorter. The run page cuts it at 500 characters and the tail — the caps, the reconciliation and the support line — is the half that must survive; several clauses now say the same thing in fewer words, and where a long run still will not fit, the "values this run could not use" note steps aside with a pointer to the run's OUTPUT record, which carries it in full.
1.0.47 — 2026-09-04
Rows pasted into the "Dataset ID" field are read as your rows. A paying buyer pasted a dataset's exported rows into that field; the run sent them to the platform as if they were an ID and stopped on the answer. The run now recognises pasted rows there, works them exactly like rows pasted into "Dataset items", and adds one uncharged note saying where they belong next time. Anything else that is not a dataset ID or dataset name — a link, a value with spaces, a very long value — is answered with one uncharged row naming what it looked like, before anything is sent anywhere. Nothing was charged for the runs that stopped, and nothing is charged for these notes.
A Dataset ID the platform itself cannot answer for is settled in seconds, not minutes. When the platform's own dataset lookup fails on its side, the run used to wait through the platform client's long retry schedule — close to seven minutes on one lookup — and could then run out of its own time. The lookup now gets two quick tries plus one short pause, and the run moves on to its honest uncharged row. Nothing about what is charged changed.
1.0.45 — 2026-09-04
A chained Dataset ID the run cannot read now ends as one uncharged row, whatever the reason. Until now a refusal the run did not recognise — anything other than "no such dataset" or "no access" — stopped the run within seconds with nothing delivered. Every answer now becomes an honest input_error row naming what the platform said, a momentary refusal gets one more try, a read that stops part-way keeps the rows already read and says where it stopped, and the run carries on with the rest of the input. A value that is not a dataset ID at all says so instead of reading as a temporary problem. Rows chained in an unexpected shape were already harmless and now have a test that keeps them so. Nothing was charged in any of these cases before, and nothing is now.
A run that stops on a fault of ours now tells us what kind of fault it was, so it is looked at without anyone needing to share the run. Nothing about what is delivered or charged changed.
1.0.44 — 2026-09-04
A problem inside the speech recognition service is no longer reported as a problem with your file. Every refusal from that service used to read the same way — a permanent, uncharged miss saying the media held nothing we could transcribe — so "the service changed something on its side" and "this file has no usable audio in it" looked identical in your dataset. A fault on the service's side now ships as an uncharged row marked retryable that asks for a re-run, and a file we genuinely cannot transcribe keeps the honest, uncharged miss it always had. Nothing about what is charged changed.
1.0.43 — 2026-09-04
A long file is no longer turned away before it is tried. A single item over an hour — a podcast, a webinar, a recorded call — was refused with a note about a spent allowance even on a run that had transcribed nothing yet, and a list of long files stopped after the first one. The allowance now counts only the minutes that produced nothing chargeable, so long files transcribe normally; a run of silent or unreadable media still stops at the same limit, and nothing is charged for what is not transcribed.
1.0.42 — 2026-09-04
A run that stops before it starts now reports its outcome too — a run refused for its memory setting, and a run stopped because a key on our side is missing, now reach us the same way every other run does: counts and reason codes only, never your input or your rows.
1.0.41 — 2026-09-04
Every run now reports its own outcome to us — counts and reason codes only, never your input or your rows — so a run that goes wrong reaches us even when nobody shares it.
Speech recognition on media that yields nothing chargeable is bounded per run — a small allowance that every charged minute widens, so a paid run is never cut short; past it, items ship as uncharged vendor_budget rows.
A chained Dataset ID is looked up read-only. A mistyped ID creates nothing and returns one uncharged input-error row that names the ID as the problem; a real dataset is read page by page with an honest notice past 10,000 rows.
Row notes state what happened and what was charged; the support ask lives on the run page.
1.0.40 — 2026-09-04
Run status lines fit the run page again; sample-run wording shortened.
Rows that carry no result fee are now described as uncharged, not free — running any actor still uses platform time.
1.0.39 — 2026-09-04
A run that ends with a problem now says where to reach us. A miss, an input error, an early stop or a failed run closes by pointing at the Issues tab and naming the reply time; a run that delivered everything, including one that filled the row cap you set, is left alone.
One support promise across this page — issues are answered in a couple of hours, always within a day.
1.0.37 — 2026-09-04
A run that resumes after a platform restart now recognises every row it already delivered, so nothing is delivered or charged twice.
Apify occasionally moves a running actor to another server. When that happens, the run re-reads its own dataset to remember what it already delivered. Until now it trusted the dataset's row count, which can lag for a moment after a restart; a lagging count could make the run start over and charge again for rows you already had, or stop reading before the end. The run now checks for real rows instead of trusting the count, and reads to the end whatever the count says. Rows, prices, charges and the status line on a normal run are exactly as before.
1.0.36 — 2026-09-04
Internal bookkeeping only — nothing changes in your rows, prices, charges or status line.
The record this actor keeps of its own running costs now reports itself: if it cannot be saved, the run log says so plainly instead of staying quiet, and every run's OUTPUT record carries whether it was saved. Your dataset, your charges, your prices and the status line are exactly as before.
1.0.35 — 2026-09-03
An output format we do not recognise now stops the run instead of quietly changing what you get back.
An unrecognised value in "Formats to return" used to be dropped, plain text returned in its place, and the loss mentioned only in the run summary — so a run that asked for subtitles could be charged in full for a transcript in a format you never asked for. That value now returns one uncharged guidance row naming it and the values the field accepts, and the run does nothing else: nothing is fetched, nothing is transcribed, nothing is charged. Fix the list and start again. Valid formats, an empty field and a null are all unchanged, and a Start with nothing set still transcribes the sample clip.
1.0.34 — 2026-09-03
Listing text only — nothing changes in your rows, prices, charges or status line.
The input-form and dataset screenshots now load from Apify storage instead of an outside host, so they render on the listing itself.
Every link that pointed outside Apify has been removed from the listing text.
The free n8n workflow templates still exist; the listing now says where to find them (our Apify profile) instead of linking out.
1.0.33 — 2026-09-02
Internal accounting fix — nothing changes in your rows, prices, charges or status line.
A small correction to how our own cost records count very short clips, and our cost records are now complete for every run. Your dataset, your charges and the status line are unchanged.
1.0.32 — 2026-09-02
Internal cost accounting only — nothing changes in your rows, prices, charges or status line.
Runs started from our own account now record the processing they used, so our cost reports are measured instead of estimated. Nothing is added to your run or its storage, and your dataset, your charges and the status line are unchanged.
1.0.31 — 2026-09-02
See what you are buying before you run it: real console screenshots in the listing.
The listing now shows the input form exactly as it appears in the console, and the dataset table of a real run — the 25-second sample clip with its language, word count, transcript and source link. You can see the fields you fill in and the columns you get back before starting anything.
Every link to another steadyfetch actor now carries that actor's current store name.
1.0.30 — 2026-09-02
Start with nothing set and get a real transcript instead of a placeholder row.
Clicking Start with no links used to return one demo row and no transcript. It now transcribes a fixed 25-second public-domain sample clip (the Gettysburg Address, read for LibriVox), charged like any run — one audio minute — so the first thing you see is real output: the text, the timestamps, the SRT and VTT. The status line and the row say it was the sample and how to run your own links. If the clip cannot be fetched at that moment, you still get one uncharged row explaining it, never an empty result.
1.0.29 — 2026-08-31
Runs sent from a template or an agent no longer fail before they start. Tools that build the input for you — n8n, MCP clients, agent frameworks — send null for every option you left unset. The platform used to refuse those runs outright, before this actor ever saw them. Every optional field now accepts null and reads it as "use the default": the dataset ID, pasted dataset rows, the output formats, the language hint and both minute caps. urls is the one exception — it is required and needs a real value.
A format we do not recognise is answered, not refused. "Formats to return" still offers the same four choices in the console, but a name outside that list no longer stops the run before it starts — the run goes ahead on the formats it understood and the status line names the one it dropped. Nothing about what is delivered or charged changed.
1.0.27 — 2026-08-31
A file that could not be reached is no longer handed back as "please re-run". On some runs the download opened a network route the run cannot actually use, and every retry opened the same one — so the file came back as a retryable row that re-running could not fix, while the same link downloaded fine minutes later. Downloads made from the run's own address now take the route that works. Those rows were never charged, and nothing about what is charged changed.
1.0.26 — 2026-08-30
A file with no real speech is never charged. A transcript that is only the speech engine's own filler — including the subtitle-credit lines it sometimes invents on music-only recordings — or one whose timing does not fit the measured audio, now ships as an uncharged no-speech row. Genuinely spoken recordings, in any language, are delivered and charged exactly as before.
1.0.25 — 2026-08-30
This page's pricing copy now states the price plainly: $0.003 per audio minute — the same price on every Apify plan. Nothing about what this actor delivers or charges changed.
1.0.24 — 2026-08-30
Very large files no longer risk running out of memory.
Memory use no longer grows with file size. A downloaded file is now handled on disk from the first byte instead of being held in memory, so a long or very large recording — a multi-hour podcast, a big video — takes the same memory as a short clip. Runs with several big files in a batch are the ones that gain.
Nothing lingers between items. Every item's working files are removed the moment its audio is ready, whether the item delivered or was refused.
Nothing about what this actor delivers or charges changed: a transcript is charged per audio minute exactly as before, and every uncharged row stays uncharged.
1.0.23 — 2026-08-30
A blocked or stalled stream is now retried — and reported honestly — instead of being called a broken file.
A page whose stream is refused or cut off is retried before any row ships. When a site's stream refuses to play — its parts answer 403 or 429, time out, or stop partway — the item is now tried again on the site's next-best rendition, and then once more over a fresh connection, before anything is reported. Most such items now come back transcribed.
When it still fails, the row says what happened and that a re-run is worth it. Those rows used to read "the media could not be decoded" with retryable: false — a permanent-sounding verdict for what was usually a temporary block. They now name the host's answer (for example HTTP 403) and carry retryable: true. A stream that really is gone (404 or 410) still comes back as a permanent answer.
A stream that stops partway is never delivered as a shorter transcript. A stream cut off mid-way could previously come back as a short, silently incomplete result. It is now retried and, failing that, reported as cut off — uncharged.
A host that goes silent is given up on after about half a minute of silence, instead of holding the item until its full time-out.
A blocked or throttled direct file link gets one fresh retry before its row ships. A host that served a block page, or answered 401/403/429 or a server error, is tried again over a fresh connection; the uncharged row only ships if that fails too.
A page lookup that was refused temporarily is looked up once more over a fresh connection before the item is reported. Permanent answers — removed, account-only, no media on the page — are unchanged and never retried.
Nothing about pricing changed: none of the rows above is ever charged; a transcript is charged per audio minute exactly as before.
1.0.22 — 2026-08-30
Clearer release notes — this page's notes now read more plainly. Nothing about what this actor delivers or charges changed.
1.0.21 — 2026-08-30
Suite links now point at the full live shelf — every actor named in this README is a live
store link. Nothing about what this actor delivers or charges changed.
1.0.20 — 2026-08-30
An interrupted charge can no longer fail a resumed run. If a charge left over from an interrupted run cannot be completed the moment the run resumes, the run carries on and settles it later. Nothing is ever charged twice.
A chained dataset is read in full, and a cut is always announced. Chained datasets larger than one page no longer lose rows beyond the first page: everything up to the 10,000-row ceiling is read, and a dataset holding more than that now says so plainly on the run's status line — how many rows this run processed, and how to get the remainder.
1.0.19 — 2026-08-29
Clearer wording on uncharged no-speech rows: an item whose audio turns out to be music-only or silent now says exactly that in its row note, in plain language. The same items are recognised as before, and they are still never charged.
1.0.18 — 2026-08-29
Chain it after any scraper, and hear about a setting the run could not use.
New: chain a scraper run straight into this actor. Put a finished run's Dataset ID into the new field — with Apify's Connect actor integration it fills itself — and every row is deep-scanned for one audio or video link, whatever the scraper calls it (videoUrl, audioUrl, mediaUrl, playAddr, downloadAddr, a bare file link, nested or top-level). Prefer to paste? The new Dataset items field takes the rows directly.
One media link per row, and never a double charge. A row's video and its own audio track are the same item, so they bill once; a link you also listed in Media URLs is transcribed once; thumbnails, covers and profile pictures are never mistaken for media. Rows carrying no media link are counted on the run's status line and cost nothing.
A dataset this actor cannot read is answered, not swallowed. A wrong ID — or one whose read access was never granted — comes back as a single uncharged row telling you which ID failed and what to do, and the good links in the same run