# YouTube Playlist Scraper: Videos & Metadata (`deepmine/youtube-playlist-scraper`) Actor

YouTube playlist scraper: paste playlist links or search words, get every video with its true position, views, exact likes, comments, publish time, duration, tags and channel, plus the playlist's title, owner, counts and last update. Deleted videos are listed, never charged. No login.

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

## Pricing

from $0.22 / 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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Playlist Scraper: Videos & Metadata

YouTube playlist scraper: paste playlist links (or find playlists by search words or a channel link) and get every video in playlist order with its **true position**, exact views, likes and comment counts, exact publish time, duration, description, tags and channel stats, plus the playlist's own title, owner, video count, views and last update on every row. Deleted and private entries keep their places and are listed with YouTube's reason, never charged. No login, no YouTube API key, no browser.

| # | 🎬 Video | 📺 Channel | 👀 Views | ❤️ Likes | 💬 Comments | 📅 Published | ⏱️ Length |
|---|---|---|---|---|---|---|---|
| 1 | [Crystal Castles - Alice Practice (Music Video)](https://www.youtube.com/watch?v=PbPL86YAWjk) | xxzxcuzx me! | 17,924 | 716 | 35 | 2022-11-12 | 00:02:19 |
| 2 | *This video is private* (listed in the run summary, not charged) | | | | | | |
| 3 | [Crystal Castles - Crimewave (Official Video)](https://www.youtube.com/watch?v=sEKOczkt0tU) | Gastón Ossandón | 576,992 | 12,367 | 488 | 2011-07-16 | 00:02:53 |
| 4 | [Crystal Castles - Crimewave (2007 HD Music Video)](https://www.youtube.com/watch?v=bNjxrVVtwTI) | xxzxcuzx me! | 2,579 | 146 | 9 | 2024-07-10 | 00:02:50 |
| 5 | [Crystal Castles - "Magic Spells" - Official Video](https://www.youtube.com/watch?v=zSsvcT9cDZ0) | Crystal Castles official | 876,262 | 8,852 | 629 | 2008-03-12 | 00:00:55 |
| 6 | [Air War - Crystal Castles (Official Music Video)](https://www.youtube.com/watch?v=QZUvP2AQsps) | WaxTrax ! | 13,308 | 522 | 49 | 2022-08-18 | 00:02:28 |
| 7 | *This video is private* (listed in the run summary, not charged) | | | | | | |
| 8 | [Crystal Castles "COURTSHIP DATING" Official](https://www.youtube.com/watch?v=JwiG9CUDo-I) | Crystal Castles official | 3,384,045 | 48,815 | 1,231 | 2009-10-18 | 00:02:36 |
| 9 | [Vanished - Crystal Castles Music Video (Alternate)](https://www.youtube.com/watch?v=R4YDswiuzrE) | cc | 2,128 | 84 | 6 | 2025-01-25 | 00:02:42 |
| 10 | *This video is private* (listed in the run summary, not charged) | | | | | | |
| 11 | [Crystal Castles "TELL ME WHAT TO SWALLOW" (Home Footage Video)](https://www.youtube.com/watch?v=SC4Xs8Z0D8E) | Untitled Profile | 31,762 | 1,705 | 50 | 2017-12-14 | 00:02:13 |
| 12 | [Crystal Castles - Celestica](https://www.youtube.com/watch?v=IsxNUl1IHnE) | Crystal Castles official | 13,802,606 | hidden | 4,111 | 2010-07-20 | 00:03:28 |

<sub>The first 12 entries of [Crystal Castles Music Videos Official](https://www.youtube.com/playlist?list=PLpzb2yI5Jct7iaQGqn6HE4YF5ZwyBgdVl) (25 entries, owner ★Dead_Ericc★), collected on 2026-10-18. Every row also carries the playlist's own fields.</sub>

**$0.29 per 1,000 videos on Starter** ($0.30 Free, $0.25 Scale, $0.22 Business). The prefilled run (50 videos of one playlist) costs about **$0.0145**. Nothing per run, nothing per playlist or search, and nothing for a deleted or private entry.

| Plan | Price per 1,000 videos |
|---|---|
| Free | $0.30 |
| Starter | $0.29 |
| Scale | $0.25 |
| Business | $0.22 |

### What you type

| Input | Example | You get |
|---|---|---|
| 🔗 Playlist links or IDs | `https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek`, `https://www.youtube.com/watch?v=kXYiU_JCYtU&list=PL6Lt9p1lIRZ311J9ZHuzkR5A3xesae2pk`, `PL...`, `UU...` (a channel's uploads), `OLAK5uy_...` (an album) | Every video of each playlist, in playlist order |
| 📺 Channel links | `https://www.youtube.com/@Fireship` | That channel's playlists (its Playlists tab), each read like a link |
| 🔍 Search words | `python tutorial` | The playlists, courses and podcasts YouTube's playlist search finds, each read like a link |
| Max videos per playlist | `50` | The first 50 available videos of each playlist (empty = all) |
| Max playlists per search or channel | `10` (default) | How many playlists a search or channel link reads |
| Full video details | on (default) | Off: rows from the playlist pages only (position, title, duration, rounded views, relative age, channel), several times faster on huge playlists, same price |

Playlist pages, watch links with `&list=`, `music.youtube.com` and mobile links all work, as do podcasts and courses. Mixes (`RD...`) and personal lists (Watch later, Liked videos) aren't public, so they're skipped with a note. A video that is in several of your playlists is saved and charged once, with the first playlist that lists it; the run summary says where else it appears.

### Positions and unavailable entries

`position` is YouTube's own numbering of the playlist (1 = first). Playlists are read the way YouTube's "Show unavailable videos" option shows them, so a deleted or private entry keeps its place and the videos after it keep theirs. Without that option YouTube renumbers the list and hides entries: a 575-entry playlist showed 533 entries numbered 1 to 533 on 2026-10-16.

Deleted and private entries are never rows and never charged. The run summary (key-value store record `OUTPUT`) lists them for each playlist with YouTube's reason:

```json
{
  "playlistId": "PLr5X5rpmjKVkkQyRoN9NWqhRxTtWqFgi5",
  "title": "Jonas Brothers All Music Videos",
  "status": "ok",
  "videoCount": 180,
  "listed": 180,
  "videosRead": 170,
  "unavailableCount": 10,
  "unavailable": [
    {"position": 14, "id": "sp9-c-qSgso", "reason": "This video is private"},
    {"position": 118, "id": "ej8Tt4RCI8g", "reason": "Video unavailable: It was removed following a copyright removal request by Universal Music Group"},
    {"position": 130, "id": "xFzKpvchphw", "reason": "Video unavailable: This video is no longer available because the YouTube account associated with this video has been terminated."}
  ],
  "totalDurationSeconds": 31444
}
```

(Shortened: this playlist has 10 unavailable entries.) The summary also gives each playlist's status (`ok`, `notFound` with YouTube's reason, `empty`), its total duration, and the entries saved with another playlist.

### What you get in every row

| Group | Fields |
|---|---|
| 📍 Position | `position` (1 = first, YouTube's numbering) |
| 🎬 Video | `title`, `type` (`video`, `shorts`, `stream`), `id`, `url`, `embedUrl`, `text` (description), `descriptionLinks`, `hashtags`, `keywords` (tags), `category`, `duration`, `durationSeconds` |
| 🗣️ Language | `originalLanguage`, `originalLanguageCode` (the spoken language, e.g. English / en), `audioTracks` (dubbed audio tracks: language, name, original or automatic dub; null with one track) |
| 📈 Stats | `viewCount`, `likes` (exact), `commentsCount` (exact: 1,231, not YouTube's rounded 1.2K), `commentsTurnedOff` |
| 📅 Dates | `date` (exact publish time, UTC), `uploadDate`, `dateText`, `publishedTimeText` ("3 years ago"), `liveStartedAt`, `liveEndedAt`, `streamType`, `isLiveNow` |
| 🖼️ Thumbnails | `thumbnailUrl` (the largest) and `thumbnails`: every size YouTube lists, smallest first, `{url, width, height}` up to 1280x720 |
| 🚩 Flags | `hasCaptions`, `isAgeRestricted`, `isFamilySafe`, `isUnlisted`, `isPrivate`, `isMembersOnly`, `isPaidContent` ("Includes paid promotion"), `isMonetized` (YouTube serves ads on it to a logged-out viewer; null on Shorts), `allowRatings`, `availableCountries` |
| 📺 Channel | `channelName`, `channelUrl`, `channelUsername`, `channelId`, `numberOfSubscribers`, `isChannelVerified`, `channelBadges` ("Verified", "Official Artist Channel"), `channelAvatarUrl`, `channelDescription`, `channelDescriptionLinks`, `channelJoinedDate`, `channelCreatedAt` (to the second, when the run reads the channel's feed anyway), `channelLocation`, `channelTotalVideos`, `channelTotalViews`, `collaborators` |
| 💬 Captions and previews | `captionTracks` (each caption track's language, name, whether it's automatic, and its link), `videoPreviews` (YouTube's storyboard preview images per size), `isEmbeddable` |
| 🎵 Shorts | `soundTitle`, `musicTitle`, `musicArtist` |
| 📃 Playlist | `playlistId`, `playlistUrl`, `playlistTitle`, `playlistDescription`, `playlistType` (Playlist, Podcast, Course), `playlistVisibility` (public, unlisted), `playlistVideoCount`, `playlistViewCount`, `playlistLastUpdated`, `playlistLastUpdatedText`, `playlistThumbnailUrl`, `playlistOwnerName`, `playlistOwnerId`, `playlistOwnerUrl`, `playlistOwnerUsername` |
| 🧾 Run | `input` (what you typed), `searchQuery` (the words that found the playlist), `scrapedAt` |

The dataset has four views: Videos, Playlists, Video details, and Channels.

Measured on the 242 rows of two runs on Apify on 2026-10-17 (the prefill and two playlists with 13 unavailable entries):

| Field | Filled |
|---|---|
| `position` | 100% |
| `viewCount` | 100% |
| `likes` | 98% (the rest have likes hidden by the uploader) |
| `commentsCount` | 100% |
| `date` (exact time) | 100% |
| `durationSeconds` | 100% |
| `text` (description) | 90% |
| `keywords` | 87% |
| `category` | 100% |
| `thumbnails` | 100% |
| `hasCaptions` | 100% |
| `numberOfSubscribers` | 100% |
| `channelTotalViews` | 100% |
| `playlistTitle`, `playlistOwnerName`, `playlistVideoCount`, `playlistLastUpdated` | 100% |

`text` and `keywords` are empty where the video has none.

### Speed

On Apify at the default 512 MB: the prefill (50 videos) ran 10-16 seconds, first row after 2.3-4.5 seconds; two playlists with 192 videos ran 34-43 seconds; 1,000 entries of a channel's uploads with full video details off ran 19 seconds. apidojo's run of 10 videos from the same playlist ran 50 seconds (at its default 256 MB).

### Moving from another playlist Actor

Saved inputs run here unchanged: `startUrls` and `keywords` and `maxItems` (apidojo), `playlistUrls`, `maxVideosPerPlaylist` and `includeVideoDetails` (dz_omar), `startURLs` (toludare). Field names differ a little:

| Their field | Here |
|---|---|
| `views` | `viewCount` |
| `comments` | `commentsCount` (exact) |
| `description` | `text` |
| `duration` (seconds), `lengthSeconds` | `durationSeconds` |
| `channel.id`, `channel.name`, `channel.handle`, `channel.url`, `channel.subscriberCount` | `channelId`, `channelName`, `channelUsername`, `channelUrl`, `numberOfSubscribers` |
| `channel.badges` | `channelBadges` |
| `isRatingsAllowed` | `allowRatings` |
| `isLive` | `isLiveNow` |
| `publishDate`, `relativeDate` | `date` (exact time) |
| `captions.captionTracks` | `captionTracks` (and `hasCaptions`) |
| `videoPreviews` | `videoPreviews` |
| `isEmbeddable` | `isEmbeddable` |
| `index`, `position` | `position` |
| `relativeUploadTime`, `videoInfo` | `publishedTimeText` (and the exact `viewCount`) |
| `playlistInfo.*`, playlist `title`/`videoCount`/`channel*` | `playlistTitle`, `playlistVideoCount`, `playlistOwnerName`, ... |
| `hiddenVideoCount` | the run summary's `unavailableCount` (with every entry's position and reason) |

Same playlist, same day (Crystal Castles, above; apidojo on a free account, which caps runs at 10 videos):

| | apidojo/youtube-playlist-scraper | This Actor |
|---|---|---|
| Price | $0.50 per 1,000 videos, plus $0.025 per playlist and $0.005 per search (each with about 100 videos free) | $0.29 per 1,000 videos on Starter, nothing per playlist or search |
| Position in the playlist | not in its rows | `position`, YouTube's own numbering |
| The first 16 entries | 10 rows: positions 1, 4, 6, 8, 11-16 (the private 2, 7 and 10 and the videos at 3, 5 and 9 left out, unreported) | 13 rows (positions 1, 3-6, 8, 9, 11-16) and the 3 private entries listed with their reason |
| Playlist title, owner, counts, last update | not in its rows | on every row |
| Likes and views on the same videos | same numbers | same numbers |
| Comment counts | YouTube's rounded figure (1,200 for 1.2K) | exact (1,231) |

Prices are from the Apify Store on 2026-10-16. Not offered here: playback stream URLs (YouTube ties them to the reading IP and expires them within hours). The caption files themselves: the [YouTube Transcripts](https://apify.com/deepmine/youtube-transcripts) Actor downloads them.

### Output

One row per video. A real row from the prefilled run, shortened:

```json
{
  "position": 1,
  "title": "Python Tutorial 2026",
  "type": "video",
  "id": "YZkyL-f-YXY",
  "url": "https://www.youtube.com/watch?v=YZkyL-f-YXY",
  "embedUrl": "https://www.youtube.com/embed/YZkyL-f-YXY",
  "thumbnailUrl": "https://i.ytimg.com/vi/YZkyL-f-YXY/maxresdefault.jpg",
  "viewCount": 588078,
  "likes": 4318,
  "commentsCount": 107,
  "date": "2025-09-23T12:31:03Z",
  "uploadDate": "2025-09-23T12:31:03Z",
  "dateText": "Sep 23, 2025",
  "publishedTimeText": "1 year ago",
  "duration": "00:05:21",
  "durationSeconds": 321,
  "text": "This introductory Python tutorial is perfect for those stepping into the world of programming for the very first time. I...",
  "keywords": ["telusko", "navin", "reddy", "tutorial"],
  "category": "Science & Technology",
  "hasCaptions": true,
  "isAgeRestricted": false,
  "isPrivate": false,
  "isFamilySafe": true,
  "allowRatings": true,
  "channelName": "Telusko",
  "channelUrl": "https://www.youtube.com/channel/UC59K-uG2A5ogwIrHw4bmlEg",
  "channelUsername": "Telusko",
  "numberOfSubscribers": 3030000,
  "isChannelVerified": true,
  "channelBadges": ["Verified"],
  "channelJoinedDate": "Apr 29, 2014",
  "channelLocation": "India",
  "channelTotalVideos": 2184,
  "channelTotalViews": 423968100,
  "playlistId": "PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek",
  "playlistUrl": "https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek",
  "playlistTitle": "Python Tutorial 2026 | Complete Python Course (Beginner to Advanced) | Telusko",
  "playlistType": "Playlist",
  "playlistVisibility": "public",
  "playlistVideoCount": 54,
  "playlistViewCount": 1310698,
  "playlistLastUpdated": "2026-06-12",
  "playlistLastUpdatedText": "Last updated on Jun 12, 2026",
  "playlistOwnerName": "Telusko",
  "playlistOwnerUrl": "https://www.youtube.com/@Telusko",
  "input": "https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek",
  "searchQuery": null,
  "scrapedAt": "2026-10-17T02:20:01Z"
}
```

(Some fields left out here: every row has all of the fields listed above, empty ones as `null`.)

### Did it help?

A short review helps others find it: https://apify.com/deepmine/youtube-playlist-scraper/reviews

# Actor input Schema

## `playlistUrls` (type: `array`):

Playlists to read, one per line: playlist links (youtube.com/playlist?list=...), watch links with \&list=..., music.youtube.com and mobile links, bare playlist IDs (PL..., UU... channel uploads, OLAK5uy\_... albums), podcasts and courses. A channel link (youtube.com/@name) reads that channel's playlists. Mixes and personal lists (Watch later, Liked videos) aren't public and are skipped.

## `searchQueries` (type: `array`):

Words to search YouTube's playlists for (its Playlists filter), one search per line: courses, podcasts and playlists come back in YouTube's order, and each one is read like a link above.

## `maxVideosPerPlaylist` (type: `integer`):

The first N available videos of each playlist, in playlist order. Leave empty for every video. Deleted and private entries don't count.

## `maxPlaylists` (type: `integer`):

How many playlists each search (or channel link) reads, in YouTube's order.

## `includeVideoDetails` (type: `boolean`):

On: every video's exact likes, comments, description, tags, category, exact publish time and channel stats (one read per video). Off: only what the playlist page shows (position, title, duration, approximate views, relative age, channel), much faster for very large playlists. Same price per video.

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

Apify's datacenter proxy works for every call; there's no need to change it.

## `startUrls` (type: `array`):

The same as playlistUrls, for saved inputs from other playlist Actors (startUrls: links or {url} objects).

## `startURLs` (type: `array`):

The same as playlistUrls (startURLs).

## `keywords` (type: `array`):

The same as searchQueries (keywords).

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

At most this many videos in the whole run, in input order (maxItems).

## Actor input object example

```json
{
  "playlistUrls": [
    "https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek"
  ],
  "maxVideosPerPlaylist": 50,
  "maxPlaylists": 10,
  "includeVideoDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "playlistUrls": [
        "https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek"
    ],
    "maxVideosPerPlaylist": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("deepmine/youtube-playlist-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 = {
    "playlistUrls": ["https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek"],
    "maxVideosPerPlaylist": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("deepmine/youtube-playlist-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 '{
  "playlistUrls": [
    "https://www.youtube.com/playlist?list=PLsyeobzWxl7omDoEYrrf3oXvXxa6MPgek"
  ],
  "maxVideosPerPlaylist": 50
}' |
apify call deepmine/youtube-playlist-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deepmine/youtube-playlist-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/azoacxVbaNupAyCHy/builds/QITGvyUvtQmWxgPS6/openapi.json
