Audio & Video Transcription — Speech to Text, SRT/VTT, Any URL
Pricing
from $3.00 / 1,000 audio minutes
Audio & Video Transcription — Speech to Text, SRT/VTT, Any URL
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 10 tested sites for page links. About 90 languages, no API key of your own.
Pricing
from $3.00 / 1,000 audio minutes
Rating
0.0
(0)
Developer
Steadyfetch Team
Maintained by CommunityActor stats
0
Bookmarked
46
Total users
40
Monthly active users
16 hours ago
Last modified
Categories
Share
Audio & Video Transcription — Transcribe Any URL to Text, SRT
Any media URL to text, SRT and VTT. Unreachable, silent and unsupported items are never charged. Direct file links always work; sites only where listed. Paste a link to an .mp3, .m4a, .wav, .mp4, .mov — or a podcast, SoundCloud, Loom, Twitch VOD, Archive.org or Wistia page — and get back the transcript, the timestamps, and ready-made subtitle files. No account, no API key of your own, no subscription.
Just want to see it work? Click Start with nothing set and the run transcribes a fixed 25-second public-domain sample clip (the Gettysburg Address, read for LibriVox), charged like any run: one audio minute, $0.003. You get the text, the timestamps and the subtitle files on real audio. Paste your own links for a real run. Set only the options — a format, a minute cap — and no links, and the same clip runs under them, charged the same way, with one extra uncharged row naming which settings were yours. The one exception is Language hint: the clip is an English recording, so a hint naming another language answers with an uncharged row instead of inventing words.

| You give it | You get back |
|---|---|
| A direct audio or video file link, from any host | text — the full transcript, in the language it was spoken |
| …or a page link on one of the 11 tested sites below | srt and vtt — subtitle files, ready to upload |
| …or a whole list of them in one run | segments — start/end timestamps for every line |
| …or the Dataset ID of any scraper run you already have | language, durationSeconds, wordCount, status, chargedMinutes |
Pricing: $3.00 per 1,000 audio minutes — the same price on every Apify plan. You are charged per audio minute actually transcribed, rounded up to the next whole minute (a 90-second clip bills 2 minutes). Nothing else — no start fee, no subscription.
You never pay for the same item twice (the default sample aside — see the end of this paragraph). Every run remembers what it transcribed for your account — in a key-value store called media-transcriber-watch-account in your own Apify account — under the link you passed, under the file that link names, and, for a page link, the site's own id for the media. Re-run the same list and the items you already have come back from the run that delivered them: repeat: true, firstSeenAt, firstSeenRunId, nothing fetched, nothing decoded, nothing transcribed, nothing charged. It works across link shapes too — the same track pasted as a share link today and as its full permalink tomorrow is one item and one charge, and an episode reached through its page one day and pasted as its own audio address the next is likewise one item and one charge. Delete that store to forget everything; entries older than 90 days stop counting as repeats. If the store cannot be read on a given run, 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. The one exception is the default sample — a Start with no links, or with only settings set: it transcribes our fixed 25-second clip live every time, is neither read from nor written to that memory, and is charged like any run (one audio minute), so a second bare Start pays for it again. Every run that names its own links, dataset or rows is covered exactly as above.
What can fail, and what it costs you: nothing. The two most common non-deliveries are no_speech (the audio decoded fine but it is music, silence or a tone) and unsupported_site (a page link on a site outside the tested list below that does not plainly embed its media file). Both come back as a row with charged: false and a note saying what happened — as do unreachable, not_media, no_audio_stream, too_large, drm_or_paywall and budget_stop. You are billed for decoded audio minutes and nothing else.
Output
One JSON row per URL. Delivered rows carry the transcript and charged: true; anything that could not be delivered carries a status saying why, and charged: false. Every row also carries isNew (your account had never had this item before) and firstSeenAt; an item you already had comes back with repeat: true, firstSeenRunId and charged: false. Real rows from a verified run (long text trimmed with … for this page):

