# YouTube Video Details | Views, Dates & Captions (`keystonelabs/youtube-video-details-scraper`) Actor

Extract metadata for supplied YouTube video links: title, channel, views, publish date, duration and caption availability. Export JSON or CSV for video research. Does not discover channel videos or retrieve transcripts.

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

## Pricing

$1.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.

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

### Build a video research spreadsheet

YouTube Video Details: Extract metadata for supplied YouTube video links: title, channel, views, publish date, duration and caption availability. Export JSON or CSV for video research. Does not discover channel videos or retrieve transcripts.

### Start with a small example

1. Open Input, switch to JSON and paste the example below. Replace the example videoUrls with your own research targets when ready.
2. Check the live Pricing tab and set a run spending limit.
3. Run the Actor, then open the **Workflow** output view. Inspect `videoId`, `title`, `channelName`, `viewCount`, `publishDate`, `durationSeconds`, `error`.
4. Export JSON for nested data or CSV for a spreadsheet. Review a small sample before increasing the scope.

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "9bZkp7q19f0"
  ],
  "maxItems": 3
}
```

At the rate checked September 9, 2026, 3 saved rows cost $0.003 in Actor event fees ($1.00 per 1,000 rows). Actual returned rows determine the event fee. Taxes and other account services may be separate.

### What can I use it for?

- **Content research and competitor analysis.** Pull views, likes, publish dates and durations for a competitor's catalogue and see what performs.
- **Enriching video lists.** Turn a spreadsheet of links into a dataset with titles, channels, dates and engagement numbers.
- **Monitoring.** Re-run on a schedule to track view and like counts over time for the videos you care about.
- **AI and RAG pipelines.** Combine with the YouTube Transcript Scraper: this actor tells you which videos have captions and in which languages before you spend on transcripts.
- **Curation and cataloguing.** Category, keywords, duration and thumbnails for building playlists, feeds or directories.
- **Link validation.** Dead, private and age-restricted videos come back flagged, so your links stay clean.

### What data does it return?

One record per video:

| Field | Meaning |
|---|---|
| `videoId`, `url`, `input` | The resolved video, its canonical URL and the exact string you supplied |
| `title`, `description` | Title and full description text |
| `channelName`, `channelId`, `channelUrl` | The uploading channel |
| `durationSeconds` | Length in seconds |
| `viewCount`, `likeCount` | Public counts at the time of the run |
| `publishDate`, `uploadDate` | ISO timestamps as YouTube reports them |
| `category` | YouTube's category, e.g. `Music`, `Education` |
| `keywords` | The uploader's tags |
| `thumbnailUrl`, `thumbnails` | The largest thumbnail, and every size with dimensions |
| `isLiveContent`, `isPrivate`, `isUnlisted`, `isFamilySafe` | Flags |
| `availableCountriesCount` | How many countries the video is available in |
| `captionsAvailable`, `captionLanguages`, `autoCaptionsAvailable` | Whether captions exist, in which languages, and whether any are auto-generated |
| `playability` | YouTube's own status, `OK` for normal videos |
| `error` | `null` on success, otherwise a plain-language reason |

Invalid links, deleted videos and private videos still produce a record with an `error`, so your input and output always line up one to one. Age-restricted videos return their metadata with `playability` set accordingly.

#### Illustrative output

Values explain the output shape; they are not a live result or guaranteed field coverage.

```json
{
  "videoId": "jNQXAC9IVRw",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "title": "Me at the zoo",
  "description": "The first video on YouTube. ...",
  "channelName": "jawed",
  "channelId": "UC4QobU6STFB0P71PMvOGN5A",
  "durationSeconds": 19,
  "viewCount": 417706870,
  "likeCount": 18123456,
  "publishDate": "2005-04-23T20:31:52-07:00",
  "category": "People & Blogs",
  "keywords": ["me at the zoo", "jawed karim", "first youtube video"],
  "thumbnailUrl": "https://i.ytimg.com/vi/jNQXAC9IVRw/maxresdefault.jpg",
  "isLiveContent": false,
  "captionsAvailable": true,
  "captionLanguages": ["en", "de"],
  "autoCaptionsAvailable": false,
  "playability": "OK",
  "error": null
}
```

### How to use it

1. Paste your video links or IDs into **YouTube videos**, one per line, or pass them through the API as `videoUrls`.
2. Click **Start**. The defaults are tuned for most jobs.
3. Download results from the **Output** tab as JSON, CSV, Excel or XML, or read them from the dataset via the API.

#### Input example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "9bZkp7q19f0"
  ],
  "maxItems": 100
}
```

