# YouTube Scraper — Channels, Videos, Shorts & Search (`tortuga/youtube-scraper`) Actor

Scrape YouTube search results, channels, Shorts, live streams and playlists, plus optional full video details: views, likes, comments, tags, chapters, subscribers.

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

## Pricing

from $1.00 / 1,000 list 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?

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 Scraper — Channels, Videos, Shorts & Search

Scrape YouTube without an API key or login: **search results** with YouTube's own filters, **channels** (subscribers, total views, joined date, country, links, avatar, banner) with all their **videos, Shorts and live streams**, **playlists**, and **full video details** (likes, comment count, exact publish time, duration, tags, category, description, chapters). Export to JSON, CSV or Excel, or pull it through the API into Google Sheets, Make, Zapier, n8n or your own code.

- Search by keyword with the filters from YouTube's filter menu: type (videos, Shorts, channels, playlists, movies), upload date, duration, features (Live, 4K, HD, Subtitles/CC, Creative Commons, 360°, VR180, 3D, HDR, Location) and popularity.
- Paste any channel URL (`/@handle`, `/channel/UC...`, `/c/name`) or a bare `@handle`: one channel row plus the Videos, Shorts and/or Live tab, newest, most popular or oldest first, through every page.
- Paste playlist URLs for all their videos, or video and Shorts URLs for full details.
- Pay only for what you get: cheap list rows, and a separate price for videos with full details.
- **Monitoring mode**: schedule it and get only new videos, plus channels whose subscriber or video count changed.

### What data does YouTube Scraper extract?

Every row has `type`: `video`, `short`, `channel` or `playlist`, and `id` (video, channel or playlist id).

**Video and Shorts rows** (from searches, channel tabs and playlists):

| Field | Description |
|---|---|
| `videoId`, `url`, `title` | Video id, watch or Shorts URL, title |
| `viewCount`, `viewCountText` | Views (YouTube rounds list views, e.g. "1.2M views") |
| `publishedTimeText`, `publishedAt` | "7 days ago" as shown, and the approximate date it means (exact with details) |
| `duration`, `durationSeconds` | "12:07" and 727 |
| `thumbnailUrl` | Largest thumbnail |
| `isShort`, `isLive`, `isUpcoming`, `scheduledStartTime` | Shorts / live now / scheduled stream |
| `channelName`, `channelId`, `channelHandle`, `channelUrl`, `channelIsVerified` | Uploader |
| `descriptionSnippet`, `badges` | Search only: snippet and badges ("4K", "CC", "New") |
| `sourceType`, `sourceInput`, `position`, `searchQuery`, `channelTab`, `playlistId`, `playlistTitle` | Where the row came from and its rank |

**Extra fields with full video details** (video URLs, or any list with **Full video details** on):

| Field | Description |
|---|---|
| `viewCount` | Exact view count |
| `likeCount`, `commentCount`, `commentCountText` | Exact likes; comment count as YouTube shows it ("5.7K") |
| `publishDate`, `uploadDate`, `publishedAt` | Exact publish time (`2025-10-01T13:41:40-07:00`) |
| `description`, `tags`, `hashtags`, `category` | Full description, the uploader's keyword tags, hashtags, category |
| `chapters` | `[{title, startSeconds, start}]` |
| `isLiveContent`, `liveStartTime`, `liveEndTime` | Was or is a live stream, with its times |
| `isFamilySafe`, `isUnlisted` | Flags |
| `channelSubscriberCount`, `channelAvatarUrl`, `collaborators` | Channel subscribers; co-creators on collaboration videos |

**Channel rows** (one per channel URL; channels found by search have the first block):

| Field | Description |
|---|---|
| `channelId`, `title`, `handle`, `channelUrl`, `avatarUrl`, `isVerified` | Identity |
| `subscriberCount`, `subscriberCountText`, `videoCount` | Subscribers (rounded by YouTube) and number of videos |
| `viewCount`, `joinedDate`, `country` | Total channel views, joined date, country (when the channel sets one) |
| `description`, `links`, `keywords`, `bannerUrl` | About text, the links listed on the About panel `[{title, url}]`, channel tags, banner |
| `hasBusinessEmail` | The channel has a business email behind YouTube's sign-in + CAPTCHA (the address itself is not collected) |

**Playlist rows** (search): `playlistId`, `url`, `title`, `playlistType` (Playlist, Course, Mix, Podcast), `videoCount`, `firstVideoId`, `thumbnailUrl`, channel fields.

### How to scrape YouTube search results

Add terms to **Search terms**, pick filters (type, upload date, duration, features, prioritize) and set **Max results per search**. You can also paste a search URL from your browser (`https://www.youtube.com/results?search_query=...&sp=...`): the filters in the URL are kept.

