X (Twitter) Video Transcript API – Captions, AI Transcription
Pricing
from $1.00 / 1,000 results
X (Twitter) Video Transcript API – Captions, AI Transcription
Extract transcripts from public X/Twitter videos with native captions first and AI speech-to-text fallback. Get text, timestamps, SRT, WebVTT, optional translation, and structured metadata for AI, research, monitoring, and automation.
Pricing
from $1.00 / 1,000 results
Rating
0.0
(0)
Developer
LanceAPI
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
X (Twitter) Video Transcript API
Extract native X/Twitter video captions when available, automatically fall back to AI speech recognition when needed, and optionally translate the completed transcript.
Turn public X/Twitter video posts into clean text, timestamped segments, WebVTT, SRT, translations, post metadata, and structured JSON for research, media monitoring, subtitle workflows, AI agents, and automation.
- 🎯 Native captions first — reuse a usable caption track exposed by X whenever possible.
- 🤖 AI fallback — transcribe accessible video audio when captions are unavailable or unusable.
- 🌍 Optional translation — keep the original transcript and add a translated version in one of 20 target languages.
- 📝 Text, segments, VTT, and SRT — request one or several formats without running transcription more than once.
- 📦 Batch processing — process up to 1,000 X/Twitter post URLs or Tweet IDs per run.
No X cookies or user-supplied transcription or translation API keys are required for normally accessible public posts. Private, restricted, or login-gated content may still be unavailable.
✨ Key features
- Native captions first — avoid unnecessary AI speech recognition when a readable native X caption track already exists.
- Automatic AI transcription fallback — videos without usable captions can still produce transcripts without a separate workflow.
- Optional transcript translation — translate native or AI transcripts while preserving the original text and segment timing.
- Keep your original transcript — a translation problem does not throw away a successfully generated transcript. API users receive
status: "partial_success". - Flexible transcript formats — return complete text, timestamped segments, normalized WebVTT, SRT, or any combination.
- Three transcription modes — use recommended Auto mode, require native captions, or force AI transcription.
- One result per video — a post containing multiple videos produces one independently traceable dataset item per video.
- Consistent structured output — native captions, AI transcripts, translations, and failures follow a predictable schema.
🚀 Quick start
{"urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"],"transcriptionMode": "auto","language": "auto","formats": ["text", "segments"]}
In Apify Console:
- Paste one or more public X/Twitter video post URLs or Tweet IDs.
- Keep Auto — X captions first, AI fallback selected.
- Choose the transcript formats you need.
- Optionally enable translation and choose a target language.
- Click Start, then open Transcript results.
Short output example
{"success": true,"status": "success","tweetId": "2095300108095046051","transcriptSource": "native","language": "EN","transcriptText": "we have to give that back to the states...","translationLanguage": null,"translationText": "","transcript": {"source": "native","language": "EN","text": "we have to give that back to the states...","segments": [{"start": 2.842,"end": 5.762,"text": "we have to give that back to the states, by the way, we have to give"}]},"translation": null}
Input
| Field | Type | Required | Default | Allowed values / limits | Description |
|---|---|---|---|---|---|
urls | array of strings | Yes | — | 1–1,000 items | Public x.com or twitter.com status URLs and numeric Tweet IDs. Forms can be mixed. Empty entries are ignored. Exact duplicate strings are trimmed and processed once. |
transcriptionMode | string | No | auto | auto, native_only, asr_only | auto tries native captions before AI. native_only never invokes AI. asr_only skips native captions and transcribes audio directly. |
language | string | No | auto | auto, en, es, fr, de, it, pt, ja, ko, zh, ar, hi, ru | Source-language preference. It selects a preferred labelled caption track and provides a hint to AI transcription; it is not a guarantee. This is different from targetLanguage. |
formats | array of strings | No | text, segments | One or more of text, segments, vtt, srt | Controls fields inside both transcript and translation. Multiple formats do not trigger multiple transcription or translation operations. |
translate | boolean | No | false | true, false | Keeps the original transcript and adds a translated version when enabled. No translation request is made when disabled. |
targetLanguage | string | No | en | 20 supported values listed below | Translation target used only when translate is enabled. |
maxVideoDurationMinutes | integer | No | 30 | 1–360 | Maximum duration for AI speech recognition. Native caption transcripts are not blocked by this limit, and translation of a completed transcript is not limited by video duration. |
proxyConfiguration | object | No | Direct connection | Apify proxy configuration | Optionally routes requests through an Apify proxy. It can improve reachability but cannot unlock private or login-gated posts. |
Source language vs. target language
{"language": "auto","translate": true,"targetLanguage": "en"}
This means: detect or select the original transcript language automatically, retain that transcript, and add an English translation.
Available translation targets:
| Code | Language | Code | Language |
|---|---|---|---|
en | English | es | Spanish |
pt | Portuguese | fr | French |
de | German | it | Italian |
ja | Japanese | ko | Korean |
zh-CN | Chinese (Simplified) | zh-TW | Chinese (Traditional) |
ar | Arabic | hi | Hindi |
ru | Russian | id | Indonesian |
tr | Turkish | vi | Vietnamese |
th | Thai | nl | Dutch |
pl | Polish | uk | Ukrainian |
Transcript formats
| Format | Output |
|---|---|
text | Complete transcript text in text. |
segments | Array of start, end, and text objects; timestamps are seconds. |
vtt | WebVTT subtitle text with timestamps. |
srt | SRT subtitle text with timestamps. |
VTT and SRT are generated from normalized segments for both native and AI transcripts. They do not invoke another AI operation.
Input examples
The X video URLs below have been used in real Apify cloud runs.
Recommended Auto mode
{"urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"],"transcriptionMode": "auto","language": "auto","formats": ["text", "segments", "srt"]}
Native captions only with translation
{"urls": ["https://x.com/nicksortor/status/2095306242507866288?s=20"],"transcriptionMode": "native_only","language": "en","formats": ["text", "segments", "vtt", "srt"],"translate": true,"targetLanguage": "es"}
If no usable native caption track exists, this mode returns native_caption_not_found or the relevant caption error and does not run AI transcription or translation.
AI transcription only with translation
{"urls": ["https://x.com/nicksortor/status/2083859314402382147?s=20"],"transcriptionMode": "asr_only","language": "auto","formats": ["text", "segments", "srt"],"translate": true,"targetLanguage": "fr","maxVideoDurationMinutes": 30}
Batch processing
{"urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20","https://x.com/nicksortor/status/2095306242507866288?s=20","https://x.com/nicksortor/status/2095163328070840539?s=20"],"transcriptionMode": "auto","language": "auto","formats": ["text", "segments"],"translate": false,"maxVideoDurationMinutes": 30,"proxyConfiguration": {"useApifyProxy": false}}
API usage
Keep your Apify token in an environment variable or secret manager.
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('lance_api/x-twitter-video-transcript-api').call({urls: ['https://x.com/nicksortor/status/2095300108095046051?s=20'],transcriptionMode: 'auto',language: 'auto',formats: ['text', 'segments', 'srt'],translate: true,targetLanguage: 'es',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Python
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ['APIFY_TOKEN'])run = client.actor('lance_api/x-twitter-video-transcript-api').call(run_input={'urls': ['https://x.com/nicksortor/status/2095300108095046051?s=20'],'transcriptionMode': 'auto','language': 'auto','formats': ['text', 'segments', 'srt'],'translate': True,'targetLanguage': 'es',})items = client.dataset(run['defaultDatasetId']).list_items().itemsprint(items)
cURL
curl -X POST \'https://api.apify.com/v2/acts/lance_api~x-twitter-video-transcript-api/run-sync-get-dataset-items' \-H "Authorization: Bearer $APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"],"transcriptionMode": "auto","language": "auto","formats": ["text", "segments", "srt"],"translate": true,"targetLanguage": "es"}'
For long videos or large batches, start the Actor asynchronously and fetch the default dataset after the run finishes. A synchronous HTTP request can time out while the underlying Actor run continues.
Output
The Actor writes one dataset item per video. Original and translated transcripts use the same selectable format structure:
{"transcriptSource": "native","language": "zh","transcriptText": "原始字幕全文...","translationLanguage": "en","translationText": "Translated text...","transcript": {"source": "native","language": "zh","text": "原始字幕全文...","segments": [{"start": 10.2,"end": 13.8,"text": "你好,欢迎来到今天的视频。"}]},"translation": {"source": "translation","language": "en","detectedSourceLanguage": "zh-CN","text": "Translated text...","segments": [{"start": 10.2,"end": 13.8,"text": "Hello, welcome to today's video."}]}}
The original transcript is never overwritten by translation. When translation is disabled, translation is null, translationLanguage is null, and translationText is an empty string.
Run summary
Every run writes a SUMMARY record to the default key-value store:
{"startedAt": "2026-09-03T02:00:00.000Z","finishedAt": "2026-09-03T02:00:05.000Z","totalUrls": 3,"totalResults": 4,"succeeded": 3,"fullSuccess": 2,"partialSuccess": 1,"failed": 1,"nativeTranscripts": 2,"asrTranscripts": 1,"translated": 1,"translationFailed": 1,"byStatus": {"success": 2,"partial_success": 1,"asr_failed": 1}}
succeeded includes both full and partial successes because the original transcript exists in both cases. totalResults can exceed totalUrls when a post contains multiple videos.
Output field reference
Processing and status
| Field | Type | Nullable | Meaning |
|---|---|---|---|
requestId | string | No | Result identifier inside the run. Multi-video results add -video-N. |
processingTimeMs | integer | No | Processing time for the input post in milliseconds. |
processedAt | string | No | ISO 8601 result timestamp. |
success | boolean | No | true when the original transcript was produced, including partial_success. |
status | string | No | success, partial_success, or a structured failure status. |
warning | string | Yes | Non-fatal detail, such as native captions failing before successful AI fallback. |
error | object | Yes | null on full success; otherwise code, safe message, and retryable. On partial success it describes the optional translation failure. |
Post, author, and video
| Field | Type | Nullable | Meaning |
|---|---|---|---|
inputUrl | string | No | Original URL or Tweet ID. |
normalizedUrl | string | Yes | Canonical x.com/i/status/{tweetId} URL. |
resolutionSource | string | Yes | Diagnostic information about how the public post was resolved. |
tweetId | string | Yes | Numeric post ID. |
tweetText | string | Yes | Public post text. |
createdAt | string | Yes | ISO 8601 post creation timestamp. |
authorId | string | Yes | Public author account ID. |
authorUsername | string | Yes | Author username without @. |
authorName | string | Yes | Author display name. |
mediaIndex | integer | Yes | Zero-based video position in the post. |
mediaId | string | Yes | X media identifier. |
duration | number | Yes | Video duration in seconds; null when unavailable. |
thumbnailUrl | string | Yes | Temporary media thumbnail URL. |
videoUrl | string | Yes | Resolved media URL; it may expire. |
Native caption source
| Field | Type | Nullable | Meaning |
|---|---|---|---|
nativeCaptionAvailable | boolean | No | Whether a native caption track was found during the attempt. |
captionUrl | string | Yes | Native caption or caption-playlist URL. |
captionLanguage | string | Yes | Caption-track language label. |
captionFormat | string | Yes | vtt when a native caption track is selected. |
Transcript and translation
| Field | Type | Nullable | Meaning |
|---|---|---|---|
transcriptSource | string | No | native, asr, or none. Translation never changes this field. |
language | string | Yes | Original transcript language when known. |
transcriptText | string | No | Complete original text for Dataset, CSV, and spreadsheet convenience, regardless of selected nested formats. Empty on failure. |
translationLanguage | string | Yes | Requested target language when translation succeeds; otherwise null. |
translationText | string | No | Complete translated text for Dataset, CSV, and spreadsheet convenience. Empty when absent. |
transcript | object | Yes | Original transcript with source, language, and only the requested text, segments, vtt, and srt fields. null on failure. |
translation | object | Yes | Translation with source: "translation", language, detectedSourceLanguage, and requested formats. null when disabled or unsuccessful. |
Each segment contains start and end in seconds plus text. Translation preserves the original segment timestamps.
Engagement
| Field | Type | Nullable | Meaning |
|---|---|---|---|
likesCount | integer | Yes | Public like count at processing time; null when unavailable. A reported zero is preserved. |
repliesCount | integer | Yes | Public reply count at processing time; null when unavailable. A reported zero is preserved. |
retweetsCount | integer | Yes | Public repost/retweet count at processing time; null when unavailable. A reported zero is preserved. |
quotesCount | integer | Yes | Public quote-post count at processing time; null when unavailable. A reported zero is preserved. |
bookmarksCount | integer | Yes | Public bookmark count at processing time; null when unavailable. A reported zero is preserved. |
viewsCount | integer | Yes | Public view count at processing time; null when unavailable. A reported zero is preserved. |
Transcript and translation sources
| Value | Meaning |
|---|---|
native | Original transcript came from a usable caption track exposed for the X video. |
asr | Original transcript was generated from accessible video audio through AI speech recognition. |
none | No original transcript was produced; inspect status and error. |
translation | Nested translation was generated from the completed original transcript. It is not a transcript-source value. |
Native captions may have been created by the publisher or by the platform; the Actor does not claim who created them.
Status values
| Status | Meaning |
|---|---|
success | Original transcript and every requested optional operation succeeded. |
partial_success | Original transcript succeeded, but optional translation failed. The original remains available. |
invalid_input | Item is not a supported X/Twitter URL or Tweet ID. |
tweet_not_found | Post could not be found through available public sources. |
video_not_found | Post resolved but had no supported video. |
private | Post is protected, private, or not publicly exposed. |
unavailable | A required public resource was unavailable for another reason. |
login_required | X requires login, age verification, or another authenticated gate. |
blocked | A public X request was blocked from the run environment. |
rate_limited | Public access was temporarily rate-limited. |
native_caption_not_found | native_only was selected but no native caption track was found. |
caption_fetch_failed | Selected caption track or segment could not be downloaded. |
caption_parse_failed | Caption data contained no readable WebVTT cues. |
duration_limit | AI transcription was required but exceeded the configured limit. |
no_audio | Resolved media was an animated GIF without audio. |
media_resolve_failed | No downloadable media URL was available for AI transcription. |
asr_failed | AI speech recognition failed or returned no usable transcript. |
timeout | A required public request timed out. |
charge_limit | The run charge limit could not cover a requested paid operation. |
billing_configuration_error | Billing is temporarily unavailable; contact support. |
Translation errors use safe public codes such as TRANSLATION_FAILED and TRANSLATION_SERVICE_UNAUTHORIZED.
How it works
- Add X/Twitter video posts — provide public post URLs or Tweet IDs.
- Choose your transcription mode — use Auto for native captions first with automatic AI fallback, or choose native-only or AI-only when needed.
- Choose your output — request text, timestamped segments, VTT, SRT, or multiple formats together.
- Optionally translate — keep the original transcript and add a translation in your selected target language.
- Run the Actor — each video is processed independently, so multi-video posts can return multiple results.
- Use the results — download them from the Dataset or send structured JSON into APIs, AI agents, research pipelines, subtitle workflows, or automation.
💰 Pricing
Pay a base fee for each unique input processed, plus any successful AI transcription or translation you use.
- Results (base processing) — $1 per 1,000 unique inputs actually processed, whether or not a transcript is produced.
- AI transcription — from $0.002 per started minute, only when AI speech recognition is needed and successfully produces a usable transcript.
- Translation — from $0.07 per started 1,000 source characters, only when requested and successfully produced.
Native captions can save AI transcription costs
In Auto mode, the Actor uses available native X/Twitter captions first. When native captions can produce the transcript, there is no AI transcription-minute charge. If AI is needed, started minutes are charged only after a usable transcript is successfully produced.
The base result fee is charged once for each unique input that is actually processed, even if the post is unavailable or no transcript can be produced. AI transcription and translation are separate optional usage charges.
Failed AI transcription has no AI transcription-minute charge. Failed translation has no translation charge. Neither failure cancels the base processing fee.
Selecting multiple output formats such as text, segments, VTT, and SRT does not create additional transcription or translation charges. A multi-video post incurs one base fee for its input, not one per video; successful AI transcription and translation are charged separately for each video that uses them.
Inputs are trimmed and exact duplicates are processed and charged once per run. Different URL forms or a URL and Tweet ID for the same post remain separate inputs under this rule. Internal retries and fallback attempts do not add base fees. Invalid URLs or IDs rejected before processing are not charged.
Check the Actor's Pricing tab for the latest plan-specific rates.
🤖 Use with AI agents and LLM workflows
Use this Actor as the X video → structured transcript and translation layer in an automation or agent workflow. Through the Apify API or an Apify MCP connection, an agent can submit public video posts and receive predictable JSON without building separate workflows for videos with and without native captions.
The output can feed:
- summarization and key-point extraction;
- topic classification and content analysis;
- semantic search and retrieval-augmented generation (RAG);
- media-monitoring and research agents;
- translation and multilingual indexing;
- subtitle editing and content-repurposing pipelines.
These are downstream uses. The Actor itself returns transcripts, optional translations, subtitles, and metadata rather than performing summarization, sentiment analysis, or RAG.
Use cases
- Media monitoring — convert public statements, interviews, announcements, and clips into searchable text. Auto mode handles captioned and uncaptioned videos in one workflow.
- Multilingual research — retain source transcripts while adding a normalized target-language version.
- Subtitle workflows — obtain timestamped segments, WebVTT, or SRT for editing and publishing systems.
- AI datasets — build transcript corpora with consistent structured results from native captions or AI, avoiding unnecessary AI transcription costs when captions are available.
- Content intelligence — analyze what creators, brands, executives, journalists, and public organizations say in video posts.
- Automation — connect X video transcription to the Apify API, MCP, scheduled runs, data stores, and downstream applications.
Public data and responsible use
This Actor is designed for publicly accessible X/Twitter posts. It does not provide access to private content or bypass login, age, regional, or account restrictions.
You are responsible for complying with applicable laws, X's terms, copyright rules, privacy requirements, and obligations that apply to storing or processing results. Do not use the Actor for harassment, spam, unlawful profiling, rights-violating surveillance, or attempts to obtain non-public information.
FAQ
Does every X/Twitter video have native captions?
No. Some videos expose usable native subtitle tracks; others require AI transcription. Check transcriptSource to see which route produced each result.
Am I charged if a post is deleted, private, unavailable, or has no video?
Yes. Once a valid input starts processing, its base result fee applies even if no transcript can be produced. Inputs rejected by initial URL or Tweet ID validation are not charged.
Do native captions cost AI transcription minutes?
No. When native captions produce the transcript, only the base result fee and any requested successful translation apply.
Can I force native captions only?
Yes. Use transcriptionMode: "native_only". If no track exists, the Actor returns a structured failure without calling AI transcription or translation.
Can I force AI transcription?
Yes. Use transcriptionMode: "asr_only" to skip native-caption retrieval and transcribe accessible audio directly.
Is language the translation target?
No. language is the original/source preference. Set translate: true and use targetLanguage for the translation destination.
Am I charged if AI transcription fails?
No. AI transcription minutes are charged only after AI speech recognition successfully produces a usable transcript. The base result fee still applies because the input was processed.
Am I charged if translation fails?
No translation charge is applied when translation fails. A translation problem does not throw away a successfully generated transcript: the original remains available with status: "partial_success". The base result fee and any successful AI transcription charge still apply.
What happens when source and target languages match?
When the source language is known to match the target, the original text and timestamps are also returned as the translation, with no translation charge.
Can I get both SRT and WebVTT?
Yes. Include both srt and vtt in formats to receive both subtitle formats at no additional transcription cost.
What happens if one URL fails?
The rest of the batch continues. The failed video or URL receives a dataset item with success: false, a status, and a structured error.
What happens if one post contains multiple videos?
Each video receives its own item, media identifiers, transcript, translation outcome, and requestId suffix.
Do I need X cookies or a separate transcription or translation subscription?
No. Transcription and optional translation are included in this Actor's workflow. Public posts that require authentication can still be inaccessible.
Limitations
- X can change public web behavior, metadata availability, caption exposure, and media access without notice.
- Private, deleted, suspended, unavailable, age-restricted, login-gated, or region-restricted posts may not be accessible.
- A post that opens in a signed-in browser may not be available through public endpoints from an Apify run location.
- Native captions are available only when X exposes a readable caption track.
- Media, thumbnail, and caption URLs can be temporary and may expire.
- AI transcription requires resolvable video audio. Accuracy varies with noise, accents, language, overlapping speakers, encoding, and recording quality.
- Translation quality depends on the accuracy and language labelling of the original transcript.
- Long videos and large batches take longer; synchronous API requests can time out while asynchronous Actor runs continue.
maxVideoDurationMinutesapplies only to AI transcription, not native captions or translation of completed text.- Public engagement counts may be missing, delayed, or differ from values later displayed on X.
- Optional proxies improve network reachability but cannot grant access to non-public content.
Troubleshooting
| Symptom or status | What to try |
|---|---|
invalid_input | Use a numeric Tweet ID or an x.com / twitter.com URL containing /status/ or /statuses/ followed by the numeric ID. |
tweet_not_found | Confirm the post exists and is publicly accessible outside a signed-in browser session. |
private or login_required | The Actor does not accept cookies to bypass public-access restrictions. |
rate_limited, blocked, or timeout | Retry later or try an appropriate Apify proxy for public reachability. |
video_not_found | Confirm the post contains video rather than only images, a link card, or unsupported media. |
native_caption_not_found | Use auto or asr_only if AI transcription is acceptable. |
caption_fetch_failed or caption_parse_failed | In Auto mode, check whether AI fallback succeeded and inspect warning; otherwise retry or use asr_only. |
duration_limit | Increase maxVideoDurationMinutes within 1–360, or use native_only when a native track is expected. |
asr_failed | Inspect error.retryable, confirm accessible audio, and retry temporary failures. |
TRANSLATION_FAILED | The original transcript remains available. Retry a temporary failure later. |
TRANSLATION_SERVICE_UNAUTHORIZED | Contact support with the Run ID; translation is currently unavailable. Your original transcript remains available. |
charge_limit | Increase the maximum run charge or disable the optional paid operation. |
Run logs include request ID, status, transcript source, resolution source, processing time, and safe error code for each result.
Support
Use the Actor's Issues page or email the address below. Include the Apify Run ID, requestId, status/error code, and a non-sensitive example URL or input. Never send your Apify token, X cookies, translation keys, API keys, or other secrets.