### How it works

The actor asks YouTube's own player service for each video's details, the same service the mobile apps use, and makes a second lightweight request for the fields only the web player exposes — publish date, like count, category and the unlisted flag. Numbers are returned as integers, dates as ISO strings, and text is decoded so titles read the way they do on YouTube.

If YouTube refuses a direct request, the actor automatically retries that video through a residential proxy, and keeps using it for the rest of the run once it has been needed. You do not need to configure anything for this.

### Pricing

You pay per video processed. There are no subscriptions or minimums, and you can cap the maximum spend of any run before it starts. Videos that turn out to be deleted or private still count as processed, because the actor has to ask YouTube to find that out.

### Scope and responsible use

This actor reads only the public metadata YouTube serves for a video. It does not log in, does not use cookies or credentials, does not download video or audio, and does not attempt to bypass age gates, membership restrictions or privacy settings. You are responsible for using the results in line with the rights of the people who made the content and the laws that apply to you.

### Limitations

- Like counts and publish dates come from the web player's metadata block; on the rare video where YouTube omits it, those fields are `null` while the rest of the record is intact.
- Comment counts, subscriber counts and per-video revenue are not part of what YouTube exposes here and are not returned.
- Playlists and channel URLs are not expanded; supply the individual video links.
- Live streams in progress report the metadata available at that moment.

### Something wrong?

Open an issue on this actor's **Issues** tab with the video link and what you expected. YouTube changes its internals from time to time and source changes can affect coverage; include a small reproducible example for investigation.

### Interpret empty results and errors

Check the run status and log if no records are returned. Restrictive filters, inaccessible content or a changed source page can produce an empty result; empty output does not prove there are no matching records on the source.

Every saved row, including an error row, incurs the configured per-row event fee. Proxy selection and run duration can affect operating costs. Do not increase concurrency or enable a more expensive proxy merely to work around an unclear result.

For a reproducible problem, open this Actor’s Issues tab with a small public input, expected behavior and relevant error text. Remove tokens and confidential information. No response-time or uptime guarantee is offered.

### Use the result in an automation

In Make or n8n, use the Apify integration to run this Actor with the same JSON input, wait for completion, then retrieve its default dataset. Route failed runs and error rows to a review step before sending valid results to your spreadsheet or CRM. Scheduling does not make these Actors emit only new records: deduplicate downstream using the source URL or record ID.

For Node.js, install the official `apify-client` package and set your own `APIFY_TOKEN` environment variable. The following example starts a paid run with a small spending limit.

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const input = {
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "9bZkp7q19f0"
  ],
  "maxItems": 3
};
const run = await client.actor('keystonelabs/youtube-video-details-scraper').call(input, {
  memory: 512, timeout: 180, maxTotalChargeUsd: 0.10
});
if (run.status !== 'SUCCEEDED') throw new Error('Run did not succeed: ' + run.id);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 100 });
console.log(items);
```

The first 100 rows are retrieved in this example. Use the dataset API pagination for a larger result. Keep credentials out of shared inputs and source files.

### A useful next step

- [YouTube Transcript Scraper | Text, SRT & VTT](https://apify.com/keystonelabs/youtube-transcript-scraper): turn captioned video links into searchable text. This is a separate Actor with its own input and price.

# Actor input Schema

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

Video URLs or IDs, one per line. Accepts watch URLs, youtu.be links, Shorts, embed URLs and bare 11-character video IDs. Duplicates are removed.

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

Stop after this many videos.

## `maxConcurrency` (type: `integer`):

How many videos to process in parallel.

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

Optional. The actor already retries through residential proxies automatically if YouTube blocks a request, so you normally leave this off.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "9bZkp7q19f0"
  ],
  "maxItems": 3,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `videos` (type: `string`):

Dataset of video metadata records.

## `workflowJson` (type: `string`):

Selected columns for this workflow. Inspect errors and missing values before using the data.

## `workflowCsv` (type: `string`):

Download the workflow columns as CSV. Use JSON for full nested arrays.

# 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": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/jNQXAC9IVRw",
        "9bZkp7q19f0"
    ],
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("keystonelabs/youtube-video-details-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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/jNQXAC9IVRw",
        "9bZkp7q19f0",
    ],
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("keystonelabs/youtube-video-details-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 '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw",
    "9bZkp7q19f0"
  ],
  "maxItems": 3
}' |
apify call keystonelabs/youtube-video-details-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,keystonelabs/youtube-video-details-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/1cFcUojYbSaqpw0hg/builds/QeyyfHVpLeXIxE6Cp/openapi.json
