# **Social Video Downloader & Transcription & Subtitle** (`dtrungtin/social-video-downloader-transcription-subtitle`) Actor

Downloads & transcription videos or audio from **YouTube**, **TikTok**, **X (Twitter)**, **Facebook**, **Instagram**, and 1000+ other sites. The Actor stores the media files in the key-value store and rich metadata (title, uploader, duration, views, direct download link) in the dataset.

- **URL**: https://apify.com/dtrungtin/social-video-downloader-transcription-subtitle.md
- **Developed by:** [Tin](https://apify.com/dtrungtin) (community)
- **Categories:** Social media, Videos
- **Stats:** 7 total users, 5 monthly users, 91.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $9.99 / 1,000 megabyte downloadeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## Social Video Downloader (yt-dlp)

### What does Social Video Downloader do?

**Social Video Downloader** downloads videos or audio from **YouTube**, **TikTok**, **X (Twitter)**, **Facebook**, **Instagram**, **Vimeo**, **Twitch clips**, and 1000+ other sites supported by the popular [yt-dlp](https://github.com/yt-dlp/yt-dlp) project. Paste a list of video URLs, pick a quality, and the Actor stores the **media files in the key-value store** and rich **metadata (title, uploader, duration, views, direct download link) in the dataset**.

Because it runs on the [Apify platform](https://apify.com), you get everything a plain yt-dlp script does not: an **API** for every run, **scheduling**, **integrations** (Google Drive, S3, webhooks, Zapier), **proxy rotation**, run **monitoring**, and downloadable results in JSON, CSV, or Excel.

### Why use Social Video Downloader?

- **Archive content** — back up your own channels or brand mentions before they disappear.
- **Media monitoring** — capture videos referenced in news, social listening, or brand-safety workflows.
- **Content pipelines** — feed downloaded audio/video into transcription, translation, or AI analysis Actors.
- **Research** — collect video datasets together with structured metadata in one run.
- **No infrastructure** — no servers, no ffmpeg setup, no IP blocks; everything runs in the cloud.

### How to use Social Video Downloader

1. Open the Actor and go to the **Input** tab.
2. Paste one or more **video URLs** (one video per URL).
3. Optionally choose **video quality**, **audio only** mode, or a **proxy**.
4. Click **Start**. The run usually takes a few seconds to a few minutes per video.
5. Open the **Output** tab to browse results, or the **Storage → Key-value store** tab to grab the media files. Each dataset item also contains a ready-to-use `downloadUrl`.

### Input

The main input fields (see the Input tab for the full list):

| Field                            | Description                                                                                 |
| -------------------------------- | ------------------------------------------------------------------------------------------- |
| `videoUrls`                      | List of video page URLs to download (required).                                             |
| `quality`                        | Max resolution: best available, 1080p, 720p (default), 480p, or 360p.                       |
| `audioOnly`                      | Download just the audio track instead of the video.                                         |
| `proxyConfiguration`             | Proxies for the downloads; defaults to Apify residential proxies for best success rates.    |
| `cookies`                        | Contents of a `cookies.txt` file for videos that require login (use only your own account). |
| `s3Bucket`, `s3AccessKeyId`, ... | Optional: upload files to your own S3 bucket (see below).                                   |
| `maxFileSizeMb`                  | Skip videos larger than this limit (0 = unlimited).                                         |
| `extraYtDlpArgs`                 | Advanced: extra command-line arguments passed straight to yt-dlp.                           |

```json
{
    "videoUrls": [{ "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ" }, { "url": "https://vimeo.com/76979871" }],
    "quality": "720",
    "audioOnly": false
}
```

### Output

Media files are stored in the run's **key-value store** (keys like `video-Youtube-aqz-KE-bpKQ.mp4`). One metadata item per URL is pushed to the **dataset**. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

```json
{
    "sourceUrl": "https://www.youtube.com/watch?v=aqz-KE-bpKQ",
    "status": "SUCCESS",
    "title": "Big Buck Bunny 60fps 4K - Official Blender Foundation Short Film",
    "videoId": "aqz-KE-bpKQ",
    "extractor": "Youtube",
    "uploader": "Blender",
    "uploadDate": "2014-11-10",
    "durationSeconds": 635,
    "viewCount": 8500000,
    "width": 1280,
    "height": 720,
    "thumbnailUrl": "https://i.ytimg.com/vi/aqz-KE-bpKQ/maxresdefault.jpg",
    "fileName": "video-Youtube-aqz-KE-bpKQ.mp4",
    "fileSizeBytes": 104857600,
    "fileSizeGb": 0.0977,
    "contentType": "video/mp4",
    "downloadUrl": "https://api.apify.com/v2/key-value-stores/STORE_ID/records/video-Youtube-aqz-KE-bpKQ.mp4",
    "error": null,
    "downloadedAt": "2026-08-07T12:00:00.000Z"
}
```

### How to transcribe videos with AI

Enable **Transcribe audio with AI** and provide your OpenAI API key — every downloaded video then gets a `transcript` field in the dataset, ready for search indexes, LLM pipelines, subtitles drafts, or content repurposing:

```json
{
    "videoUrls": [{ "url": "https://www.tiktok.com/@apifyoffice/video/7200360993149553925" }],
    "transcribe": true,
    "openaiApiKey": "sk-...",
    "transcriptionLanguage": "en"
}
```

- Instead of the input field, you can also provide the key via the `OPENAI_API_KEY` environment variable (set it in the Actor's **Settings → Environment variables** as a secret).
- The audio track is extracted inside the Actor (mono 16 kHz MP3) and sent to OpenAI's speech-to-text API — videos up to roughly 100 minutes are supported.
- OpenAI usage is billed to **your** API key: about $0.003 per audio minute with the default `gpt-4o-mini-transcribe` model.
- Setting the audio language (ISO code like `en` or `vi`) improves accuracy; leave it empty for auto-detection.
- Speech-to-text often mishears words in songs, noisy audio, or heavy accents. Enable **Fix transcript wording with AI** (`fixTranscript`) to post-process each transcript with `gpt-5.6-luna` (OpenAI's fast low-cost GPT-5.6 tier), which corrects clearly misheard words using the video title as context. The corrected text replaces the raw transcript in the dataset; if the correction step fails, the raw transcript is kept.
- Set **Translate transcript to** (`targetLanguage`, e.g. `en`, `vi`, or `Spanish`) to also get an AI translation of the transcript in the `translatedTranscript` dataset field — and translated burned-in captions when `subtitledVideo` is enabled. Translation runs on `gpt-5.6-luna` and is billed to your OpenAI key (typically well under $0.01 per video).
- A failed transcription never loses the video — the item stays `SUCCESS` with a `transcriptionError` field explaining what happened.

#### How to create a video with burned-in captions

Enable **Create video with burned-in captions** (`subtitledVideo`, requires `transcribe: true`) and the Actor renders a second MP4 with the transcript drawn onto the frames — TikTok-style captions that stay visible on any player and survive re-uploading to any platform:

```json
{
    "videoUrls": [{ "url": "https://www.tiktok.com/@apifyoffice/video/7200360993149553925" }],
    "transcribe": true,
    "fixTranscript": true,
    "subtitledVideo": true,
    "transcriptionLanguage": "en"
}
```

- Captions need timestamps, so transcription automatically uses OpenAI's `whisper-1` model ($0.006 per audio minute) — the `transcriptionModel` setting is ignored.
- With `fixTranscript` enabled, the AI wording correction is applied to the captions too (timestamps are preserved).
- With `targetLanguage` set, the captions are burned in the target language — e.g. English captions on a Vietnamese video (the `transcript` field keeps the original language, `translatedTranscript` holds the translation).
- The subtitled file is stored next to the original (key `subtitled-...mp4`) and linked in the dataset as `subtitledDownloadUrl` (and `subtitledS3Url` when S3 upload is on).
- Burning captions re-encodes the video with ffmpeg, so expect runs to take several times longer than a plain download, and the stored megabytes of the subtitled file are charged like the original.
- Not available in `audioOnly` mode, and a failure to render never loses the original video.

### How to upload the videos to Amazon S3

Fill in the **Upload to Amazon S3** section of the input and every downloaded file is also uploaded to your bucket:

```json
{
    "videoUrls": [{ "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ" }],
    "s3Bucket": "my-video-archive",
    "s3Region": "eu-central-1",
    "s3AccessKeyId": "AKIA...",
    "s3SecretAccessKey": "...",
    "s3KeyPrefix": "videos/2026/",
    "s3Only": true
}
```

- The credentials only need the `s3:PutObject` permission on the bucket (multipart uploads are used for large files).
- `s3Only: true` skips the Apify key-value store entirely and keeps the files only in S3 — useful for large archives, since you avoid double storage costs.
- Each dataset item then contains `s3Key` and `s3Url` with the object's location.
- **S3-compatible storage** (Cloudflare R2, MinIO, DigitalOcean Spaces, Backblaze B2) works too — set `s3Endpoint` to your provider's endpoint URL.

### Data fields

| Field                                       | Description                                                   |
| ------------------------------------------- | ------------------------------------------------------------- |
| `sourceUrl`                                 | The input URL.                                                |
| `status`                                    | `SUCCESS`, `FAILED`, or `SKIPPED` (e.g. over the size limit). |
| `title`, `uploader`, `uploadDate`           | Basic video metadata.                                         |
| `durationSeconds`, `viewCount`, `likeCount` | Engagement/technical metadata (when the site provides it).    |
| `width`, `height`                           | Resolution of the downloaded file.                            |
| `fileName`                                  | Key of the file in the key-value store.                       |
| `fileSizeBytes`, `fileSizeGb`               | Size of the stored file in bytes and gigabytes.               |
| `contentType`                               | MIME type of the stored file.                                 |
| `downloadUrl`                               | Direct URL of the stored media file.                          |
| `s3Key`, `s3Url`                            | Object key and URL in your bucket (when S3 upload is used).   |
| `subtitledDownloadUrl`, `subtitledS3Url`    | URLs of the caption-burned MP4 (when `subtitledVideo` is on). |
| `transcript`, `transcriptionError`          | AI transcript of the audio (when transcription is enabled).   |
| `translatedTranscript`                      | Transcript translated to `targetLanguage` (when set).         |
| `error`                                     | Error or skip reason for unsuccessful items.                  |

### How much does it cost to download videos?

The Actor uses transparent **pay-per-event pricing** — you pay only for what is actually downloaded:

| Event                   | When it is charged                                                        |
| ----------------------- | ------------------------------------------------------------------------- |
| **Video downloaded**    | Once per successfully downloaded video.                                   |
| **Megabyte downloaded** | Per started MB of each stored file, so a 25.3 MB video counts as 26 units (the subtitled copy, when enabled, is counted too). |
| **Minute transcribed**  | Per started audio minute, only when AI transcription is enabled.          |

Failed or skipped URLs are **not charged**. The exact rates are listed on the Actor's pricing tab, and you can cap spending with the **Maximum cost per run** setting — the Actor stops gracefully when the cap is reached and reports what it managed to download. To keep costs down, use `quality: "480"` or `audioOnly: true` and set `maxFileSizeMb`.

### Tips and advanced options

- **Lower the quality** — 480p is usually fine for analysis pipelines and downloads several times faster than 1080p.
- **Audio only** — for transcription workflows, enable `audioOnly` to skip the video entirely.
- **Proxies** — US **residential** proxies are used by default, which most social sites do not block, and each video is downloaded through a single sticky IP. Keep a proxy country selected; a random exit country gets 403s from sites that are blocked in some regions. If you only download from sites that tolerate datacenter IPs (e.g. many public YouTube videos), switching the proxy configuration to datacenter or none makes runs cheaper. For geo-restricted videos, set the proxy country to a country where the video is available.
- **Login-required videos** — export cookies from your own browser session with a "cookies.txt" browser extension and paste the file contents into the `cookies` field.
- **Power users** — anything yt-dlp can do can be enabled via `extraYtDlpArgs`, e.g. `["--write-auto-subs"]`.
- **Keeping yt-dlp fresh** — sites change their internals often; rebuilding the Actor installs the latest yt-dlp release.

### FAQ and disclaimers

**Is it legal to download videos?** You are responsible for how you use this Actor. Download only content you have the right to download (your own uploads, licensed or public-domain content) and respect each platform's Terms of Service and copyright law. This Actor does not bypass DRM.

**Why did a URL fail?** The most common reasons are region locks, login walls, removed videos, or IP blocks — the `error` field contains the exact yt-dlp message. Try proxies or cookies as described above.

**Why does TikTok return HTTP 403?** TikTok blocks datacenter IPs, non-browser TLS fingerprints, and requests from countries where it is unavailable. The Actor already impersonates a real browser (via yt-dlp's `curl_cffi` support) — just make sure the proxy configuration is left on the default **residential** proxies with a country selected. When running locally, install impersonation support with `pip install yt-dlp "curl-cffi==0.11.4"`.

**Why do TikTok downloads sometimes fail with "Unable to extract universal data for rehydration"?** TikTok intermittently serves an empty page to new visitors. The Actor automatically retries each failed download (see `downloadRetries`, default 2) with a fresh proxy IP, which usually resolves it. If a URL still fails, simply re-run it — or provide TikTok `cookies` for the most reliable results.

**Does it download whole playlists or channels?** No — one video per URL, by design. Pass individual video URLs.

Found a bug or missing feature? Please open an issue in the Actor's **Issues** tab. If you need a customized version or a full pipeline (download → transcribe → analyze), consider ordering a custom solution on Apify.

### Running locally

```bash
npm install
## yt-dlp and ffmpeg must be on PATH (or set YTDLP_PATH)
apify run --purge
```

# Actor input Schema

## `videoUrls` (type: `array`):

Direct URLs of the videos to download. Supports YouTube, TikTok, X (Twitter), Facebook, Instagram, Vimeo, Twitch clips, and 1000+ other sites supported by yt-dlp. One video is downloaded per URL (playlists are not expanded).

## `quality` (type: `string`):

Maximum video resolution to download. Lower resolutions download faster and produce smaller files.

## `audioOnly` (type: `boolean`):

Download only the audio track (kept in its native format, typically M4A or Opus) instead of the full video.

## `s3Bucket` (type: `string`):

Name of the S3 bucket to upload the downloaded files to. Leave empty to keep files only in the Apify key-value store.

## `s3Region` (type: `string`):

AWS region of the bucket, e.g. `us-east-1` or `eu-central-1`.

## `s3AccessKeyId` (type: `string`):

Access key ID of an IAM user (or S3-compatible credentials) allowed to write to the bucket.

## `s3SecretAccessKey` (type: `string`):

Secret access key belonging to the access key ID above.

## `s3KeyPrefix` (type: `string`):

Optional folder path inside the bucket, e.g. `videos/2026/`.

## `s3Endpoint` (type: `string`):

Only for S3-compatible storage: custom endpoint URL, e.g. `https://<account>.r2.cloudflarestorage.com`. Leave empty for AWS S3.

## `s3Only` (type: `boolean`):

Skip storing the media files in the Apify key-value store and keep them only in S3. Saves Apify storage costs; metadata is still pushed to the dataset.

## `transcribe` (type: `boolean`):

Generate a text transcript of each video using OpenAI's speech-to-text API and add it to the dataset as the `transcript` field.

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

Language to translate the transcript into — an ISO code (`en`, `vi`) or a language name (`Spanish`). Adds a `translatedTranscript` field to the dataset, and burned-in captions (when enabled) use the translation.

## `summaryPrompt` (type: `string`):

Instructions for an AI-generated summary of each video, e.g. `Summarize the key points in 5 bullet points` or `One-paragraph summary in English`. The summary is generated from the transcript with gpt-5.6-luna and added to the dataset as the `summary` field. Requires transcription; leave empty for no summary.

## `subtitledVideo` (type: `boolean`):

Render a second MP4 with the transcript burned in as always-visible captions and link it in the dataset as `subtitledDownloadUrl`. Requires transcription; timestamps force the whisper-1 model ($0.006 per audio minute). Re-encoding makes runs noticeably slower, and the stored megabytes of the subtitled file are charged like the original video. Cannot be combined with audio-only mode.

## `maxFileSizeMb` (type: `integer`):

Videos larger than this limit are skipped. Use 0 for no limit.

## `downloadTimeoutSecs` (type: `integer`):

Maximum time allowed for downloading a single video before it is marked as failed.

## `downloadRetries` (type: `integer`):

How many times a failed download is retried. Each retry uses a fresh proxy session (new IP address), which resolves most intermittent anti-bot blocks.

## `extraYtDlpArgs` (type: `array`):

Additional command-line arguments passed to yt-dlp as-is, one per item (e.g. `--extractor-args` and its value as two items). For advanced users — invalid arguments will make all downloads fail.

## Actor input object example

```json
{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ"
    }
  ],
  "quality": "720",
  "audioOnly": false,
  "s3Region": "us-east-1",
  "s3Only": false,
  "transcribe": false,
  "subtitledVideo": false,
  "maxFileSizeMb": 0,
  "downloadTimeoutSecs": 1800,
  "downloadRetries": 2,
  "extraYtDlpArgs": []
}
```

# 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 = {
    "videoUrls": [
        {
            "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dtrungtin/social-video-downloader-transcription-subtitle").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 = { "videoUrls": [{ "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ" }] }

# Run the Actor and wait for it to finish
run = client.actor("dtrungtin/social-video-downloader-transcription-subtitle").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 '{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=aqz-KE-bpKQ"
    }
  ]
}' |
apify call dtrungtin/social-video-downloader-transcription-subtitle --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dtrungtin/social-video-downloader-transcription-subtitle"
        }
    }
}

```

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/9c4Q2aFsRXNsNiCPY/builds/n0AHzvBI9FeuWH4Md/openapi.json
