Short Video Transcriber - TikTok, Reels & Shorts to Text, SRT
Pricing
from $1.70 / 1,000 transcript from native captions
Short Video Transcriber - TikTok, Reels & Shorts to Text, SRT
Transcribe TikTok videos and YouTube Shorts to text, timestamped segments, SRT and VTT. Uses each platform's native captions when they exist and falls back to Whisper speech-to-text when they don't. No API key, no cookies.
Pricing
from $1.70 / 1,000 transcript from native captions
Rating
0.0
(0)
Developer
Saulius Saulenas
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Short Video Transcriber — TikTok, Reels & Shorts to Text, SRT
TikTok and YouTube Shorts to transcript, SRT and VTT. Native captions when they exist, Whisper when they don't.
Paste short-form video URLs, get back the words. Every video comes back as plain text, timestamped
segments, a ready-to-use .srt subtitle file, a .vtt file, and the basic source metadata —
author, caption, hashtags, duration, language.
The Actor reads each platform's own caption track first and only runs Whisper speech-to-text on videos that have none. That is why it is fast, and why videos with captions cost a fraction of what a transcribe-everything tool charges.
- No API key, no login, no cookies.
- 90+ languages. The transcript comes back in the video's own language by default.
- Bulk-safe. One dead link never breaks the rest of the run.
- You only pay for delivered transcripts. Failures, private videos and over-length videos are free.
Supported platforms
| Platform | Video URLs | Native captions | Whisper fallback |
|---|---|---|---|
| TikTok | tiktok.com/@user/video/123…, vm.tiktok.com/… share links | ✅ creator captions and TikTok's own auto-captions | ✅ |
| YouTube Shorts | youtube.com/shorts/ID, youtu.be/ID, youtube.com/watch?v=ID | ✅ creator captions and YouTube auto-captions | ✅ |
| Instagram Reels | — | — | — |
Instagram Reels URLs are recognised and rejected with a clear message. Reels support ships as its own Actor; this one does not pretend to handle it.
What you get
One dataset row per video:
{"url": "https://www.youtube.com/shorts/BDf5mOfSkkw","platform": "youtube_shorts","videoId": "BDf5mOfSkkw","author": "UCUvUKGDBCqJzV4OwJkQWCHg","authorName": "King Arthur Baking Company","title": "The Only Sourdough Recipe You'll Ever Need","hashtags": ["baking", "sourdough"],"durationSeconds": 15.0,"language": "en","source": "native_captions","text": "I'm going to show you how to make the only sourdough bread that you need to know…","segments": [{ "start": 0.0, "end": 1.14, "text": "I'm going to show you how to make the" },{ "start": 1.14, "end": 2.46, "text": "only sourdough bread that you need to" }],"srt": "1\n00:00:00,000 --> 00:00:01,140\nI'm going to show you how to make the\n\n2\n…","vtt": "WEBVTT\n\n00:00:00.000 --> 00:00:01.140\nI'm going to show you how to make the\n\n…","wordCount": 62,"segmentCount": 9,"isAutoGenerated": true,"sourceDetail": "youtube_shorts_auto_captions","viewCount": 184231,"publishedAt": "2026-02-14T10:03:11+00:00","status": "ok","error": null}
source is always exactly one of:
native_captions— the words came from the platform's own caption track.whisper_asr— the video had no usable captions, so the audio was transcribed with Whisper.
The srt and vtt fields are complete, valid subtitle files. Overlapping caption cues from
auto-generated tracks are trimmed, so subtitles do not double up on screen.
How it works
For every URL:
- Identify the platform and reject anything that is not a single short-form video, with a specific reason.
- Resolve the video — title, author, hashtags, duration, view count.
- Check the duration against your cap before doing anything expensive.
- Try native captions. If the platform has a usable track, normalise it into text, timed
segments, SRT and VTT. Charged as
caption_transcript. - Otherwise download the audio and run Whisper (faster-whisper, CPU, int8). Charged as
asr_transcript.
A video is charged for exactly one of those two events, after the row is written — never both, never before.
Pricing
| Event | Price | When it fires |
|---|---|---|
caption_transcript | $0.002 | A transcript was produced from the platform's native captions. |
asr_transcript | $0.040 | A transcript was produced by Whisper because there were no captions. |
apify-actor-start | $0.00005 | Apify's standard start event, charged once per gigabyte of the run's memory. This Actor runs at 2 GB, so $0.0001 per run. |
Those are the Free-plan prices. Apify's paid plans get its standard Store discount off every
event — Bronze 5 %, Silver 10 %, Gold and above 15 % — so asr_transcript costs $0.038, $0.036 or
$0.034 on those plans and caption_transcript costs $0.0019, $0.0018 or $0.0017.
Whisper costs about a second of CPU for every 1.5 seconds of audio, which is why it is priced twenty times the caption rate. Most videos never touch it.
Nothing else is charged. In particular you are not charged for:
- videos that are deleted, private, region-locked or age-restricted;
- videos longer than your
maxDurationSecondscap; - caption-less videos longer than
maxAsrDurationSeconds(they returnASR_DURATION_EXCEEDED); - videos where Whisper finds no speech at all (music-only clips);
- malformed URLs or unsupported platforms;
- any run that fails.
What a run actually costs
Caption availability is what moves the bill. On short-form video it varies a lot by niche — talking-head, educational and news content is largely captioned; dance, music and meme content often is not.
| 1,000 videos | Cost |
|---|---|
| All have captions | $2.00 |
| 90 % captions / 10 % Whisper | $5.80 |
| 70 % captions / 30 % Whisper | $13.40 |
| None have captions | $40.00 |
(Free-plan prices; a Gold plan pays 15 % less than each of these.)
Turn "Transcribe with Whisper when there are no captions" off to cap the price at $0.002 per
video: caption-less videos then return a CAPTIONS_UNAVAILABLE error, free of charge, and you can
decide what to do with them.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
videoUrls | array | — | Required. TikTok video URLs and YouTube Shorts URLs. |
language | string | (blank) | Two-letter code (en, es, pt…). Blank keeps the video's own language — usually what you want for a transcript. Setting it selects that language's caption track when the platform offers one (which may be a translation) and tells Whisper what to expect. |
allowWhisperFallback | boolean | true | Off = captions only, and caption-less videos cost nothing. |
maxDurationSeconds | integer | 600 | Videos longer than this are skipped, free. Max 1200. |
maxAsrDurationSeconds | integer | 180 | Length limit for the Whisper path only. Caption-less videos longer than this return ASR_DURATION_EXCEEDED, free. Captioned videos are unaffected. |
maxVideos | integer | 1000 | Safety cap on the number of URLs processed. |
forceWhisper | boolean | false | Transcribe with Whisper even when captions exist. Charged at the asr_transcript rate. |
whisperModel | string | base | tiny, base or small. Only affects the Whisper fallback. |
beamSize | integer | 1 | Whisper decoding beam width. |
concurrency | integer | 5 | Videos resolved in parallel. Whisper always runs one at a time. |
includeFailedItems | boolean | true | Keep a row (with an error code) for every failure, so inputs reconcile to outputs. |
useResidentialFallback | boolean | true | Retries through a residential IP when a platform answers the normal proxy with a bot check. YouTube needs this. |
proxyConfiguration | object | Apify Proxy | Recommended. Used for the first attempt on every request. |
Minimal input
{"videoUrls": ["https://www.tiktok.com/@nasa/video/7189113838831488302","https://www.youtube.com/shorts/BDf5mOfSkkw"]}
Run it
In the Apify Console
Open the Actor, paste your URLs into Video URLs, one per line, and click Start. Results
appear in the dataset and can be exported as JSON, CSV, XLSX or Excel — or you can download the
srt column straight into a subtitle file.
API
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~short-video-transcriber/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"videoUrls": ["https://www.tiktok.com/@nasa/video/7189113838831488302","https://www.youtube.com/shorts/BDf5mOfSkkw"],"maxDurationSeconds": 600}'
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('YOUR_USERNAME/short-video-transcriber').call({videoUrls: ['https://www.tiktok.com/@nasa/video/7189113838831488302','https://www.youtube.com/shorts/BDf5mOfSkkw',],});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const item of items) {if (item.status !== 'ok') {console.warn(`${item.inputUrl}: ${item.error}`);continue;}console.log(`${item.author} (${item.source}): ${item.text}`);// Save subtitles next to your video files:// await fs.writeFile(`${item.videoId}.srt`, item.srt);}
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("YOUR_USERNAME/short-video-transcriber").call(input={"videoUrls": ["https://www.tiktok.com/@nasa/video/7189113838831488302","https://www.youtube.com/shorts/BDf5mOfSkkw",],"allowWhisperFallback": True,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["status"] != "ok":print(f"{item['inputUrl']}: {item['error']} — {item['errorMessage']}")continueprint(item["author"], item["language"], item["wordCount"], "words")with open(f"{item['videoId']}.srt", "w", encoding="utf-8") as handle:handle.write(item["srt"])
n8n, Make and Zapier
The Actor works with the standard Apify → Run Actor node in n8n and the Apify → Run an
Actor module in Make. Point the node at this Actor, pass videoUrls as the input JSON, then read
the dataset items in the next step — text for the transcript, srt for a subtitle file, error
to branch on failures.
A common shape: a spreadsheet or webhook supplies URLs → this Actor returns transcripts → an LLM
node summarises, translates or tags them → the result is written back. Because failures come back
as rows with an error field rather than as a broken run, a filter on status == "ok" is all the
error handling most workflows need.
Errors
Failed videos get a dataset row with status: "error", an error code and a plain-English
errorMessage. They are never charged.
| Code | Meaning |
|---|---|
VIDEO_UNAVAILABLE | Deleted, never existed, or region-locked. |
VIDEO_PRIVATE | Private, friends-only or followers-only. |
DURATION_EXCEEDED | Longer than maxDurationSeconds. |
ASR_DURATION_EXCEEDED | No captions, and longer than maxAsrDurationSeconds. Raise that limit to transcribe it anyway. |
CAPTIONS_UNAVAILABLE | No native captions and the Whisper fallback is turned off. |
NO_SPEECH_DETECTED | Whisper found no speech — a music-only or silent video. |
UNSUPPORTED_PLATFORM | Not a TikTok or YouTube Shorts URL. |
INVALID_URL | Not a link to a single video (a profile or channel URL, for example). |
MEDIA_UNAVAILABLE | The audio could not be downloaded for transcription. |
BLOCKED / RATE_LIMITED | The platform refused the request. Try again, ideally with a proxy. |
MEMORY_LIMIT | The video does not fit in the run's memory for the chosen model. Raise the memory or pick a smaller model. |
Limits and good to know
- Short-form only. The default cap is 600 seconds and the maximum is 1200. For podcasts, webinars and long YouTube videos, use a general-purpose transcriber instead.
- Memory. Run with at least 2 GB. That covers the whole duration range with the
basemodel; the Actor refuses an impossible combination up front instead of being killed mid-run. - Auto-generated captions are auto-generated. When a transcript comes from a platform's own ASR
track (
isAutoGenerated: true), it carries that track's mistakes. For the highest accuracy on a specific video, turn captions off in your workflow and let Whisper do it — or use thesmallmodel. - Public videos only. No login, no cookies, no private content.
- Caption availability changes. Platforms backfill captions hours after upload, so the same video can take the Whisper path today and the caption path tomorrow.
- Leave the residential retry on for YouTube. YouTube refuses datacenter IPs outright ("Sign
in to confirm you're not a bot"). The Actor retries those requests through a residential IP
automatically; with
useResidentialFallbackoff, YouTube Shorts returnLOGIN_REQUIRED. TikTok does not need it. - Whisper is length-limited; captions are not. A caption-less video is transcribed up to
maxAsrDurationSeconds(180 s by default — the length of the longest YouTube Short, and longer than the overwhelming majority of TikToks). Past that, Whisper's cost outgrows a per-video price, so the video returnsASR_DURATION_EXCEEDEDand costs you nothing. Raise the limit if you want those anyway. Videos with captions run to the fullmaxDurationSecondsat the $0.002 rate, whatever their length.
What this Actor does not do
It transcribes videos. It does not scrape comments, profiles, followers or hashtags, does not monitor accounts, and does not summarise, translate or analyse sentiment — pipe the transcript into an LLM step for that.
Related searches
video to text · video transcript · transcribe video · TikTok transcript · TikTok to text · YouTube Shorts transcript · Shorts to text · caption extractor · subtitle downloader · SRT generator · VTT subtitles · speech to text · Whisper transcription