TikTok Transcript Scraper — TikTok Subtitles to Text, No Login
Pricing
from $1.00 / 1,000 videos
TikTok Transcript Scraper — TikTok Subtitles to Text, No Login
TikTok subtitles and transcripts for a creator's videos: type a handle and get every video as a row — views, likes, comments, shares, saves, caption, hashtags, duration and a playable MP4 link — with an opt-in transcript from TikTok's own captions or speech-to-text. No login.
Pricing
from $1.00 / 1,000 videos
Rating
0.0
(0)
Developer
Steadyfetch Team
Maintained by CommunityActor stats
0
Bookmarked
13
Total users
7
Monthly active users
3 days ago
Last modified
Categories
Share
Type a TikTok handle and get that creator's videos as rows: the caption, hashtags, when it was posted, how long it runs, plays, likes, comments, shares and saves, the sound it uses, the cover image and a playable MP4 link — and, on request, the spoken words of every video as a timestamped transcript. No login, no cookies, no proxy to configure. Your result limit is exact — set 30 and you get 30, and a video we could not deliver is never charged. A transcript is charged only when it was actually produced. From $1.00 per 1,000 videos on Gold and above, $2.00 on Bronze and $3.00 per 1,000 on the Apify free plan, platform usage included, nothing charged for starting a run. Press Start with nothing set and the run returns three built-in sample rows so you can see the exact output shape first — nothing is read from TikTok and there is no result fee.
Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/tiktok-profile-transcripts
- Actor id:
steadyfetch/tiktok-profile-transcripts - Input:
{ "profiles": ["natgeo"], "includeTranscripts": true, "resultsLimit": 30 }—"profiles"is the one field you have to set;"includeTranscripts"adds the spoken words. - Cap the bill: set
maxTotalChargeUsdon the run (a run option, not Actor input), e.g.0.50— the run stops when it reaches it, and the last row says what it did not deliver. - How often it changes: a busy creator posts every day and a quiet one a few times a month, so the useful pattern is the same creators again on a daily or weekly schedule — every run remembers the videos it delivered to your account, and a later run skips them by default and never charges for them again, so a schedule pays only for what is new.
Unofficial. Not affiliated with, endorsed by, or sponsored by TikTok or ByteDance Ltd.
What one row looks like
A real delivered row, built by this actor's own parser from a harvested profile feed:
{"videoId": "7683891628230315295","url": "https://www.tiktok.com/@natgeo/video/7683891628230315295","authorHandle": "natgeo","authorName": "National Geographic","authorId": "6780344874811442181","caption": "Presented by @ROLEX. Welcome to Africa 🌍 Africa Earth's Wild Home takes you deep into the heart of our wildest continent…","hashtags": ["rolex", "perpetualplanet"],"createdAt": "2026-09-10T13:04:07.000Z","durationSeconds": 72.619,"playCount": 74592,"likeCount": 10803,"commentCount": 193,"shareCount": 291,"saveCount": 576,"isPinned": true,"mediaType": "video","musicTitle": "original sound - natgeo","originalSound": true,"hasCaptions": true,"captionLanguage": "en"}
The caption is trimmed for this page; a real row carries it in full. coverUrl and videoUrl ride the same row — they are signed links that stop working after a few days, so they are not reprinted here. With Include transcripts on, the row also carries transcript, transcriptSource, transcriptLanguage and timestamped transcriptSegments. Every column is listed in the table below.
What you get
One row per video, with everything the video exposes at one price — there is no second charge for "details".
| Column | What it holds |
|---|---|
videoId · url | the video's own id and its permalink |
caption · hashtags | the description, and the tags pulled out of it |
createdAt | when it was posted, ISO-8601 UTC |
durationSeconds · mediaType · isPinned | how long it runs, whether it is a video or a photo post, and whether the creator pinned it |
playCount · likeCount · commentCount · shareCount · saveCount | engagement |
authorHandle · authorName · authorId | who posted it |
musicTitle · originalSound | the sound, and whether it is the creator's own |
coverUrl · videoUrl | the cover image and the playable MP4 link |
transcript · transcriptSource · transcriptLanguage · transcriptSegments | the spoken words, where they came from, the language, and the timestamped segments |
transcriptStatus · transcriptReason · transcriptCharged | whether a transcript was produced, why not when it was not, and whether it billed |
source · charged · chargedEvents · status · statusReason | whether this row was billed and for which events, so your invoice reconciles from the dataset itself |
Media links are signed and time-limited by TikTok, so download what you need in the same session.
What to set
Two fields say what to collect, and either one works on its own:
profiles— TikTok handles, one per line.natgeo,@natgeo,https://www.tiktok.com/@natgeoandtiktok.com/@natgeo/all mean the same creator.startUrls— the same creators in the store's own field name, as plain strings or as{ "url": "https://www.tiktok.com/@natgeo" }objects. A creator sent through both fields is collected once and charged once.
The rest only shape a run that already has a creator:
includeTranscripts(defaultfalse) — adds the spoken words to every delivered video.sortBy(default"latest") —"latest"walks the creator's grid newest first;"popular"walks TikTok's own most-played ordering for that creator, which is a different set of videos. An order this actor does not recognise is refused on an uncharged row naming the two that work — never quietly swapped for one of them.resultsLimit(default30) — videos per creator.maxItems(default1000) — videos for the whole run.maxRunSeconds(default1800) — the run stops cleanly before this and reports what is left. Up to 2,000 videos per creator, 20,000 in a run, and a clock between 30 and 3,600 seconds — ask outside any of those and the run still starts, continues at the nearest end of the range, and one uncharged row names what you asked for and what bound it.includeSeen(defaultfalse) andresumeFromDatasetId— the repeat memory described below.
Every optional field accepts null, which means "use the default", so the all-fields-null shape that templates and agent frameworks send runs exactly as a bare call does. A field name this actor does not have, or a creator list left empty, comes back as one uncharged row naming the field you meant — never as sample rows pretending the typo worked.
Price
Three charged events, and each lands only on work that was actually delivered.
video — one per delivered video row, tiered by your Apify plan:
| Apify plan | Per video | Per 1,000 videos |
|---|---|---|
| Free plan | $0.003 | $3.00 |
| Bronze | $0.002 | $2.00 |
| Silver | $0.0015 | $1.50 |
| Gold and above | $0.001 | $1.00 per 1,000 videos |
transcript — one per video whose spoken words were actually produced: $0.015 on the Free plan, $0.010 Bronze, $0.0075 Silver and Gold. It is off by default; switch on Include transcripts to get it.
long-video-surcharge — $0.005 per started minute beyond the first three minutes of a transcribed video, on every plan. Three minutes of speech are included in the transcript price, so most TikToks never touch it: a 45-second video is one transcript and nothing else, and a 5-minute video is one transcript plus two surcharge minutes.
Your Apify plan decides which column you pay, and the run itself tells you which one it was. A run billed at the Apify free plan column says so on its own run page and names what the same run costs on a paid one — the plan is Apify's, not this actor's. One thing differs on the free plan column: a daily allowance. One account reads a creator's feed at most 10 times in any 24 hours (one read is a page of about ten videos, so about 100 videos: the default 30 from one creator, three times over), and all free plan accounts share a daily allowance on this actor that resets at midnight UTC; past either, the run stops cleanly, nothing more is read or charged, and each creator it did not finish gets an uncharged row naming the reset time. Paid plans carry no daily allowance.
The arithmetic on a typical job: 30 videos with transcripts on, all of them under three minutes, on Gold = 30 × $0.001 + 30 × $0.0075 = $0.255. On Bronze the same job is $0.36, on Silver $0.27, and on the Apify free plan $0.54. Platform usage is included in all three prices — there is no separate compute bill on top, and no start fee. Set maxTotalChargeUsd on the run and it is a hard ceiling: a video and its transcript are reserved together, so the run stops cleanly under your maximum rather than half-billing a unit.
TikTok publishes its own captions for most videos, and those cost no speech-to-text at all — the words are taken straight from the caption track, and transcriptSource on the row says captions when that is where they came from and speech when the audio had to be transcribed. You pay the same either way. A video that cannot be transcribed at all — no speech in it, no audio track, an expired media link, or a photo post, which carries no video — is still delivered as a normal row with an explicit null transcript and a reason, and the transcript event is not charged for it.
Honest rows: what "not charged" actually means
Every run closes its own books. A creator that returned less than it was asked for leaves an uncharged row saying which thing happened, and none of them is billed:
profile_not_found— TikTok says the account does not exist or has been deactivated.profile_private— the account is private, so its videos are not public data.no_videos— the feed had no more videos to list for that account (from any region this actor can read), or the run stopped at this actor's own per-creator page limit. The row says which of the two it was: a limit of ours is named as ours and never dressed up as a verdict about the account, and where the limit went unfilled because the videos were ones you already have, the row names the switch that would have filled it (a token that cannot open key-value stores cannot see what you already have — the run says so and collects as usual).vendor_unavailable— the read did not go through. Temporary, says nothing about the creator, and a re-run is the fix.unsupported_shape— TikTok answered for the account, but not with a list of videos this actor can read. A re-run returns the same answer, so the row says so instead of asking for one: open the profile in a browser, and if it shows public videos there, say which handle on the Issues tab.vendor_budget·vendor_out_of_credit— this actor reached a data limit of its own (this run's data budget, which a re-run clears straight away, or its data source is paused until next month), or the data source behind it ran out of collection credit for now, and it stopped rather than collecting more. That is our limit, never yours, the row says which, and nothing was charged for it.stopped_at_limit— one of your own limits, your maximum total charge, or the run clock stopped it first. The row names which.user_input— a value that is not a creator: a link to a single video, a share link, another site's address, or an order this actor does not recognise. The row names the mistake and, where another actor of ours takes that value, names that actor too.
On a delivered video, transcriptStatus carries the same honesty for the transcript leg: no_video, no_speech, no_audio_stream, expired, transcript_too_long, failed_download, asr_failed, asr_unavailable. Each is an explicit null transcript with a reason on the row, and none of them is charged.
The last row of every run is a receipt: delivered, asked for, transcripts produced, how many came from TikTok's own captions, what was skipped, what stopped the run, and the charged-event totals for all three events.
How often the data changes
An active creator posts daily to weekly, and a video's play, like and comment counts keep moving for roughly 72 hours after it goes up. The MP4 and caption links are signed and expire within days. Re-run for fresh counts and fresh links.
Re-running the same creators does not re-buy what you already have. Every run remembers the videos it delivered to your account, in a key-value store in your own Apify account, and a later run skips them: not delivered again, not charged, never re-transcribed, and the slot goes to the next new video instead, so you still get the number you asked for. That makes a daily or weekly schedule on the same creators cost only what is new. One exception, on a run billed at the Apify free plan column: once the videos your account already holds cover what you asked for from a creator, two feed pages running of them end that creator's walk instead of paging on into older videos, and an uncharged row says so. An ask larger than what your account holds still walks on to the older videos. On that same free plan column, this actor reads one creator's feed at most once an hour for your account: a run inside that hour reads nothing for that creator and its uncharged row says when it was last checked. A run cut short by its time limit, your maximum cost per run or your result limit, or by TikTok not answering, is not that check, so the next run reads on. And on every plan, once a run has read a creator's whole feed, a later run stops at the newest video that run saw, because everything behind it was read already — so a re-run with nothing new posted reads a single page. The memory is a key-value store in your own Apify account, so a run started with a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission, or Actor runs set to Full access (Write is what lets the run remember what it delivered; without it every run pays again); without it the run says so on its status line and on an uncharged row of its own, and cannot skip what you already have. Switch Include videos you already have on to get them back anyway, marked repeat: true and still uncharged, and use Skip videos in this dataset to carry the same memory across accounts.
Reliability
The videos come from a licensed data feed rather than from scraping TikTok's own web pages, which is why there is nothing to log into and nothing to configure. When that feed cannot answer, the run says so on an uncharged row and finishes successfully — a failed read is never billed and never dressed up as an empty profile. Transcripts come from TikTok's own caption track where the video has one, and from a speech-recognition service over the video's audio where it does not; when neither can answer, the video is still delivered and only the transcript is left null.
If this actor is not the door you want
A link to a single TikTok video is refused here with an uncharged row, because this listing is the profile door: it takes a creator and returns that creator's videos. Paste single video links into our media transcriber instead — it takes a video link or a direct media file from any host and returns the transcript.
Use it as an API endpoint (Standby)
You can also call this actor like an API: send one TikTok creator and get their videos back in the same response, with no run to start and no dataset to read. The address is https://steadyfetch--tiktok-profile-transcripts.apify.actor, and the actor's Endpoints tab lets you try it from the browser.
curl -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \"https://steadyfetch--tiktok-profile-transcripts.apify.actor/?profile=natgeo&maxVideos=10&includeTranscripts=true"
| Setting | What it does |
|---|---|
profile | One TikTok handle, @handle or profile link. One creator per request. |
maxVideos | How many videos to return. 30 if you leave it out; at most 200 per request, or 25 with transcripts (30 and 10 on the Apify free plan). |
includeTranscripts | true adds each video's spoken words, exactly as a run does. Off by default. |
sortBy | latest (the default) or popular. |
The answer is JSON: videos (each with the same fields as a row in a run's dataset), delivered, charged, summary (the run's own receipt row) and notes for anything that was not returned, in plain words. A request is charged with the same events and the same per-event prices as a run with the same settings: one video per video returned, one transcript per transcript delivered, and the long-video minutes past the first three. A request that returns no videos is not charged, and if the charge for a request's videos cannot be completed, its videos are not sent and nothing is charged.
Good to know: the endpoint does not skip videos your account already has — every request returns the videos it reads. To skip what you already collected, or for many creators at once, run the actor as usual.
Limits and good manners
Only public accounts are returned; a private account comes back as an uncharged row saying so. Transcripts are capped at 30 minutes of speech per video, and a longer video is delivered with its transcript left null and a reason. Photo posts are delivered as rows like any other post, with no transcript and no transcript charge. This actor may stop working if TikTok changes how the data is served; if it does, failed items are never charged.
If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.