```json
{
  "searchQueries": ["iphone 17 review"],
  "searchType": "video",
  "uploadDate": "month",
  "features": ["4k"],
  "maxResultsPerSource": 100
}
```

YouTube removed "Last hour" and "Sort by upload date" from search in 2025; for recent videos use **Upload date: Today / This week**, or monitoring mode.

### How to scrape a YouTube channel's videos, Shorts and live streams

Paste channel URLs or `@handles` into **YouTube URLs**, choose **Channel tabs** (Videos, Shorts, Live) and **Channel sort** (newest, popular, oldest). Each channel gives one channel row and then its videos, page after page, until **Max results per ... channel tab** (set 10000 for a whole channel). A URL ending in `/shorts` or `/streams` scrapes only that tab. Set the limit to 0 to get channel rows only.

```json
{
  "urls": ["https://www.youtube.com/@mkbhd", "https://www.youtube.com/@NASA/streams"],
  "channelTabs": ["videos", "shorts"],
  "channelSort": "popular",
  "maxResultsPerSource": 200
}
```

### How to get YouTube video details (likes, comments, tags, chapters)

Video and Shorts URLs always come back with full details. For searches, channels and playlists, turn on **Full video details**; each video then costs the video-details price instead of the list price.

Output example (shortened):

```json
{
  "type": "video",
  "id": "rng_yUSwrgU",
  "url": "https://www.youtube.com/watch?v=rng_yUSwrgU",
  "title": "iPhone 17 Review: No Asterisks!",
  "viewCount": 6934137,
  "likeCount": 174796,
  "commentCount": 5700,
  "publishDate": "2025-10-01T13:41:40-07:00",
  "durationSeconds": 680,
  "category": "Science & Technology",
  "tags": ["iPhone 17", "iPhone 17 review", "iPhone 17 vs", "MKBHD"],
  "chapters": [{"title": "The customer's dilemma", "startSeconds": 0, "start": "0:00"},
               {"title": "Recommendation and value", "startSeconds": 69, "start": "1:09"}],
  "isShort": false,
  "channelName": "Marques Brownlee",
  "channelHandle": "@mkbhd",
  "channelSubscriberCount": 21400000,
  "sourceType": "search",
  "searchQuery": "iphone 17 review",
  "position": 1
}
```

Channel row example (shortened):

```json
{
  "type": "channel",
  "channelId": "UCHnyfMqiRRG1u-2MsSQLbXA",
  "title": "Veritasium",
  "handle": "@veritasium",
  "subscriberCount": 21300000,
  "videoCount": 539,
  "viewCount": 4652811692,
  "joinedDate": "2010-07-21",
  "country": "United States",
  "links": [{"title": "Patreon", "url": "https://patreon.com/veritasium"}],
  "isVerified": true
}
```

### Monitor changes on a schedule

Turn on **Only new or changed items** and save the input as a task with a Schedule (Apify Console → Save as task → Schedules). Each run remembers what earlier runs returned and outputs only new videos, Shorts and playlists, plus channel rows whose subscriber or video count changed; unchanged items are not output and not charged, and channel tabs stop paging at the first page that holds a video it has already seen. New rows carry `changeType: "new"`; changed channel rows carry `changeType: "changed"` and `previousValues` with the old counts. Use **Monitoring key** to keep several schedules on the same input apart.

### How much does it cost to scrape YouTube?

Pay per result, no subscription:

- **List result: $0.001** — a video, Short, playlist or channel row from a search, channel tab or playlist (channel rows include the About panel).
- **Video with full details: $0.003** — replaces the list price for that video.

Platform usage is billed by Apify separately and is small: about $0.01 per 1,000 list rows on the default datacenter proxy, and about $0.10 per 1,000 detailed videos (mostly the residential proxy traffic described below).

### Does it work without login or an API key?

Yes. It reads the same public data the YouTube website shows to a signed-out visitor, with no login, cookies or YouTube Data API quota. **Proxy note:** search, channel, playlist and video pages go through Apify Proxy (datacenter). For full video details, one small request (about 10 KB) per video goes through Apify RESIDENTIAL proxy, because YouTube asks datacenter IPs to sign in before it shows duration, tags and category; that is roughly $0.0001 of proxy traffic per video on your Apify account. If residential proxy is not available on your plan, the other details are still delivered.

### Limits

- No video or audio download, no comments text (see YouTube Comments Scraper), no transcripts (see YouTube Transcript Scraper).
- Members-only, private and age-restricted videos need a login and are skipped.
- List view counts and subscriber counts are rounded by YouTube ("1.2M"); detailed rows have exact views and likes. Comment counts are as YouTube shows them ("2.4M").
- Business emails behind "Sign in to see email address" are not collected.