| status | charged | chargedMinutes | siteName | title | durationSeconds | language | wordCount | text |
|---|---|---|---|---|---|---|---|---|
ok | true | 1 | Loom | Sell Faster and Smarter with Loom | 53.45 | English | 118 | Ready to prospect more efficiently, close more pipeline, and create loyal champions? Loom … |
ok | true | 9 | Archive.org | 1 Laying Plans - 2 Waging War | 506.78 | English | 1133 | Part 1. Laying Plans. Sun Tzu said, 1. The Moral Law 2. Heaven 3. Earth 4. The Commander … |
no_speech | false | 0 | Wistia | Wistia Product Page - Looping Header | 15.07 | null | null | null |
The last row is a music-only clip with no voice-over: it comes back with its metadata and is not charged.
Browse a full live example dataset → — one real run, three rows: a 3-minute public-domain recording transcribed and charged for 4 audio minutes, a music-only clip returned no_speech, and a dead link returned unreachable. The run charged 4 audio minutes in total — exactly the one row that carries a transcript.
A delivered row in full:
{"url": "https://www.loom.com/share/3690d29d5e3c426eb0f7ea00f9d3ae7b","resolvedMediaUrl": "https://cdn.loom.com/sessions/transcoded/3690d29d5e3c426eb0f7ea00f9d3ae7b.mp4","sourceType": "site","siteName": "Loom","title": "Sell Faster and Smarter with Loom","durationSeconds": 53.45,"language": "English","languageConfidence": null,"text": "Ready to prospect more efficiently, close more pipeline, and create loyal champions? …","segments": [{ "start": 0.88, "end": 7.12, "text": "Ready to prospect more efficiently…" }],"srt": "1\n00:00:00,880 --> 00:00:07,120\nReady to prospect more efficiently…","vtt": "WEBVTT\n\n00:00:00.880 --> 00:00:07.120\nReady to prospect more efficiently…","wordCount": 118,"status": "ok","retryable": false,"charged": true,"chargedMinutes": 1,"note": null}
Every field is always present — explicit null over silent omission. Non-delivered rows carry a status and a note instead, plus retryable: true means the host refused us this time (a block, a throttle, a bot page) and running the same input again is worth it; false means the answer will not change.
| status | What happened | Charged |
|---|---|---|
ok | Speech transcribed | ✅ per audio minute |
no_speech | Audio decoded, but it is music, silence or a tone | ❌ |
no_audio_stream | The file is video with no audio track at all | ❌ |
not_media | The link returns an image, a feed or a live stream — not a file. If a host served a web page where the file should be, that is a block: the row comes back retryable: true | ❌ |
no_media | The page resolved but exposes no audio or video we can fetch, or lists several files without saying which | ❌ |
unsupported_site | A page link on a site outside the tested list that does not plainly embed its media file (the note names the working route, and the actor that does handle it when one exists) | ❌ |
unreachable | 404 / 410 / DNS failure — permanent. A 401, 403, 451, throttle or timeout is a block, not a missing file: same status, retryable: true — unless the same 401/403/451 came back on every network route the run has, which carries retryable: false. That last row names its cause: a signed link whose own expiry stamp had already passed says it expired and tells you to re-run the scraper that made it, while a link with no stamp says the host is refusing the file to us and points you at its own feed or player | ❌ |
drm_or_paywall | The page streams only inside its own app (Spotify's open.spotify.com player, Amazon Music, Apple Music) or needs a signed-in account — there is no public file to fetch | ❌ |
too_large | Over the per-item size limit, or over your maxMinutesPerItem | ❌ |
asr_failed / failed_download / failed_resolve | Did not complete after all attempts (also listed in the run's ERRORS record) | ❌ |
asr_unavailable | Our speech-recognition service refused this actor's access mid-run — that is on us, not you. Remaining items were answered instantly without being started; try again later | ❌ |
budget_stop | The run hit your charge cap, your minute cap, or the run timeout before reaching this item | ❌ |
input_error | The entry is not a usable link — the note says what to pass | ❌ |
This actor may fail when the platform changes things — failed items are never charged.
For AI agents & LLMs — copy this block
Actor: steadyfetch/media-transcriber (Apify Store)Does: any audio/video URL -> transcript + SRT + VTT + timestamped segments.One JSON row per URL. No login, no cookies, no third-party API key.Run: POST https://api.apify.com/v2/acts/steadyfetch~media-transcriber/run-sync-get-dataset-items?token=APIFY_TOKENInput:{"urls": ["https://example.com/episode.mp3","https://archive.org/details/some_item","https://soundcloud.com/user/track"],"datasetId": "aBcD1234efGh5678i", // optional, chain a scraper run"datasetItems": [{"videoUrl": "https://cdn/clip.mp4"}],// optional, paste rows instead"outputFormats": ["text", "segments", "srt", "vtt"], // optional, default ["text"]"language": "auto", // optional, or an ISO-639-1 code"maxMinutesPerItem": 0, // optional, 0 = no limit"maxTotalMinutes": 0 // optional, 0 = no limit}Every optional field above accepts null, meaning "use the default" — so atemplated body can send the same shape every run. Only `urls` needs a realvalue.Output one row per URL: url, resolvedMediaUrl, sourceType, siteName, title,durationSeconds, language, text, segments[], srt, vtt, wordCount,status, retryable, charged, chargedMinutes, isNew, firstSeenAt,repeat, firstSeenRunId, noteretryable=true on a non-ok row means the host blocked/throttled us —re-run the same input. retryable=false means the answer will not change.Chain datasetId takes the default dataset of ANY finished actor run. Each row isdeep-scanned for one audio/video link (videoUrl, audioUrl, mediaUrl,playAddr, downloadAddr, a bare file link — at any depth); thumbnails,covers and profile pictures are ignored. One media link per row, and alink already in `urls` is never transcribed twice. Rows with no medialink are counted on the run's status line, never charged.Pricing pay per event, platform usage INCLUDED, no start fee:audio-minute-transcribed $0.003 per audio minute — the same price on every Apify planBilled per started minute, on decoded audio. Unreachable, silent, oversized andunsupported items charge nothing at all.Cap any run with maxTotalChargeUsd (minimum $0.05) or with maxTotalMinutes.Repeats an item already transcribed for your account comes back from that run,repeat:true + firstSeenRunId, uncharged — no fetch, no ASR. The memorylives in your own key-value store media-transcriber-watch-account;delete it to forget everything (90-day window).MCP: reachable through Apify's MCP server; ask for "speech to text from a URL".
What this does not do
- No speaker labels. This returns one transcript per file with timestamps, not a who-said-what breakdown. If you need speaker separation, this is not the right actor.
- No translation. Speech is transcribed in the language it was spoken. About 90 languages are detected automatically.
- No downloading. You get text and subtitles, not a media file.
Which links work
Check this before you start, not after. A page link on a site that is not below comes back as an uncharged unsupported_site row — you lose the run, not money. The six platforms with their own steadyfetch actor (YouTube, Instagram, TikTok, Facebook, LinkedIn, Google Ads Transparency Center) are listed at the end of this section; everything else is a direct file link or one of the tested sites.
Any direct media file link, from any host. .mp3 .m4a .wav .flac .ogg .opus .aac .aiff · .mp4 .m4v .mov .webm .mkv .avi — signed CDN links and links with no file extension work too; the file is identified by its actual bytes, not by its name. That covers S3 and CloudFront links, and every podcast host's own audio address.
Page links, on these 11 tested sites only:
| Podcast hosts | Libsyn · Megaphone · Buzzsprout · Acast · Spotify for Creators (creators.spotify.com, podcasters.spotify.com, anchor.fm — formerly Spotify for Podcasters / Anchor) |
| Podcast catalogues | Apple Podcasts (an EPISODE page — podcasts.apple.com/…?i=<episode id>) |
| Archives | Archive.org |
| Audio & video | SoundCloud · Loom · Twitch VODs · Wistia |
Each of those eleven was measured on a real public episode page, and every one of those pages is kept as a test fixture, so the list cannot drift away from what the actor can actually do.
On Apple Podcasts it is the episode page that transcribes, not the show page. An episode link carries ?i=<episode id>; a show page lists every episode in the feed, so it comes back as an uncharged row asking which one you meant rather than picking one for you. Apple Podcasts is Apple's catalogue page and reads the podcast's own audio address out of it — Apple Music (music.apple.com) is a player that streams only inside the app and is refused as before.
The field name from our other transcript actors also works. If you came here from one of the sibling actors and sent your links in videoUrls, reelUrls or adLibraryUrls, they are read and transcribed exactly as if they had arrived in urls — with one extra uncharged row on the run naming urls as the field to use next time. Nothing is renamed and nothing is dropped; urls remains the actor's own field.
Any other page link is read once for the one media file it plainly embeds — the audio link its own player uses, an audio or video tag, a feed enclosure — and if there is exactly one, that file is transcribed as usual. A page with none, or one listing several files without saying which (a show page, say), comes back as an uncharged row naming what to paste instead: the direct file link, which works from any host, or a page link on a tested site. We do not claim "1,000+ sites" — this list is what was actually measured.
Spotify's own player pages — open.spotify.com/episode/…, open.spotify.com/show/… and spotify.link short links — stream only inside Spotify's app, so there is no file to transcribe. They come back, before anything is fetched, as an uncharged drm_or_paywall row saying exactly that and where to paste instead: the episode's page on Spotify for Creators or any podcast host, or its direct audio link. Amazon Music and Apple Music player pages are answered the same way.
Sites we deliberately send elsewhere. YouTube, Instagram, TikTok, Facebook, LinkedIn and the Google Ads Transparency Center each have a dedicated steadyfetch actor that does the job better and cheaper than a general transcriber can. Paste one of those page links here and the row tells you which actor to use — and if you already hold the direct media file link, paste that instead: it works.
Known limits, stated plainly. Vimeo only serves video to a signed-in account, so it cannot be transcribed from a public link. Live streams have no end and are refused before anything is spent — both a live page and a raw ICY/SHOUTcast radio URL are caught on the first few kilobytes, not after a download. Files over 256 MB come back as too_large with their measured size — point us at an audio-only version instead.
How much does it cost to transcribe audio?
One result = one audio minute actually transcribed. Billed per started minute, measured from the decoded audio, so a 90-second clip bills 2 minutes. $0.003 per audio minute — the same price on every Apify plan.
| Job | Approx. cost at $0.003/minute |
|---|---|
| A 45-minute podcast episode | ≈ $0.14 |
| 20 podcast episodes (~15 hours) | ≈ $2.70 |
| A 3-hour recorded session | ≈ $0.54 |
| Apify's $5 starter credit | ≈ 1,600 audio minutes |
Platform usage (compute + transfer) is included in the event price — the per-minute price you see is the whole price. Unreachable links, music-only audio, oversized files and unsupported pages carry no result fee.
How to transcribe an mp3 URL (no code)
- Open the actor and paste your links into Media URLs, one per line.
- Optional: tick SRT subtitles and VTT subtitles under Formats to return — they cost nothing extra.
- Optional: set Max total minutes if you want a hard ceiling on the run.
- Click Start. Each link comes back as one JSON row. Export as JSON/CSV, or read it over the API.
Leave the list empty and the actor transcribes its fixed 25-second sample clip instead (one audio minute, charged like any run), so you see the output shape on real audio. Set only the options below and no links and that clip runs under them; the settings you did not touch keep their usual values.
Chain it after any scraper
You do not have to paste links by hand. Run whatever scraper you already use, then put its Dataset ID into this actor — with Apify's Connect actor integration the field fills itself, so the two runs become one workflow.
Every row is deep-scanned for a single audio or video link, whatever the scraper calls it: videoUrl, audioUrl, mediaUrl, playAddr, downloadAddr, a bare file link, nested or top-level. Thumbnails, covers and profile pictures are never mistaken for media, a row's video and its audio track count as one item (never two charges), and a link you also listed in Media URLs is transcribed once. Rows carrying no media link are counted on the run's status line and cost nothing. If you would rather not chain by ID, paste the rows straight into Dataset items.
Page links belonging to YouTube, Instagram, TikTok, Facebook, LinkedIn or the Google Ads Transparency Center behave the same way here as anywhere else in this actor — an uncharged row naming the dedicated actor for that platform.
FAQ
How do I transcribe an mp3 URL? Paste the link into Media URLs and start the run. Any host works — the file is identified by its bytes, so signed CDN links and extension-less links are fine.
Speech to text API without a subscription? That is this actor. You pay per audio minute transcribed, with no monthly fee and no start fee, through your existing Apify account.
How do I transcribe a podcast episode to SRT? Paste the episode's audio URL (or its page on Libsyn, Megaphone, Buzzsprout, Acast or Spotify for Creators, or any episode page that plays the file), tick SRT subtitles, and the srt field comes back ready to save as a .srt file.
What happens if the link is wrong? The actor reads about 4 KB of the response and checks the actual bytes before downloading anything. A web page, an image or a dead link comes back as an uncharged row telling you what it found.
Music-only audio, or a video with no voice-over? Speech models hallucinate filler like "Thank you." over music. This actor detects that in any language and returns an honest, uncharged no_speech row instead of selling you a fake transcript.
How long can a file be? There is no duration limit. Long recordings are transcribed in overlapping windows and stitched back together with continuous timestamps; the row says when that happened. The only size limit is 256 MB per file.
Which languages? Whisper-class multilingual recognition — English, Spanish, German, Japanese, Arabic, Portuguese, Indonesian and about 90 more, detected automatically. The detected language ships on every row.
Do I need cookies, a login, or my own API key? No. The actor fetches public media only.
Can I feed it the output of another actor? Yes — put that run's Dataset ID in, or paste its rows into Dataset items. The actor finds the media link in each row itself, so you do not have to know which column your scraper used. See Chain it after any scraper above.
Why did some chained rows come back with nothing? Rows that carry no audio or video link are skipped, counted on the run's status line, and never charged — an image post or a comment row has nothing to transcribe.
Can I cap what a run spends? Two ways: maxTotalMinutes stops the run after that many charged audio minutes, and Apify's own Maximum cost per run caps the charge in dollars. Either way the run stops cleanly and the items it did not reach come back as uncharged budget_stop rows.
Also from steadyfetch
Audio & Video Transcription — Speech to Text, SRT/VTT, Any URL is the general-purpose transcriber: any direct audio or video link, or a file you host. For the platforms below, use the platform's own actor — it reads the platform's listing, carries its metadata, and costs less per row.
The rest of the steadyfetch shelf — same contract everywhere: all-inclusive pay per event, no start fee, charged only on delivery.
| Family | Actors |
|---|---|
| Ad creative intelligence | Facebook · Google Ads video · TikTok · LinkedIn · Google Ads text & OCR |
| Trends & keywords | Google Trends · Trends Now · Breakout keywords · Autocomplete keywords · Keyword volume & CPC · Social trends |
| YouTube transcripts | YouTube videos · YouTube channels |
| Reel transcripts · Profile posts | |
| Jobs | Indeed · Career sites by domain · Glassdoor · Multi-board · Google Jobs |
| Amazon | Products · Search · Bestsellers · Sellers |
| Any media file | Speech to Text · any link or file |
Free n8n workflow templates for this actor are listed on our profile website (link on our Apify profile).
Feedback & support
Found an issue? Open it on the Issues tab — we usually reply within a couple of hours, always within a day. Feature requests welcome, especially a site you would like added to the tested list.
Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/media-transcriber.
If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.