# YouTube Video Downloader (`thenetaji/youtube-video-downloader`) Actor

Rebuilt YouTube downloader for normalized direct CDN formats or saved playable files. Resolve public videos by access country, automatically combine separate high-quality video and audio, and optionally include subtitles or timed transcripts.

- **URL**: https://apify.com/thenetaji/youtube-video-downloader.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (community)
- **Categories:** Social media, Videos, For creators
- **Stats:** 683 total users, 58 monthly users, 87.6% runs succeeded, 24 bookmarks
- **User rating**: 3.75 out of 5 stars

## Pricing

from $7.00 / 1,000 videos

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

## YouTube Video Downloader

The Actor resolves public YouTube videos and Shorts into normalized direct media formats, or saves one playable MP4, WebM, or MKV file with compatible video and audio combined.

Each input is handled independently. A run can return temporary direct media URLs for downstream processing without transferring the whole file, or `saveMedia` can retain a playable file in the run's key-value store. Optional transcript and subtitle enrichment can be attached to the same video record.

### Accepted video references

`urls` is required and accepts request-list entries or bare strings in JSON input. Supported public references include:

- `youtube.com/watch?v=...` URLs
- `music.youtube.com/watch?v=...` URLs
- `youtu.be/...` URLs
- `/shorts/`, `/embed/`, and `/live/` URLs
- bare 11-character video IDs

Recognized URLs are reduced to their stable video ID before media resolution. Extra playlist context on a watch URL does not change which video is processed.

### Input fields

| Field | Type | Default | Behaviour |
|---|---|---|---|
| `urls` | array | required | Public YouTube URLs or bare video IDs. Duplicate entries are removed. |
| `region` | string | `US` | Two-letter country code used to resolve and transfer the media, such as `US`, `IN`, or `DE`. |
| `saveMedia` | boolean | `false` | Saves one playable file instead of returning only direct media formats. |
| `quality` | enum | `best` | Maximum saved resolution: `144` through `4320`, or `best`. |
| `format` | enum | `default` | Saved container: `default`, `mp4`, `webm`, or `mkv`. `default` produces MP4. |
| `includeTranscript` | boolean | `false` | Adds timed transcript segments and combined text when a transcript exists. |
| `subtitleLanguage` | string | empty | Adds one available VTT subtitle track matching the supplied language code. |

```json
{
  "urls": [
    { "url": "https://www.youtube.com/watch?v=arj7oStGLkU" },
    { "url": "https://youtu.be/Gef1KePPRoI" }
  ],
  "region": "US",
  "saveMedia": true,
  "quality": "1080",
  "format": "mp4",
  "includeTranscript": false
}
```

### Direct-format records

With `saveMedia: false`, one dataset row contains video metadata and every usable combined, video-only, and audio-only format returned for that video.

| Field | Contents |
|---|---|
| `id`, `url` | Video ID and canonical watch URL. |
| `title`, `description` | Public video text. |
| `accessCountry` | Country used to resolve the media. |
| `durationSeconds`, `viewCount`, `isLive` | Duration, reported views, and live-content state. |
| `channel` | Channel ID, title, and canonical channel URL. |
| `thumbnails` | Available thumbnail URLs and dimensions. |
| `availableSubtitles` | Language metadata for available caption tracks. |
| `formats` | Normalized media streams with container, codecs, dimensions, bitrate, size, duration, and `directUrl`. |
| `directUrlsExpireAt` | Approximate shared expiry time for the signed direct URLs, when reported. |
| `transcript` | Optional combined text and timed segments. |
| `subtitle` | Optional language metadata and VTT content. |

```json
{
  "id": "abc123def45",
  "url": "https://www.youtube.com/watch?v=abc123def45",
  "title": "Example video",
  "accessCountry": "US",
  "durationSeconds": 60,
  "viewCount": 1234,
  "isLive": false,
  "formats": [
    {
      "itag": 18,
      "streamType": "video+audio",
      "container": "mp4",
      "qualityLabel": "360p",
      "width": 640,
      "height": 360,
      "contentLengthBytes": 1048576,
      "sizeMb": 1,
      "directUrl": "https://.../"
    }
  ]
}
```

Direct URLs are signed and temporary. `directUrlsExpireAt` records the approximate expiry when the source reports enough timing information; a retained file is the appropriate output when a stable run artifact is required.

