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

YouTube video scraper for Apify — get YouTube video metadata (title, channel, views, duration, description, likes, thumbnails) from watch URLs, Shorts, or IDs. Bulk Dataset export. Not a video downloader.

- **URL**: https://apify.com/scrapemamba/youtube-video-scraper.md
- **Developed by:** [Scrape Mamba](https://apify.com/scrapemamba) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.70 / 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.

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 Scraper

**[YouTube Video Scraper](https://apify.com/scrapemamba/youtube-video-scraper)** is a **youtube video scraper** and **youtube data scraper** for Apify: scrape YouTube watch URLs, Shorts links, `youtu.be` short links, embed URLs, or bare 11-character video IDs into structured **youtube video metadata** — title, channel, views, duration, description, likes, thumbnails, and more — and store each result in an Apify Dataset you can export as JSON, CSV, or Excel.

Built by **ScrapeMamba** as an **apify youtube scraper** Actor for **youtube scraping** at scale. It normalizes inputs, deduplicates IDs, calls ScrapeMamba’s YouTube API (`POST /youtube/video_details` with `{ video_id }`), and pushes clean rows (plus a `raw` payload) into the run Dataset — no browser farm to maintain in your account. Use it as a **youtube scraper api** workflow through Apify’s REST API or Client SDKs when you need to **get youtube video metadata** in bulk from IDs you already have, and to **scrape YouTube** video pages without quota theater or fragile HTML parsers.

***

### Overview

Collecting public video fields at scale is awkward via the official Data API alone (quotas, project setup, field gaps) or brittle HTML parsers. A dedicated **youtube metadata scraper** on Apify solves that:

This Actor workflow:

1. You pass a list of `videoIds` (URLs or IDs).
2. The Actor normalizes each value to a canonical 11-character ID and drops duplicates.
3. Up to **10 workers** fetch details in parallel via ScrapeMamba.
4. Each success becomes a Dataset item with `type: "video"`; each failure becomes `type: "error"` so one bad ID does not abort the whole run.
5. Mapped fields are flattened for tables; the full backend response is retained under `raw` when you need deeper inspection later.

Typical outcomes: a Dataset ready for BI joins on `videoId`, a CSV for editorial review, or a webhook payload into your warehouse pipeline.

***

### Who this Actor is for

- **Data / analytics teams** building performance baselines, niche catalogs, or competitor video tables.
- **Agencies and researchers** who already discovered IDs and need consistent enrichment without quota theater.
- **Product and growth teams** wiring Apify schedules or the Apify API into CRM, Sheets, or ETL.
- **Pipelines** that chain **Search** or **Channel Videos** → **Video Scraper** → **Subtitles** / **Related Videos**.

#### Who this is not for

- **Not a YouTube video downloader.** This Actor extracts **public metadata and text fields only** — not MP4/WebM media, audio tracks, or offline playback files. Use it for titles, stats, descriptions, and thumbnails — not saving video files.
- Less ideal for keyword discovery alone (**YouTube Search Scraper**) or channel profile stats (**YouTube Channel Scraper**). Those Actors produce the IDs and channel keys this one consumes.

***

### What it does

- Accepts watch URLs, Shorts URLs, `youtu.be` links, embed URLs, or raw 11-character IDs
- Normalizes and **deduplicates** IDs before fetching
- Returns titles, stats, descriptions, thumbnails, and channel identity fields
- Batches many videos per run with **per-item error rows**
- Includes a `raw` object with the full ScrapeMamba response for advanced consumers
- Runs with up to **10 parallel workers** for throughput on larger lists

***

### Why ScrapeMamba on Apify

ScrapeMamba’s Actors call a hardened scrape backend for stable mapped schemas, run-based Apify billing, and a composable suite (Search, Channel Videos, Video details, Subtitles, Related, Channel profile) sharing `videoId` / `channelId` join keys — plus Console UI, Client SDKs, REST, schedules, webhooks, and Dataset exports.

Use this Actor when you already have IDs. Chain after **YouTube Search Scraper** or **YouTube Channel Videos Scraper**, then hand IDs to **YouTube Subtitles Scraper** or **YouTube Related Videos Scraper**. Many teams treat the suite as a strong **best youtube scraper** fit when enrichment must join cleanly across discovery, captions, and recommendations.

***

### What data can it extract?

Successful rows expose mapped fields such as:

| Field | Description |
|-------|-------------|
| `type` | Always `"video"` on success |
| `videoId` | Canonical 11-character identifier |
| `title` | Video title |
| `author` | Uploader / channel display name |
| `channelId` | Canonical channel ID (`UC…`) when available |
| `views` | View count when exposed |
| `length` | Duration string (for example `"3:33"`) |
| `publishedTime` | Publish date or relative publish label when available |
| `description` | Description text suitable for tables and search |
| `likeCount` | Like count when YouTube exposes it (may be `null`) |
| `thumbnail` | Preview image URL |
| `url` | Canonical watch URL |
| `raw` | Full backend payload for the video |

Error rows use `type: "error"` with `videoId` and an `error` message string.

***

### Pricing

- **$2.70 per 1,000 results**

Billed on Apify for Dataset rows produced by the run. Check the Actor page for free-tier eligibility and platform nuances.

Store listing: <https://apify.com/scrapemamba/youtube-video-scraper>

***

### Input

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `videoIds` | `string[]` | **Yes** | YouTube video URLs or 11-character IDs |

#### Accepted input shapes

Each list entry may be any of:

- Watch URL: `https://www.youtube.com/watch?v=dQw4w9WgXcQ`
- Shorts URL: `https://www.youtube.com/shorts/XXXXXXXXXXX`
- Short link: `https://youtu.be/XXXXXXXXXXX`
- Embed URL: `https://www.youtube.com/embed/XXXXXXXXXXX`
- Bare ID: `dQw4w9WgXcQ` (exactly 11 characters after normalization)

Invalid or unparseable entries are filtered out during normalization. If **no** valid IDs remain, the Actor exits with an error before fetching.

#### Deduplication

The Actor maps every input through `normalizeVideoId`, then builds a unique set. If your list contains the same video as both a watch URL and a Shorts URL, you pay for **one** fetch, not two.

#### Input JSON example

```json
{
  "videoIds": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/dQw4w9WgXcQ",
    "dQw4w9WgXcQ"
  ]
}
```

After normalization and dedupe, the example above becomes a single fetch for `dQw4w9WgXcQ`.

***

### Output

#### Output types

| `type` | Meaning |
|--------|---------|
| `video` | Successful metadata extraction |
| `error` | Fetch or mapping failure for that ID |

#### Successful example

```json
{
  "type": "video",
  "videoId": "dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video)",
  "author": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "views": 1540000000,
  "length": "3:33",
  "publishedTime": "2009-10-25",
  "description": "The official video for “Never Gonna Give You Up” by Rick Astley...",
  "likeCount": 16000000,
  "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "raw": {}
}
```

(`raw` contains the full API response object; truncated here for readability.)

#### Error example

```json
{
  "type": "error",
  "videoId": "xxxxxxxxxxx",
  "error": "Error: ..."
}
```

Filter `type == "video"` for analytics; quarantine errors for retries. Join Subtitles / Related on `videoId`. Prefer mapped columns for BI; use `raw` when a flat field is missing.

***

### How to scrape YouTube video metadata on Apify

1. Open **[YouTube Video Scraper](https://apify.com/scrapemamba/youtube-video-scraper)** on the Apify Store.
2. In **Input**, pass a `videoIds` list — start with one known-public video.
3. Start the run; confirm a `type: "video"` row in the **Dataset**.
4. Scale the list, then export **JSON / CSV / Excel**, or attach a **webhook** / **schedule**.

#### Quick start checklist

1. Run one known-public video ID
2. Verify mapped fields (and `raw` if needed)
3. Scale the ID list
4. Schedule or call via Apify API for production

***

### Local run notes

```bash
cd youtube-video-scraper-js
cp .env.example .env
npm install
```

Provide input at `storage/key_value_stores/default/INPUT.json`:

```json
{
  "videoIds": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
}
```

Then `node src/main.js`.

***

### YouTube scraper API examples (Apify)

Call this Actor like a **youtube scraper api**: Apify starts the run, ScrapeMamba fetches details, and you consume Dataset items programmatically.

#### cURL (start Actor run via Apify API)

```bash
curl "https://api.apify.com/v2/acts/scrapemamba~youtube-video-scraper/runs" \
  -H "Content-Type: application/json" \
  -d '{
    "videoIds": [
      "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ]
  }'
```

Poll the run, then fetch Dataset items from the run’s default Dataset ID returned in the start response.

#### JavaScript (Apify Client)

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

const client = new ApifyClient();

const run = await client
  .actor('scrapemamba/youtube-video-scraper')
  .call({
    videoIds: [
      'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
      '9bZkp7q19f0',
    ],
  });

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const videos = items.filter((row) => row.type === 'video');
console.log(videos.map((v) => ({ id: v.videoId, title: v.title, views: v.views })));
```

#### Python (youtube scraper python)

```python
from apify_client import ApifyClient

client = ApifyClient()

run = client.actor("scrapemamba/youtube-video-scraper").call(
    run_input={
        "videoIds": [
            "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
            "9bZkp7q19f0",
        ]
    }
)

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("type") == "video":
        print(item["videoId"], item.get("title"), item.get("views"))
```

#### Backend endpoint used by the Actor

Internally: `POST /youtube/video_details` with `{ "video_id": "<11-char-id>" }`. Prefer Apify orchestration for Dataset, billing, and retries rather than calling that endpoint directly.

***

### Pipeline patterns

1. Discover IDs with **YouTube Search Scraper** or **YouTube Channel Videos Scraper**
2. Enrich here into a video dimension table (`videoId` primary key)
3. Optionally pull captions (**Subtitles**) or recommendations (**Related Videos**)
4. Optionally attach uploader profiles from **YouTube Channel Scraper** via `channelId`

Join on `videoId`. Filter `type != "error"` in staging. Persist errors with a retry counter. Deduplicate upstream lists when merging discovery feeds.

***

### Where teams use the data

Catalog enrichment and performance baselines; brand-safety archives of public metadata; editorial planning; recommendation prototypes with Related + Subtitles; competitor monitoring after Channel Videos discovery.

***

### Export and automation

Dataset export (**JSON / CSV / Excel**), **Apify Client** (JS/Python), **Apify REST API**, **schedules**, and **webhooks** cover ad-hoc analysis through production ETL. Combine schedules with an upstream script that refreshes `videoIds` for hands-off runs.

***

### Error handling

Partial success by design:

- Missing/empty `videoIds` or no valid IDs after normalization → early exit.
- Per-ID API failures → `type: "error"` row; other IDs continue.
- Private, deleted, or region-unavailable videos typically surface as error rows.

Upsert `type == "video"` into `dim_video` on `videoId`; quarantine errors with `runId` / timestamp. Retry transient failures; skip permanent “not found” IDs.

***

### Best practices

1. **Start small** — Validate schema on 1–5 public IDs before large batches.
2. **Prefer IDs** — Bare 11-character IDs when you have them.
3. **Dedupe upstream** — Especially when merging Search + Channel Videos feeds.
4. **Separate error sinks** — Quarantine `type: "error"` from analytics tables.
5. **Use `raw` sparingly** — Flat fields cover most BI.
6. **Split huge lists** — Schedule chunks for clearer monitoring.
7. **Snapshot history** — Stats change; store `runId` / timestamp when needed.
8. **Chain intentionally** — discovery → enrichment → Subtitles/Related.

***

### Troubleshooting

| Symptom | Likely cause | What to try |
|---------|--------------|-------------|
| Run fails immediately with no Dataset | Empty `videoIds` or no valid IDs | Check Input JSON; ensure URLs contain `v=` / Shorts / `youtu.be` IDs |
| Some videos succeed, some error | Expected for bad/private/deleted IDs | Inspect `error` strings; retry only transient failures |
| Duplicate-looking inputs but one result | Deduplication working | Confirm normalized IDs collide (watch vs Shorts vs bare ID) |
| `likeCount` or other fields `null` | YouTube did not expose the field | Treat as nullable; do not fail the pipeline |
| Local run finds no input | Missing `INPUT.json` | Create `storage/key_value_stores/default/INPUT.json` |
| Need channel profile, not video stats | Wrong Actor | Use **YouTube Channel Scraper** |

***

### Limitations

- Private, deleted, age-restricted, or region-blocked videos may produce error rows.
- Some engagement fields may be `null` depending on public exposure.
- Description follows source availability; treat as text, not HTML DOM.
- Public data only — you own compliance with YouTube terms, law, and policy.
- Enriches known IDs; does not search keywords or paginate channel uploads.
- Does not download video or audio files — metadata and text only.

***

### Compliance and responsible use

ScrapeMamba Actors are built for **public** YouTube metadata. Ensure lawful use under privacy, copyright, and platform terms. Do not access private content, bypass login walls, or collect personal data unlawfully.

***

### FAQ

#### How to scrape YouTube video metadata with this Actor?

Pass a `videoIds` array of URLs or IDs, start the run, then export the Dataset as JSON, CSV, or Excel — or pull items via the Apify API/Client. That is the supported way to **scrape YouTube** video pages for structured fields at scale.

#### How do I get YouTube video metadata in bulk?

Pass watch URLs or 11-character IDs as `videoIds` and run the Actor. Mapped columns (`title`, `views`, `description`, `channelId`, and more) land in the Dataset; use `raw` for attributes not yet flattened.

#### Is this a YouTube video downloader?

No. This is a metadata scraper, not a media downloader. It returns public video fields for analytics and enrichment — not MP4/WebM files or ripped audio.

#### Can I use this as a YouTube scraper API?

Yes: call the Actor through Apify’s REST API or Client with the same JSON input. The Actor talks to ScrapeMamba’s `POST /youtube/video_details` endpoint and writes Dataset rows — a managed **youtube scraper api** pattern without hosting scrape servers yourself.

#### How do I call this with YouTube scraper Python code?

Install `apify-client`, call `client.actor("scrapemamba/youtube-video-scraper").call(...)` with a `videoIds` list, then iterate Dataset items.

#### How many videos can I process in one run?

Large `videoIds` lists are fine — up to 10 parallel workers with ID dedupe. Split very large catalogs across scheduled runs for clearer monitoring.

#### Why did my watch URL and Shorts URL produce one row?

Both normalize to the same 11-character `videoId`; deduplication keeps a single fetch. That is intentional.

#### Some rows are errors — is that expected?

Yes. Unavailable or invalid IDs become `type: "error"` instead of aborting the job. Filter in staging and retry selectively.

#### Does the output include the full API response?

Yes. Successful items include a `raw` field with the backend payload plus mapped columns.

#### Can I get subtitles from this Actor?

No. Use **YouTube Subtitles Scraper** with the same `videoId` values after enrichment.

#### How do I schedule daily enrichment?

Create an Apify Schedule (or Actor task with saved Input). Refresh `videoIds` via task input or an upstream process.

#### Can I run this locally?

Yes. Copy `.env.example` to `.env`, place Input under the default key-value store path, install dependencies, and run `node src/main.js`.

#### How does pricing work?

**$2.70 per 1,000 results** on Apify for this Actor. Review the Store page for current billing nuances and platform fees.

***

### Related ScrapeMamba Actors

| Actor | Best for |
|-------|----------|
| [YouTube Search Scraper](https://apify.com/scrapemamba/youtube-search-scraper) | Keyword → video IDs |
| [YouTube Channel Videos Scraper](https://apify.com/scrapemamba/youtube-channel-videos-scraper) | Upload lists / discovery |
| [YouTube Subtitles Scraper](https://apify.com/scrapemamba/youtube-subtitles-scraper) | Transcripts / captions |
| [YouTube Related Videos Scraper](https://apify.com/scrapemamba/youtube-related-videos-scraper) | Recommendations graph |
| [YouTube Channel Scraper](https://apify.com/scrapemamba/youtube-channel-scraper) | Uploader profiles |

**Suggested pipeline:** Search or Channel Videos → **YouTube Video Scraper** → Subtitles and/or Related Videos; join Channel Scraper on `channelId` when you need subscriber and About metadata.

# Actor input Schema

## `videoIds` (type: `array`):

YouTube video URLs or 11-character video IDs.

## Actor input object example

```json
{
  "videoIds": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ]
}
```

# Actor output Schema

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

Default dataset items produced by this Actor.

# 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 = {
    "videoIds": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapemamba/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 = { "videoIds": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] }

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

```

## MCP server setup

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

```

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/dY32juylQPgVbpKzK/builds/g3lEdjh6Vvg2tub90/openapi.json
