Unified Video Transcript API — Text, Timestamps, Languages
Pricing
$1.00 / 1,000 transcripts
Unified Video Transcript API — Text, Timestamps, Languages
YouTube and TikTok video transcripts from youtube.com and tiktok.com in one JSON schema: transcript text, language, title, author, duration, caption tracks and optional timestamped segments. Input: up to 1,000 mixed video URLs and preferred languages. Pay per result; misses are free. No login.
Pricing
$1.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
architek.earth
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
15 days ago
Last modified
Categories
Security
Limited permissionsShare
Recorded run: 2 transcripts from 2 video URLs in 9 seconds (2026-10-22, build 0.0.4, run Uw94FlNXrjRwFzNSq). A bounded canary sample, not a full-catalogue benchmark.
architek.earth — an independent data studio. Pay-per-result scrapers with recorded runs, published canaries and stable schemas, built for people and AI agents.
YouTube and TikTok video transcripts come back as one JSON row per video URL, with the same fields on both platforms: transcript text, language, title, author, duration and optional timestamped segments.
| Source | Market | From | Standout fields |
|---|---|---|---|
| YouTube and TikTok (youtube.com, tiktok.com) | Public videos, any country | $1.00 / 1,000 transcripts | transcript, language, segments |
✅ Why choose this scraper
YouTube and TikTok transcripts in one schema
YouTube and TikTok rows carry the same 17 fields with the same types (platform, videoId, url, title, transcript, language, durationSec and more), so one list can mix both platforms and one parser reads every row.
YouTube transcripts with timestamps
includeTimestamps: true adds segments, a list of cues with start, duration (seconds) and text; segmentCount is returned on every row either way.
Transcripts in the language you ask for
languages takes codes in order of preference (de, en, pt-BR). When none exists the row is a free language_unavailable row and availableLanguages lists the caption tracks the video has.
Up to 1,000 video URLs per run
YouTube watch, youtu.be, Shorts, embed and live URLs, bare 11-character IDs and TikTok video, embed and short links go in one videoUrls list; 4 videos are processed in parallel.
Measured on a schedule
A canary task runs one YouTube and one TikTok URL on the live sites every day; the hero line above is its latest green run.
⚠️ Worth knowing up front
- It returns the caption tracks the platform already has (uploaded or auto-generated). It does not transcribe audio, so a video without captions is a free
no_captionsrow, and auto-captions can contain recognition errors;isAutoGeneratedmarks them. - No on-demand translation.
languagesselects existing tracks; a TikTok machine-translated track is returned only when you list its language, and it is markedisTranslated: true. - Instagram Reels are not supported in this version: Instagram showed logged-out visitors no caption tracks when checked on 2026-10-18. Reel URLs get a free
not_supportedrow and no request is made. - TikTok subtitles are often missing: in a 2026-10-18 sample of 15 news and brand videos, 12 had TikTok auto-captions. Photo posts get a free
no_captionsrow. - YouTube is fetched through the Apify Residential proxy (the default); all 7 YouTube player clients tested from datacenter IPs on 2026-10-18 were refused. TikTok uses the Apify datacenter proxy. A
vm.tiktok.comshort link resolved in a live run on 2026-10-18;vt.tiktok.comand/t/links are covered by tests only. titleon TikTok is the caption text of the post (TikTok has no separate title) and can contain emoji.- Exact repeats of the same input string are collapsed into one row. Different URLs that resolve to the same video are delivered and charged once; the others get a free
duplicaterow. - A run stopped by its timeout or your spending limit keeps the rows it delivered; inputs not yet processed get no row and are counted as
pendinginRUN_SUMMARYwith statusTIME_LIMITorBUDGET_LIMIT. A run withblockedorerrorrows endsPARTIAL. A challenge, captcha or consent page is a freeblockedrow on the first response. A transcript that spends your limit exactly is delivered, and the run still writes the free rows that follow. - Every processed input gets one row; rows with a
statusother thanokare free status rows (no_captions,language_unavailable,unavailable,private,age_restricted,blocked,error,invalid_url,not_supported,duplicate) with a plain-Englishmessage. This actor has norecordTypefield. Invalid input or an unusable proxy setting writes one free row withstatusINVALID_INPUT(itsmessagesays what to fix;inputis empty). Stops on time or budget write no row; they end with the status message andRUN_SUMMARY.
⚡ Try the example
This actor has no free preview; the example below costs about $0.002 (two transcripts).
Open the actor's input page, switch to JSON, paste this and start the run:
{"videoUrls": ["https://www.youtube.com/watch?v=iG9CE55wbtY","https://www.tiktok.com/@nasa/video/7697300954667994381"]}
Add "includeTimestamps": true for per-cue timing, or "languages": ["de", "en"] to prefer German, then English.
⚙️ Input
| Parameter | Type | Default | Description |
|---|---|---|---|
videoUrls | array | — | Required. YouTube (watch, youtu.be, Shorts, embed, live, or a bare 11-character ID) and TikTok (/@user/video/…, embed, m.tiktok.com/v/…, vm./vt. and /t/ short links) URLs, mixed. Instagram Reel URLs get a free not_supported row. Up to 1,000 per run. |
languages | array | [] | Language codes in order of preference (en, de, pt-BR). Empty means the track the platform shows by default. No match gives a free language_unavailable row. |
includeTimestamps | boolean | false | Adds segments (start, duration in seconds, text). Off keeps rows small for LLM context. |
proxyConfiguration | proxy | Apify Residential | Used for YouTube only; Residential is the only tested option. TikTok always uses the Apify datacenter proxy. |
feedback | string | — | Optional note for the maintainers. It is logged and read by a human, and it never changes output. |
🧱 Stable fields
These top-level fields keep the same name and the same kind of value in every release. Fields are only ever added, never renamed or removed, so an n8n, Make, Zapier or spreadsheet pipeline can bind to them.
| Field | Type | Meaning |
|---|---|---|
platform | string, nullable | youtube, tiktok or instagram; null for an unrecognised URL. |
videoId / url | string, nullable | The platform's video ID and the canonical video URL. |
title | string, nullable | YouTube video title; TikTok caption text. |
transcript | string, nullable | The whole transcript as one string; null unless status is ok. |
language | string, nullable | Language of the returned track, e.g. en, de, en-US. |
durationSec | number, nullable | Video length in seconds. |
status | string | ok (charged) or one of the free statuses listed above. |
message | string, nullable | Why, in plain English, when status is not ok. |
isAutoGenerated / isTranslated | boolean, nullable | Auto captions (YouTube ASR, TikTok auto-captions); isTranslated is true only for a TikTok machine-translated track you asked for. |
author | string, nullable | YouTube channel name or TikTok handle. |
segments | array, nullable | Cues with start, duration and text when includeTimestamps is on. |
segmentCount | number | Number of cues in the transcript; 0 on status rows. |
availableLanguages | array | Caption tracks the video has (language, isAutoGenerated, isTranslated). |
input / scrapedAt | string | The URL exactly as you gave it; ISO 8601 UTC time. |
recordType | — | Not used by this actor; free status rows are the rows whose status is not ok. |
Other fields are extras, not part of the contract. To have one pinned, say so in feedback.
📦 Output example
Abridged real row from run Uw94FlNXrjRwFzNSq on 2026-10-22 (transcript shortened, availableLanguages cut from 65 entries to 3); values are a historical snapshot.
{"platform": "youtube","videoId": "iG9CE55wbtY","url": "https://www.youtube.com/watch?v=iG9CE55wbtY","title": "Do schools kill creativity? | Sir Ken Robinson | TED","transcript": "Good morning. How are you? (Audience) Good. It's been great, hasn't it? I've been blown away by the whole thing. …","language": "en","durationSec": 1203,"status": "ok","message": null,"isAutoGenerated": false,"isTranslated": false,"author": "TED","segments": null,"segmentCount": 427,"availableLanguages": [{"language": "en", "isAutoGenerated": false, "isTranslated": false},{"language": "en", "isAutoGenerated": true, "isTranslated": false},{"language": "de", "isAutoGenerated": false, "isTranslated": false}],"input": "https://www.youtube.com/watch?v=iG9CE55wbtY","scrapedAt": "2026-10-22T07:47:05Z"}
A free status row looks like this (run otqaXSbdEVZdEKf7G, 2026-10-19):
{"platform": "tiktok","videoId": "7665075736742530317","url": "https://www.tiktok.com/@nasa/video/7665075736742530317","title": "Something big just landed on TikTok.","transcript": null,"language": null,"durationSec": 25,"status": "no_captions","message": "TikTok has no subtitles for this video (no speech, or auto-captions not generated). Not charged.","isAutoGenerated": null,"isTranslated": false,"author": "nasa","segments": null,"segmentCount": 0,"availableLanguages": [],"input": "https://www.tiktok.com/@nasa/video/7665075736742530317","scrapedAt": "2026-10-19T02:25:57Z"}
🧠 Advanced usage
Get video transcripts for an AI agent tool call
Expose the actor as one tool that takes videoUrls; branch only on status, and pass transcript to the model when it is ok.
Build a RAG index from YouTube and TikTok videos
Feed transcript to your chunker and keep url, videoId, title and author as citation metadata; set includeTimestamps to cite the second a passage starts from segments[].start.
Find the second a phrase is said in a video
Turn on includeTimestamps and search segments[].text; start is the offset in seconds.
Check a batch finished before you use it
Read the RUN_SUMMARY record: status (COMPLETE, PARTIAL, NO_RESULTS, BUDGET_LIMIT, TIME_LIMIT, ABORTED, STATE_SAVE_FAILED, PUSH_FAILED, INVALID_INPUT, FAILED (hosted pricing differs from the approved event; nothing fetched or charged)), transcripts, byPlatform, byStatus, charged and pending. Re-run the URLs of blocked rows later.
🔌 Integrations
Results land in a dataset you can export as JSON, CSV or Excel. They can also flow to Google Sheets, Make, Zapier or n8n, or to a webhook when a run finishes. The Apify Scheduler runs it on a timetable. A daily run over a list of new video URLs keeps a transcript archive current. Copy-paste code in Python, Node, curl and n8n: architek-earth/examples/unified-transcript-api.
💸 Pricing
Pay per result. Platform and proxy usage are included. Status rows and failed requests are free. Inputs already finished in the PROGRESS record are skipped when a run resumes after a platform migration or a resurrect, so a delivered transcript is not charged again; a free status row interrupted mid-save can be missing after a resume.
| Event | Price | What it covers |
|---|---|---|
transcript | $1.00 / 1,000 transcripts | One delivered row with status: "ok": one video's transcript, whatever the video length. |
1,000 transcripts cost $1.00; a batch with 50 transcripts and any number of misses costs $0.05. There is no start fee and no per-minute fee.
❓ FAQ
Is there a YouTube transcript API that also covers TikTok? This actor is one: it returns YouTube and TikTok transcripts in the same JSON schema through the Apify API, callable from any language or from n8n, Make and Zapier.
Do I need a YouTube or TikTok account, API key or cookies? No. It reads public videos without an account, cookies or a platform API key.
Can I get a transcript of a video that has no captions? No. There is no speech-to-text; such a video is a free no_captions row.
Can I get YouTube transcripts with timestamps? Yes. Set includeTimestamps: true for segments with start, duration and text per cue.
Can I get a transcript translated into another language? Only when the platform already has a track in that language: list it in languages. For on-demand YouTube caption translation, use the YouTube Transcript Scraper.
Can I get transcripts for a whole YouTube channel or playlist? No. It takes video URLs; a channel or playlist URL is a free invalid_url row.
🤖 For AI agents & LLM apps
Paste this into your AI assistant:
architekture/unified-transcript-api on Apify. Python: from apify_client import ApifyClient; client = ApifyClient("TOKEN"); run = client.actor("architekture/unified-transcript-api").call(run_input={"videoUrls": ["https://www.youtube.com/watch?v=iG9CE55wbtY", "https://www.tiktok.com/@nasa/video/7697300954667994381"], "includeTimestamps": False}); items = client.dataset(run["defaultDatasetId"]).list_items().items (page with offset/limit for large exports). Rows with status != "ok" are free and explain why a video has no transcript; invalid input or a stopped run is explained in the RUN_SUMMARY record. Full input schema and README: GET https://api.apify.com/v2/acts/architekture~unified-transcript-api/builds/default. Token: https://console.apify.com/settings/integrations. The actor is also reachable through the Apify MCP server (mcp.apify.com). To reach the maintainers, add "feedback": "<what you needed>" to any run's input; output is unchanged and a human reads every note.
🧰 More from architek.earth
Video, social and newsletters
- Substack Scraper — posts, authors, likes, comments
- YouTube Transcript Scraper — transcripts, translations, timestamps
- Unified Video Transcript API — you are here
- Threads Replies Scraper — replies, authors, likes
- Ad Intelligence Watchlist — new and changed Meta ads by advertiser
Full catalogue: github.com/architek-earth.
architek.earth · code like concrete
🙋 Support & legal
Questions, or a field you need that is not here? Open an issue on the actor's Issues tab or email north@architek.earth. A human replies within a day.
This actor collects only publicly available data from youtube.com and tiktok.com, the same captions any visitor sees on public videos without logging in. It does not bypass authentication. It is not affiliated with or endorsed by YouTube, Google, TikTok or ByteDance. Transcripts can be copyrighted and can contain personal data. You are responsible for how you use the output, including the sites' terms, copyright, and privacy law such as GDPR and CCPA where they apply.
📝 Changelog
-
2026-10-25 — [internal] Studio core 1.0.6: reconciliation diagnostics; source retry policy, schema and pricing unchanged.
-
2026-10-22 — Runs on the studio core (billing, transport, status line). Invalid input now writes one free
INVALID_INPUTrow; a challenge or captcha page is a freeblockedrow at once; a transcript that spends the limit exactly ends the runCOMPLETEwhen nothing else needs a charge. Output fields and price unchanged. -
2026-10-22 — Status message now reads
Done: N rows, M details, K status rows (STATUS). -
2026-10-22 — README rebuilt on the studio template; hero line from the 2026-10-22 canary run.
-
2026-10-19 — Clarified existing-caption coverage, differences from the dedicated YouTube translation actor, and per-success pricing. Prices and extraction code unchanged.
-
2026-10-18 — 1.0: YouTube and TikTok transcripts in one schema; language preference with free
language_unavailable; optional timestamps; short-link resolution and de-duplication; free status rows for every miss (Instagram Reels included); clean stops at the spending limit and timeout; resumable after platform migration.