### Is it legal to scrape YouTube?

This Actor collects only publicly visible data: video and channel metadata, public channel names and handles, and the links channels publish on their About panel. It does not log in, solve CAPTCHAs or collect hidden contact data. You are responsible for how you use the data and for complying with YouTube's terms and the laws that apply to you (for example GDPR when you process data about people).

### Integrations and API

Run it from the Apify API, the Python or JavaScript client, or connect it to Make, Zapier, n8n, Google Sheets and webhooks. The **API** tab has ready-made snippets.

### Support

Found a bug or need a field? Open an issue in the **Issues** tab; it is usually answered within a day.

# Actor input Schema

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

Keywords to search on YouTube, one per line. Each term returns up to 'Max results per search / channel tab / playlist' results (videos, Shorts, channels and playlists, or only the type you pick below).

## `searchType` (type: `string`):

Only return this kind of result (YouTube's 'Type' filter). 'All' keeps YouTube's mixed results page, including its Shorts shelves.

## `uploadDate` (type: `string`):

YouTube's 'Upload date' filter. YouTube removed 'Last hour' and sorting by upload date in 2025; use 'Today' or 'This week' to find recent videos.

## `duration` (type: `string`):

YouTube's 'Duration' filter.

## `features` (type: `array`):

YouTube's 'Features' filters; a result must have all selected features.

## `sortBy` (type: `string`):

YouTube's 'Prioritize' option: relevance (default) or popularity (view count).

## `urls` (type: `array`):

One per line: channel URLs (`/@handle`, `/channel/UC...`, `/c/name`, optionally ending in `/videos`, `/shorts` or `/streams`) or bare @handles, playlist URLs, video or Shorts URLs (always returned with full details), and search result URLs (`/results?search_query=...&sp=...`, filters kept).

## `channelTabs` (type: `array`):

Which lists to scrape for each channel URL: Videos, Shorts and/or Live (past, current and upcoming streams). A channel URL ending in `/shorts`, `/videos` or `/streams` uses only that tab. Leave empty to get only the channel row.

## `channelSort` (type: `string`):

Order of the channel tab lists: newest first (default), most popular, or oldest first.

## `includeChannelInfo` (type: `boolean`):

Save one row per channel URL with subscribers, video count, total views, joined date, country, description, links from the About panel, avatar, banner and verified badge. Charged as one result.

## `maxResultsPerSource` (type: `integer`):

Stop each search term, each channel tab and each playlist after this many results. Set a high number (e.g. 10000) to get a whole channel or playlist. 0 = channel rows only.

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

Also open every video and Short from searches, channels and playlists for likes, comment count, exact views, publish date and time, duration, tags, category, description, chapters and channel subscribers. Charged as a 'video details' event instead of a list result. Video URLs always get details.

## `includeChannelDetails` (type: `boolean`):

For channels in search results, also read the About panel (total views, joined date, country, links). No extra charge; channel URLs always get it.

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

Stop the whole run after this many saved rows. Every saved row is charged, so this caps the cost.

## `onlyNewOrChanged` (type: `boolean`):

Remember what earlier runs of this input returned and output only new videos, Shorts and playlists, plus channel rows whose subscriber or video count changed. Use with a Schedule; unchanged items are not charged.

## `monitoringKey` (type: `string`):

Name for this watch, so several schedules on the same input stay separate; defaults to a key derived from the input

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

Apify Proxy (datacenter) works for search, channel, playlist and video pages and is the cheapest option. Full video details also make one small request (about 10 KB) per video through Apify RESIDENTIAL proxy, because YouTube asks datacenter IPs to sign in for duration, tags and category: about $0.0001 of proxy traffic per video.

## Actor input object example

```json
{
  "searchQueries": [
    "web scraping tutorial"
  ],
  "searchType": "all",
  "uploadDate": "any",
  "duration": "any",
  "sortBy": "relevance",
  "urls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "channelTabs": [
    "videos"
  ],
  "channelSort": "newest",
  "includeChannelInfo": true,
  "maxResultsPerSource": 50,
  "includeVideoDetails": false,
  "includeChannelDetails": false,
  "maxItems": 1000,
  "onlyNewOrChanged": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "searchQueries": [
        "web scraping tutorial"
    ],
    "urls": [
        "https://www.youtube.com/@mkbhd"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("tortuga/youtube-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 = {
    "searchQueries": ["web scraping tutorial"],
    "urls": ["https://www.youtube.com/@mkbhd"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("tortuga/youtube-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 '{
  "searchQueries": [
    "web scraping tutorial"
  ],
  "urls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call tortuga/youtube-scraper --silent --output-dataset

```

## MCP server setup

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