# YouTube Video Scraper (`thenetaji/youtube-video-scraper`) Actor

Scrape metadata for batches of YouTube videos and Shorts. Optionally add related content, timed transcripts, a chosen caption track, or Shorts that use the same sound.

- **URL**: https://apify.com/thenetaji/youtube-video-scraper.md
- **Developed by:** [The Netaji](https://apify.com/thenetaji) (community)
- **Categories:** Social media, Videos, For creators
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.45 / 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/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

## YouTube Video Scraper

The Actor collects full metadata for a list of public YouTube videos and Shorts, and can attach four optional extras to each one: related content, a timed transcript, a caption track in a chosen language, and other Shorts using the same sound. Videos are accepted as URLs or bare IDs. No YouTube account, API key, or cookie is required.

### Accepted input

| Field | Type | Default | Description |
|---|---|---|---|
| `video_sources` | array | — | Required. Video or Shorts URLs, or bare 11-character IDs. |
| `video_kind` | enum | `auto` | `auto` detects Shorts from the URL, `video` and `shorts` force one endpoint. |
| `maxItems` | integer | `100` | Maximum videos processed. `0` processes every supplied target. |
| `region_code` | string | `US` | Two-letter country code used to localize results. |
| `language_code` | string | `en` | Language code for result text and transcript selection. |

Optional enrichment, each charged only on success:

| Field | Type | Default | Description |
|---|---|---|---|
| `includeRelatedContent` | boolean | `false` | Attaches related videos and playlists. |
| `includeTranscript` | boolean | `false` | Attaches the timed transcript when one exists. |
| `includeCaption` | boolean | `false` | Attaches one caption track as text. |
| `caption_language` | string | `en` | Language code of the caption track to attach. |
| `caption_translation` | string | — | Language code to translate the chosen caption into. |
| `includeSoundShorts` | boolean | `false` | For Shorts, attaches other Shorts using the same sound. |
| `maxRelatedItems` | integer | `20` | Cap on related or same-sound items per video. `0` collects every page. |

```json
{
  "video_sources": [
    { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" },
    { "url": "https://youtu.be/9bZkp7q19f0" },
    { "url": "kJQP7kiw5Fk" }
  ],
  "video_kind": "auto",
  "includeTranscript": true,
  "maxItems": 100
}
```

### Response fields

One row per video.

| Field | Contents |
|---|---|
| `recordType` | `video_details` or `short_details` |
| `source` | The URL or ID as supplied |
| `id`, `videoId` | Video ID |
| `title`, `description` | Video text |
| `url` | Canonical URL, derived from the ID |
| `channelId`, `channelTitle`, `channelHandle` | Publishing channel |
| `viewCount`, `likeCount`, `commentCount` | Reported counts |
| `lengthSeconds` | Duration in seconds |
| `publishedAt`, `publishDate` | ISO 8601 timestamp and date |
| `category` | YouTube category |
| `keywords` | Public tags declared on the video |
| `isLiveContent` | Whether the video is or was live |
| `hasCaption` | Whether YouTube reports a caption track |
| `availableCountries` | Countries the video is playable in |
| `soundAttribution` | Sound credited on a Short |
| `thumbnail` | Thumbnail images |

Present only when the matching enrichment is enabled:

| Field | Contents |
|---|---|
| `transcript` | Timed segments, plus the language menu YouTube offers |
| `caption` | The selected track's language, and its text |
| `related` | Related videos and playlists |
| `soundShorts` | Other Shorts using the same sound |

```json
{
  "recordType": "video_details",
  "id": "dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channelTitle": "Rick Astley",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "viewCount": "1798246481",
  "lengthSeconds": "213",
  "isLiveContent": false,
  "hasCaption": true
}
```

A transcript is returned as timed segments rather than a wall of text:

```json
{
  "transcript": [
    { "startMs": "320", "endMs": "14580", "startTime": "0:00", "text": "[Music]" }
  ]
}
```

### Transcripts and captions

The two are different things and are charged differently.

`includeTranscript` returns YouTube's timed transcript for the video — the same one the transcript panel shows — as segments with start and end times. It is one request per video.

`includeCaption` returns a caption track as text in a chosen language. It is two requests per video: one to list the available tracks, and one to fetch the chosen one. If `caption_language` is not among the tracks YouTube offers, a warning names the missing language and the video is still saved without a caption, uncharged. `caption_translation` asks YouTube to translate the chosen track, and the available translation languages are extensive — over 120 for a widely watched video.

Both are attached per video and both are optional. Transcript availability is a property of the video: a video with `hasCaption: false` has no transcript to return, and asking for one costs nothing.

### Behaviour on partial results

Each video is fetched individually. A target that cannot be parsed, or that YouTube declines to return, is logged and skipped, and the run continues with the rest. The run fails only when no supplied target was a usable video reference.

Fetching in batches was tested and is not used. The endpoint accepts several IDs at once, but a single unavailable video makes the whole batch fail — and which videos do this is not predictable from fetching them individually. One request per video costs more requests and loses nothing when one video is bad.

The four enrichments are independent. Each is attempted separately, and a failure leaves that one field absent, is not charged, does not affect the others, and does not interrupt the run. Enabling all four performs up to five extra requests per video, so a 500-video run with everything enabled is a large run — enable them on a shortlist rather than on a first pass.

### Frequently asked questions

**Which URL forms are accepted?**
`youtube.com/watch?v=`, `youtu.be/`, `youtube.com/shorts/`, `/embed/`, and `/live/`, plus bare 11-character video IDs. Mixing forms in one run is fine.

**When should `video_kind` be set rather than left on `auto`?**
`auto` identifies a Short from a `/shorts/` URL. A Short supplied as a bare ID or a `watch?v=` link is not distinguishable from a regular video, so it is fetched through the video endpoint — which works and returns the standard metadata, but not `soundAttribution`. Set `video_kind: "shorts"` when the whole list is Shorts and those fields matter.

**Why did `includeSoundShorts` produce nothing?**
It applies only to targets treated as Shorts. If a Short was supplied as a bare ID with `video_kind: "auto"`, it was fetched as a regular video and the same-sound lookup was skipped. Setting `video_kind: "shorts"` resolves it.

**Is `viewCount` live?**
It is the count YouTube reports at the moment of the request. YouTube itself delays and rounds view counts on recently published videos, so a figure for a video minutes old is provisional.

**Why is `likeCount` missing on some videos?**
Creators can hide the like count, and YouTube then omits it rather than returning zero. Absent means hidden or unreported; it does not mean no likes.

**Does `maxRelatedItems` count towards `maxItems`?**
No. `maxItems` bounds how many videos are processed; `maxRelatedItems` bounds the related or same-sound rows attached to each one. They are separate budgets.

**Is a YouTube account or API key required?**
No. Only publicly visible videos are read, with no credentials or logged-in session, and no YouTube Data API quota is consumed.

### Related Actors

| Actor | Purpose |
|---|---|
| [YouTube Search Scraper](https://apify.com/thenetaji/youtube-search-scraper) | Find videos by keyword, hashtag, or chart |
| [YouTube Channel Scraper](https://apify.com/thenetaji/youtube-channel-scraper) | Everything a channel publishes |
| [YouTube Comments Scraper](https://apify.com/thenetaji/youtube-comments-scraper) | Comments from videos, Shorts, and community posts |
| [YouTube Playlist Scraper](https://apify.com/thenetaji/youtube-playlist-scraper) | Playlist metadata and every video in it |

# Actor input Schema

## `video_sources` (type: `array`):

One or more public YouTube video or Shorts URLs. Bare video IDs are also accepted.

## `includeRelatedContent` (type: `boolean`):

Attach related videos and playlists to every eligible result. One request per page of related items, charged only when it succeeds.

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

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

## `includeCaption` (type: `boolean`):

Attach one caption track in the selected language when available. Two extra requests per video, charged only when the track is returned.

## `caption_language` (type: `string`):

Language code of the caption track to attach, such as en, es, or pt-BR.

## `includeSoundShorts` (type: `boolean`):

For Shorts inputs, attach other Shorts attributed to the same sound. One request per page, charged only when it succeeds.

## `maxRelatedItems` (type: `integer`):

Maximum related or same-sound items attached to each video. Set 0 to collect all available pages.

## `video_kind` (type: `string`):

Choose automatic detection, regular videos, or Shorts.

## `maxItems` (type: `integer`):

Maximum number of primary records to save across all targets. Set 0 to continue until no more pages remain.

## `region_code` (type: `string`):

Optional two-letter country code used to localize results, such as US, IN, or DE.

## `language_code` (type: `string`):

Optional language code used to localize result text, such as en, es, or pt-BR.

## Actor input object example

```json
{
  "video_sources": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "includeRelatedContent": false,
  "includeTranscript": false,
  "includeCaption": false,
  "caption_language": "en",
  "includeSoundShorts": false,
  "maxRelatedItems": 10,
  "video_kind": "auto",
  "maxItems": 20,
  "region_code": "US",
  "language_code": "en"
}
```

# Actor output Schema

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

Dataset containing all scraped data

# 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 = {
    "video_sources": [
        {
            "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
        }
    ],
    "maxRelatedItems": 10,
    "maxItems": 20,
    "region_code": "US"
};

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

# Run the Actor and wait for it to finish
run = client.actor("thenetaji/youtube-video-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "video_sources": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "maxRelatedItems": 10,
  "maxItems": 20,
  "region_code": "US"
}' |
apify call thenetaji/youtube-video-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=thenetaji/youtube-video-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/reJ2tGZdERO3DXXn1/builds/3K9UVYJIEoBhrVQL2/openapi.json
