# YouTube Playlist Scraper — Videos, Views & Channel Playlists (`fabrikit/youtube-playlist-scraper`) Actor

Scrape any public YouTube playlist, or every playlist of a channel: playlist title, owner, video count, views and last update, plus each video with its position, title, channel, views, date, duration and thumbnail. No API key, no login.

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

## Pricing

from $0.65 / 1,000 playlist video scrapeds

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, Views & Channel Playlists

**YouTube Playlist Scraper extracts every video of a public YouTube playlist from youtube.com** — position in the list, title, channel, views, publish date, length and thumbnail — together with the playlist's own facts: title, owner, declared number of videos, total views and last update. Point it at a channel instead and it walks the channel's Playlists tab and scrapes each list it finds, or just inventories the playlists. Plain HTTP, no YouTube API key, no Google account, no browser; you pay per row saved.

This Actor is the playlist edition of our [YouTube Videos Scraper](https://apify.com/fabrikit/youtube-videos-scraper), which covers search results, channel tabs and single videos with optional per-video details. Use this one when the **playlist is the thing you care about**: its order, its size, how it grows, and which channels fill it. The same family also has a [YouTube Channel Scraper](https://apify.com/fabrikit/youtube-channel-scraper), a [YouTube Search Scraper](https://apify.com/fabrikit/youtube-search-scraper) and a [YouTube Shorts Scraper](https://apify.com/fabrikit/youtube-shorts-scraper).

### What you get

One flat row per playlist video. The playlist's metadata is repeated on every row, so a CSV export stands on its own.

| Field | Example | Notes |
|---|---|---|
| `type` | `video` | `playlist` when `playlistsOnly` is on |
| `position` | `1` | the video's number in the playlist, as YouTube counts it (kept across pages) |
| `playlistId`, `playlistUrl`, `playlistTitle` | `PL2aBZ…`, `https://www.youtube.com/playlist?list=PL2aBZ…`, `Artemis III` | |
| `playlistDescription` | `null` | e-mail addresses and phone numbers are hidden |
| `playlistChannelName`, `playlistChannelId`, `playlistChannelUrl` | `NASA`, `UCLA_DiR1FfKNvjuUpBHmylQ`, `https://www.youtube.com/@NASA` | the playlist's owner |
| `playlistVideoCount`, `playlistViewCount` | `9`, `33385` | what the playlist page declares |
| `playlistLastUpdated` | `2026-09-03` | from "Last updated on …" or "Updated 3 days ago" |
| `unavailableVideosHidden` | `false` | `true` when YouTube hides private or deleted entries from the list |
| `videoId`, `url`, `title` | `yMoGiIeDH9s`, `https://www.youtube.com/watch?v=yMoGiIeDH9s`, `Andre Douglas Artemis III …` | |
| `channelName`, `channelId`, `channelHandle`, `channelUrl` | `NASA Video`, `UC_aP7p…`, `@NASAgovVideo` | the video's channel (often not the playlist owner) |
| `viewCount`, `viewCountText` | `51000`, `51K views` | rounded exactly as YouTube shows it on the list |
| `publishedText`, `publishedAt` | `4 months ago`, `2026-06-22T00:00:00.000Z` | the relative text and an approximate ISO date derived from it |
| `duration`, `durationSeconds` | `5:33`, `333` | `null` for live and upcoming streams |
| `isLive`, `isUpcoming`, `badges` | `false`, `false`, `[]` | upcoming premieres carry `["Upcoming"]` and a "Scheduled for …" text |
| `thumbnailUrl` | `https://i.ytimg.com/vi/yMoGiIeDH9s/hqdefault.jpg…` | |
| `sourceInput`, `scrapedAt` | the playlist or channel you entered, ISO time | |

With `playlistsOnly` each row describes one playlist (`title`, `url`, `channel*`, `viewCount` and `thumbnailUrl` refer to the playlist) and the video fields stay `null`. A `SUMMARY` record in the key-value store lists, per playlist, the declared video count, videos listed, rows saved, pages read and any error, plus the channels read and the proxy used.

### Use cases

- **Course and curriculum exports** — turn a tutorial series or lecture playlist into a spreadsheet with order, lengths and links.
- **Catalogue and archive work** — read a channel's uploads list (`UU` + channel id) or all of its playlists in one run, 100 videos per request.
- **Playlist monitoring** — schedule a daily run and diff positions, additions, removals and `unavailableVideosHidden` for curated or promotional lists.
- **Music and media research** — chart lists, label playlists and album lists (`OLAK5uy_…`) with per-track views and release age.
- **Datasets for analytics and AI** — titles, channels, views and durations as JSON, CSV or Excel for dashboards, recommendation experiments or LLM pipelines.

### How to use it

1. Paste one or more **playlist URLs or IDs** (a `/playlist?list=…` page, a video link carrying `&list=…`, or the bare id).
2. Optionally add **channels** (handle, URL or `UC…` id) to scrape all of their public playlists, and pick the **channel playlists order**.
3. Set **max rows**, and if you like **max videos per playlist** and **max playlists per channel**.
4. Run it and download the dataset as JSON, CSV, Excel or HTML, or read it through the API.

#### Input example: one playlist, every video

```json
{
  "playlistUrls": ["https://www.youtube.com/playlist?list=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ"],
  "maxItems": 1000
}
```

#### Input example: the newest 10 playlists of a channel, 50 videos each

```json
{
  "channelUrls": ["@NASA"],
  "maxPlaylistsPerChannel": 10,
  "maxVideosPerPlaylist": 50,
  "channelPlaylistsSort": "newest",
  "maxItems": 500
}
```

#### Input example: inventory of a channel's playlists, no videos

```json
{
  "channelUrls": ["https://www.youtube.com/@NASA/playlists"],
  "playlistsOnly": true,
  "channelPlaylistsSort": "lastVideoAdded",
  "maxItems": 200
}
```

### Output example

A real row from a local test run on 20 October 2026:

```json
{
  "type": "video",
  "position": 1,
  "playlistId": "PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ",
  "playlistUrl": "https://www.youtube.com/playlist?list=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ",
  "playlistTitle": "Artemis III",
  "playlistDescription": null,
  "playlistChannelName": "NASA",
  "playlistChannelId": "UCLA_DiR1FfKNvjuUpBHmylQ",
  "playlistChannelUrl": "https://www.youtube.com/@NASA",
  "playlistVideoCount": 9,
  "playlistViewCount": 33385,
  "playlistLastUpdated": "2026-09-03",
  "unavailableVideosHidden": false,
  "videoId": "yMoGiIeDH9s",
  "url": "https://www.youtube.com/watch?v=yMoGiIeDH9s",
  "title": "Andre Douglas Artemis III Crew Announcement Resource Reel",
  "channelName": "NASA Video",
  "channelId": "UC_aP7p621ATY_yAa8jMqUVA",
  "channelHandle": "@NASAgovVideo",
  "channelUrl": "https://www.youtube.com/@NASAgovVideo",
  "viewCount": 51000,
  "viewCountText": "51K views",
  "publishedText": "4 months ago",
  "publishedAt": "2026-06-22T00:00:00.000Z",
  "duration": "5:33",
  "durationSeconds": 333,
  "isLive": false,
  "isUpcoming": false,
  "badges": [],
  "thumbnailUrl": "https://i.ytimg.com/vi/yMoGiIeDH9s/hqdefault.jpg?sqp=…",
  "sourceInput": "https://www.youtube.com/playlist?list=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ",
  "scrapedAt": "2026-10-20T03:29:28.149Z"
}
```

### Pricing

Pay per event: one event per video saved, proxy and compute included. Playlist rows in `playlistsOnly` mode are charged per row as well. See the Pricing tab for current prices; paid Apify plans get lower rates.

Rows are charged only when saved. If you set a maximum cost per run, the Actor stops cleanly when it is reached.

### How it compares

- **YouTube Data API v3** — official and free up to a quota, but it needs a Google Cloud project and key, `playlistItems.list` returns 50 items per call without view counts or durations (those need a second `videos.list` call per 50 ids), and private or deleted entries come back as blanks you have to filter. This Actor returns the public playlist page as a visitor sees it, 100 videos per request, with views and lengths already on each row.
- **Other playlist scrapers in the Store** — this one adds the playlist's own metadata to every row, reads whole channels and keeps YouTube's position numbers.
- **Limits** — `publishedAt` is derived from the relative text YouTube shows ("4 months ago"), so it is approximate; view counts are rounded the way the list shows them (51K). Logged-out YouTube hides private and deleted videos, so a 192-video list may yield fewer rows (the `unavailableVideosHidden` flag tells you). Mixes (`RD…`) and personal lists (`WL`, `LL`) cannot be read. For exact dates, likes, tags and descriptions per video, feed the ids to our [YouTube Videos Scraper](https://apify.com/fabrikit/youtube-videos-scraper).

### Integrations

Run it from the Apify API or the JavaScript and Python clients, schedule it in Apify Console, and send rows to Google Sheets, Airtable, n8n, Make, Zapier or a webhook. AI agents (Claude, ChatGPT, Cursor and other MCP clients) can call it through the Apify MCP server, e.g. "list the videos of this YouTube playlist with their lengths".

### FAQ

**Is it legal to scrape YouTube playlists?** The Actor reads only public playlist pages exactly as a logged-out visitor sees them. Playlist titles, owner names and handles can be personal data when a list belongs to a private person, so laws such as GDPR, CCPA or Chile's Ley 21.719 may apply to how you store and use them; contact details are hidden in descriptions. This is not legal advice.

**Do I need a proxy?** No setup needed. The Apify datacenter proxy is used by default; if YouTube blocks it twice in a row, the run switches to Apify residential proxy (country = `region`) for the rest of the run.

**Why are there fewer rows than `playlistVideoCount`?** YouTube hides private and deleted videos from visitors; the header still counts them. The flag `unavailableVideosHidden` is `true` in that case.

**Can I scrape a channel's uploads?** Yes: use the uploads list id, which is the channel id with `UC` replaced by `UU` (for example `UULA_DiR1FfKNvjuUpBHmylQ` for @NASA), and cap it with `maxVideosPerPlaylist`.

**Does it return likes, comments or full descriptions of the videos?** No. Playlist pages carry title, channel, rounded views, relative date and length only; use the YouTube Videos Scraper with details for the rest.

**Is the order the same as on youtube.com?** Yes, `position` follows the playlist's own order (the one the owner set, or newest first for uploads lists).

### Changelog

- **2026-10-20** — First version: playlist URLs or ids, channel Playlists tab (two sort orders), playlists-only inventory, per-playlist and per-channel caps, YouTube position numbering across continuation pages, hidden-videos flag, pay per row.

### En español

**YouTube Playlist Scraper** extrae todos los videos de una lista de reproducción pública de YouTube, sin API key ni login: posición en la lista, título, canal, vistas, fecha aproximada, duración y miniatura, más los datos de la propia lista (título, dueño, número de videos declarado, vistas totales y última actualización) repetidos en cada fila. Acepta URLs o IDs de listas y también canales (lee su pestaña "Listas de reproducción" con los dos órdenes que ofrece YouTube) y puede entregar solo el inventario de listas con `playlistsOnly`. Cobra por cada fila guardada, con proxy incluido. Consulta la pestaña Pricing para ver los precios vigentes; los planes de pago de Apify tienen tarifas más bajas. Para detalles por video (likes, fecha exacta, etiquetas, descripción) usa nuestro [YouTube Videos Scraper](https://apify.com/fabrikit/youtube-videos-scraper).

# Actor input Schema

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

One playlist per entry: a playlist page (https://www.youtube.com/playlist?list=PL...), a video link that carries \&list=..., or the bare list id (PL..., UU... for a channel's uploads, OLAK5uy\_... for albums). Mixes (RD...) and private lists (WL, LL) cannot be read.

## `channelUrls` (type: `array`):

Channels whose Playlists tab you want to read: a handle (@NASA), a channel URL (https://www.youtube.com/@NASA/playlists, /channel/UC..., /c/..., /user/...) or a channel id (UC...). Every playlist found is then scraped like a pasted playlist.

## `playlistsOnly` (type: `boolean`):

On: one row per playlist with its title, owner, video count, views, last update, description and cover, and no video rows. Useful to inventory a channel's playlists. Off (default): one row per video, each row carrying the playlist's metadata too.

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

Stop after this many rows across all playlists and channels. You pay only for the rows that are saved.

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

Optional cap for each playlist, so one huge list (a channel's uploads, for example) does not use the whole budget. Playlists are read 100 videos per request. Leave empty for no per-playlist cap.

## `maxPlaylistsPerChannel` (type: `integer`):

How many playlists to take from each channel in "Channels" (30 are listed per request).

## `channelPlaylistsSort` (type: `string`):

Order of a channel's Playlists tab, the same two options YouTube offers: by date the playlist was created (newest first) or by the date a video was last added.

## `language` (type: `string`):

YouTube interface language sent with the requests (en, es, pt, de...). Keep "en": view counts, relative dates and the "last updated" text are parsed from English.

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

Two-letter country code sent with the requests (US, GB, DE, MX, BR, IN...). Playlist contents do not change by region; it also sets the country of the residential fallback proxy.

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

Apify datacenter proxy is enough in normal runs. If YouTube blocks it twice in a row (HTTP 403/429 or a consent page), the Actor switches to RESIDENTIAL proxy by itself for the rest of the run.

## Actor input object example

```json
{
  "playlistUrls": [
    "https://www.youtube.com/playlist?list=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ"
  ],
  "playlistsOnly": false,
  "maxItems": 50,
  "maxPlaylistsPerChannel": 50,
  "channelPlaylistsSort": "newest",
  "language": "en",
  "region": "US",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `rows` (type: `string`):

Every video (or playlist) saved during the run, with the playlist's metadata on each row

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

Per-playlist counts (declared videos, listed, saved, pages), channels read, proxy used and any errors

# 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=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ"
    ],
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fabrikit/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=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ"],
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("fabrikit/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=PL2aBZuCeDwlRaF6AIey7ulmW1X0SHI1mQ"
  ],
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call fabrikit/youtube-playlist-scraper --silent --output-dataset

```

## MCP server setup

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