# X (Twitter) Video Transcript API – Captions, AI Transcription (`lance_api/x-twitter-video-transcript-api`) Actor

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.

- **URL**: https://apify.com/lance\_api/x-twitter-video-transcript-api.md
- **Developed by:** [LanceAPI](https://apify.com/lance_api) (community)
- **Categories:**
- **Stats:** 1 total users, 1 monthly users, 77.8% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 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

```json
{
    "urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"],
    "transcriptionMode": "auto",
    "language": "auto",
    "formats": ["text", "segments"]
}
```

In Apify Console:

1. Paste one or more public X/Twitter video post URLs or Tweet IDs.
2. Keep **Auto — X captions first, AI fallback** selected.
3. Choose the transcript formats you need.
4. Optionally enable translation and choose a target language.
5. Click **Start**, then open **Transcript results**.

### Short output example

```json
{
    "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

```json
{
    "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

```json
{
    "urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"],
    "transcriptionMode": "auto",
    "language": "auto",
    "formats": ["text", "segments", "srt"]
}
```

#### Native captions only with translation

```json
{
    "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

```json
{
    "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

```json
{
    "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

```js
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

```python
import os
from apify_client import ApifyClient

client = 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().items
print(items)
```

#### cURL

```bash
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:

```json
{
    "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:

```json
{
    "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

1. **Add X/Twitter video posts** — provide public post URLs or Tweet IDs.
2. **Choose your transcription mode** — use Auto for native captions first with automatic AI fallback, or choose native-only or AI-only when needed.
3. **Choose your output** — request text, timestamped segments, VTT, SRT, or multiple formats together.
4. **Optionally translate** — keep the original transcript and add a translation in your selected target language.
5. **Run the Actor** — each video is processed independently, so multi-video posts can return multiple results.
6. **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.
- `maxVideoDurationMinutes` applies 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.

lanceapi2026@hotmail.com

# Actor input Schema

## `urls` (type: `array`):

Add 1–1,000 x.com or twitter.com post URLs. Tweet IDs are also accepted through the API.

## `transcriptionMode` (type: `string`):

Auto uses X captions first and AI only when needed.

## `language` (type: `string`):

Selects a preferred native caption track and provides a source-language hint to AI transcription. This is separate from the translation target language.

## `formats` (type: `array`):

Choose the original and translated transcript fields to return. Selecting multiple formats does not run transcription more than once.

## `translate` (type: `boolean`):

Keep the original transcript and add a translated version.

## `targetLanguage` (type: `string`):

Translation target used only when Translate transcript is enabled.

## `maxVideoDurationMinutes` (type: `integer`):

Maximum video duration for AI fallback. Native captions are returned regardless of this limit.

## `proxyConfiguration` (type: `object`):

Optional. Direct access is used by default; enable an Apify proxy if X is blocked from your run location.

## Actor input object example

```json
{
  "urls": [
    "https://x.com/nicksortor/status/2095300108095046051?s=20"
  ],
  "transcriptionMode": "auto",
  "language": "auto",
  "formats": [
    "text",
    "segments"
  ],
  "translate": false,
  "targetLanguage": "en",
  "maxVideoDurationMinutes": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Successful transcripts and structured error results.

## `summary` (type: `string`):

Counts of full, partial, failed, native, AI, and translated results.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://x.com/nicksortor/status/2095300108095046051?s=20"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lance_api/x-twitter-video-transcript-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": ["https://x.com/nicksortor/status/2095300108095046051?s=20"] }

# Run the Actor and wait for it to finish
run = client.actor("lance_api/x-twitter-video-transcript-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://x.com/nicksortor/status/2095300108095046051?s=20"
  ]
}' |
apify call lance_api/x-twitter-video-transcript-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lance_api/x-twitter-video-transcript-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GY35q1FtlG1eTdKeC/builds/2ujy9Fr7f7Tfy6d5D/openapi.json