### Playable saved files

With `saveMedia: true`, the Actor selects the best compatible stream at or below `quality`. Higher YouTube resolutions commonly separate video and audio; the Actor downloads both and remuxes them into the selected container without re-encoding the media streams.

A successful `savedFile` contains:

- `key` and public `url`
- `contentType` and `container`
- `qualityLabel`, `width`, and `height`
- `bytes`, `sizeMb`, and `billedMb`

```json
{
  "savedFile": {
    "key": "abc123def45-1080p.mp4",
    "url": "https://api.apify.com/v2/key-value-stores/...",
    "contentType": "video/mp4",
    "container": "mp4",
    "qualityLabel": "1080p",
    "width": 1920,
    "height": 1080,
    "bytes": 52428800,
    "sizeMb": 50,
    "billedMb": 50
  }
}
```

After a playable file is saved, the verbose `formats`, `thumbnails`, `availableSubtitles`, and `directUrlsExpireAt` fields are removed from that row. The retained file, basic video metadata, and any requested transcript or subtitle remain.

### Partial results and billing

An input that does not resolve to a public video is skipped, and processing continues with the remaining entries. If playable-file transfer or merging fails, the video row is still returned with its direct formats and without `savedFile`.

Billing follows successful output:

- one result event for each dataset row;
- one saved-file event per billed MB, rounded up to the next whole MB, only after the file is stored;
- one transcript-or-subtitle event when a transcript, a subtitle, or both are successfully attached.

Missing transcripts and unavailable subtitle languages do not prevent the video result. They are logged, omitted from the row, and not billed as enrichment.

### Choosing the related YouTube Actor

- [YouTube Music Downloader](https://apify.com/thenetaji/youtube-music-downloader) saves audio-only MP3, M4A, or WebM files.
- [YouTube Video Scraper](https://apify.com/thenetaji/youtube-video-scraper) returns public metadata and optional enrichments without downloading media.
- [YouTube Transcript Scraper](https://apify.com/thenetaji/youtube-transcript-scraper) is the narrower choice when only transcript text and subtitle files are required.
- [YouTube Playlist Scraper](https://apify.com/thenetaji/youtube-playlist-scraper) expands playlist URLs into their video entries.

Only public media that may lawfully be downloaded should be processed.

# Actor input Schema

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

One or more public YouTube, YouTube Music, or Shorts URLs. Bare 11-character video IDs are also accepted in JSON input.

## `region` (type: `string`):

Two-letter ISO country code used to retrieve the media, such as US, IN, or DE.

## `saveMedia` (type: `boolean`):

Create one playable file per video and return its download URL. Saved files are billed per MB only after a successful transfer.

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

Highest video resolution to save. Best available selects the highest available quality.

## `format` (type: `string`):

Container for saved video files. Default produces MP4 for broad player compatibility.

## `includeTranscript` (type: `boolean`):

Attach timed transcript segments when a transcript is available. One extra request per video, charged only when a transcript is returned.

## `subtitleLanguage` (type: `string`):

Optional language code for one subtitle track, such as en, es, or pt-BR.

## `proxy` (type: `object`):

Accepted for compatibility with saved tasks. Media proxying is managed automatically.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://www.youtube.com/watch?v=arj7oStGLkU"
    }
  ],
  "region": "US",
  "saveMedia": false,
  "quality": "best",
  "format": "default",
  "includeTranscript": false,
  "subtitleLanguage": "en"
}
```

# Actor output Schema

## `dataset` (type: `string`):

All records scraped by this run

## `files` (type: `string`):

Files generated by this run

# 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": [
        {
            "url": "https://www.youtube.com/watch?v=arj7oStGLkU"
        }
    ],
    "region": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("thenetaji/youtube-video-downloader").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": [{ "url": "https://www.youtube.com/watch?v=arj7oStGLkU" }],
    "region": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/youtube-video-downloader").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": [
    {
      "url": "https://www.youtube.com/watch?v=arj7oStGLkU"
    }
  ],
  "region": "US"
}' |
apify call thenetaji/youtube-video-downloader --silent --output-dataset

```

## MCP server setup

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

```

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/HbjjZNb541EJth0ZM/builds/gt7qM4ALwalTq0Gfs/openapi.json
