# Free YouTube Scraper: Videos, Shorts, Channels & Search (`wulfcare/youtube-scraper`) Actor

Free YouTube scraper: videos, Shorts and streams from searches (all filters), channels, playlists and hashtags. Views, likes, comments count, exact publish time, duration, description, tags, plus channel subscribers, total views and country. Same input and output as the popular YouTube Scraper.

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

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

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

## Free YouTube Scraper

Scrape YouTube **for free**: there's no charge per video, so a run costs only the few cents of Apify platform usage. You can get:

- **Search results** for any search term, with all of YouTube's **search filters** (upload date, type, duration, sort order, HD, 4K, subtitles, Creative Commons, live...).
- **Channels**: their **Videos**, **Shorts** and **Live** tabs, sorted newest, popular or oldest, and stopping at a date if you set one.
- **Playlists**, **hashtag pages** and **search result URLs**.
- **Single videos and Shorts** from any link (watch, youtu.be, Shorts, live, embed) or a bare video id.

Every video comes with **views, likes, comment count, exact publish time, duration, the full description with its links, hashtags, category, tags**, and the **channel's subscribers, total views, video count, country, joined date, links, avatar and banner**.

**Already using another YouTube scraper?** The inputs (`searchQueries`, `startUrls`, `maxResults`, `maxResultsShorts`, `maxResultStreams`, `sortingOrder`, `dateFilter`, `oldestPostDate`, `sortVideosBy`...) and the output fields (`title`, `id`, `url`, `viewCount`, `likes`, `commentsCount`, `date`, `duration`, `text`, `channelName`, `numberOfSubscribers`...) have the same names as the most used YouTube scraper in the Apify Store. Swap the Actor ID and your integration keeps working.

### What people use it for

- **Market and competitor research**: what's being published on a topic, by whom, and how well it does.
- **Influencer discovery**: channels in a niche with their subscribers, views, country and links (no emails or personal data).
- **Channel monitoring**: a daily schedule with `oldestPostDate: "1 day"` gives each channel's new uploads with views, likes and comments.
- **Trend and content analysis**: titles, tags, hashtags, categories and engagement of the top videos for any search.
- **Datasets for AI and analytics**: clean, structured video metadata ready for a spreadsheet, a database or an LLM.

Need **what's said in the videos**? The [YouTube Transcript Scraper](https://apify.com/wulfcare/youtube-transcript-scraper) gets transcripts (text, timed segments or SRT) for the same videos, channels, playlists and searches.

### How to use it

1. Add **search terms** and/or **YouTube URLs** (videos, channels, playlists, hashtags, search pages), in any mix.
2. Set **Maximum videos** per search term / channel / playlist (default 10). For Shorts and live streams set **Maximum Shorts** and **Maximum live streams** too (default 0 = none).
3. Optionally add **search filters**, a **date limit** (`oldestPostDate`) or the **channel order**.
4. Run it, then download JSON, CSV or Excel, or use the API.

### What you get: one row per video

```json
{
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "id": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "type": "video",
  "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
  "viewCount": 1821960823,
  "likes": 19440936,
  "commentsCount": 2457966,
  "date": "2009-10-25T06:57:33Z",
  "dateText": "Oct 24, 2009",
  "duration": "00:03:33",
  "durationSeconds": 213,
  "text": "The official video for “Never Gonna Give You Up” by Rick Astley. ...",
  "descriptionLinks": [{ "url": "https://linktr.ee/rickastleynever", "text": "https://linktr.ee/rickastleynever" }],
  "hashtags": ["#RickAstley", "#NeverGonnaGiveYouUp", "#OfficialMusicVideo"],
  "location": null,
  "category": "Music",
  "keywords": ["rick astley", "Never Gonna Give You Up", "nggyu", "rick roll"],
  "isLive": false,
  "commentsTurnedOff": false,
  "isMembersOnly": false,
  "isAgeRestricted": false,
  "isUnlisted": false,
  "collaborators": null,
  "channelName": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "channelUrl": "https://www.youtube.com/@RickAstleyYT",
  "channelUsername": "RickAstleyYT",
  "numberOfSubscribers": 4550000,
  "isChannelVerified": true,
  "channelAvatarUrl": "https://yt3.googleusercontent.com/...",
  "channelBannerUrl": "https://yt3.googleusercontent.com/...",
  "channelDescription": "Rick Astley’s Swinging Christmas 2026 🎄 ...",
  "channelDescriptionLinks": [{ "text": "Website", "url": "https://www.rickastley.co.uk/" }],
  "channelLocation": "United Kingdom",
  "channelJoinedDate": "2015-02-01",
  "channelTotalVideos": 437,
  "channelTotalViews": 2570271276,
  "input": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "fromYTUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "order": 1,
  "error": null
}
```

- `type` is `video`, `shorts` or `stream` (live, past or upcoming streams and premieres).
- `date` is the exact publish time in UTC. `dateText` is the date as YouTube shows it ("3 days ago" with *Open every video* off).
- `text` is the **full description as the creator wrote it**: YouTube's shortened links and video "chips" are turned back into the real URLs, which are also listed in `descriptionLinks`.
- `hashtags` are the ones above the title and in the description. `keywords` are the creator's tags (hidden on YouTube's page).
- `commentsCount` is the exact count. It's `null` when comments are turned off (`commentsTurnedOff: true`) and for streams that are live now.
- `collaborators` lists every channel of a video made by several channels together, each with name, id, URL, handle, subscribers and verified badge. The `channel...` fields describe the first one, the channel the video is on.
- `numberOfSubscribers` is rounded the way YouTube shows it (4.55M = 4,550,000). `channelTotalViews` and `channelTotalVideos` are exact.
- `order` is the video's position in its search, channel tab or playlist. `input` is what you entered and `fromYTUrl` is the YouTube page the video was listed on.

Small differences from other scrapers: dates are ISO (`2015-02-01`), `channelTotalViews` is a number, and `thumbnailUrl` is the 480x360 thumbnail every video has. Subtitles, translations, `isMonetized` and `isPaidContent` aren't part of this Actor (the fields are there, always `null`). For subtitles, use the [YouTube Transcript Scraper](https://apify.com/wulfcare/youtube-transcript-scraper).

#### When something goes wrong

A bad URL, a deleted or private video, or a missing channel **never crashes the run**. It gets a row with `error` set, and everything else carries on:

```json
{ "id": "aaaaaaaaaaa", "url": "https://www.youtube.com/watch?v=aaaaaaaaaaa", "input": "https://www.youtube.com/watch?v=aaaaaaaaaaa", "error": "Video unavailable" }
```

Other errors: `Channel not found`, `The playlist does not exist.`, `This channel has no Shorts tab` (for a channel tab URL), `No videos found for this search`, `Not a YouTube video, channel, playlist, search or hashtag URL`.

### Pricing

**Free.** There's no fee per video or per run. You only pay Apify's platform usage, which is tiny. In tests:

- 341 videos with every detail used **$0.002**;
- 4 channels' videos, Shorts and streams (170 videos) used **$0.0016**.

Apify's free plan includes $5 of usage every month, enough for hundreds of thousands of videos.

### Input examples

**The top 50 videos for a search, this month's uploads over 20 minutes, most viewed first**

```json
{ "searchQueries": ["python tutorial"], "maxResults": 50, "dateFilter": "month", "lengthFilter": "plus20", "sortingOrder": "views" }
```

**Search videos and Shorts**

```json
{ "searchQueries": ["iphone 17 review", "pixel 10 review"], "maxResults": 30, "maxResultsShorts": 30 }
```

**Everything a channel posted in the last 30 days (videos, Shorts and streams)**

```json
{ "startUrls": [{ "url": "https://www.youtube.com/@mkbhd" }], "maxResults": 1000, "maxResultsShorts": 1000, "maxResultStreams": 1000, "oldestPostDate": "30 days" }
```

**A channel's 100 most popular videos**

```json
{ "startUrls": [{ "url": "https://www.youtube.com/@veritasium" }], "maxResults": 100, "sortVideosBy": "POPULAR" }
```

**A whole playlist, quickly (only what the list shows: title, views, duration)**

```json
{ "startUrls": [{ "url": "https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi" }], "maxResults": 5000, "includeVideoDetails": false }
```

**Details of specific videos**

```json
{ "startUrls": [{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, { "url": "https://www.youtube.com/shorts/CEJXqm2eiJ0" }] }
```

### Good to know

- **Speed**: about **6 videos a second** with every detail (341 videos in under a minute), and much faster with *Open every video* off. *Videos at a time* (default 10, up to 30) sets the pace.
- **Proxy**: Apify Proxy (datacenter) is on by default and is all it needs. YouTube refuses some Apify servers' own IP addresses, so without a proxy the run may stop with a clear message.
- **Search filters** work like YouTube's own: YouTube decides what matches, and it doesn't keep "Upload date" sort order strictly. A search goes about 500-600 results deep, as on YouTube.
- **`oldestPostDate`** takes a date (`2026-01-31`) or an age (`7 days`, `2 weeks`, `3 months`, `1 year`). It works for every source. On a channel listed newest first the run stops at the first older video, and elsewhere older videos are skipped.
- **Hashtag pages** show only a few dozen videos, so for more the Actor continues with YouTube's search for that hashtag.
- **Live search** (`maxResultStreams` with a search term) finds streams that are live right now. A channel's Live tab has its past and upcoming streams too.
- For a few videos (mostly some music videos) YouTube doesn't give out the **category and tags**; those rows have the rest.
- **Public data only**: everything comes from public YouTube pages as a logged-out visitor sees them. No Google account, no login, no emails or other personal contact data.

### Other Actors by Wulfcare Data

- [Google Hotels Prices Scraper](https://apify.com/wulfcare/google-hotels-scraper): every booking site's price for any hotel and dates, and hotel searches
- [YouTube Transcript Scraper](https://apify.com/wulfcare/youtube-transcript-scraper): transcripts of videos, channels, playlists and searches as text, segments or SRT
- [Google Trends Scraper](https://apify.com/wulfcare/google-trends-scraper): interest over time, rising queries and Trending Now
- [ChatGPT Search Scraper & AI Brand Visibility Tracker](https://apify.com/wulfcare/chatgpt-search-scraper): what ChatGPT answers and cites for any prompt
- [Google Play Scraper](https://apify.com/wulfcare/google-play-scraper) and [App Store Scraper](https://apify.com/wulfcare/app-store-scraper): reviews, app details and charts
- [Google Jobs Scraper](https://apify.com/wulfcare/google-jobs-scraper) and [AI Training Jobs Scraper](https://apify.com/wulfcare/ai-training-jobs-scraper): job listings

# Actor input Schema

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

What you would type in YouTube's search box. Each term is searched separately and gets up to "Maximum videos" results (and Shorts / live streams if you ask for them below).

## `maxResults` (type: `integer`):

Regular videos to get from each search term, channel (its Videos tab), playlist and hashtag. 0 = none. Video URLs in Direct URLs are always scraped.

## `maxResultsShorts` (type: `integer`):

Shorts to get from each search term (YouTube's "Shorts" search filter) and each channel (its Shorts tab). 0 = none.

## `maxResultStreams` (type: `integer`):

Live streams to get from each channel (its Live tab: past, current and upcoming streams) and each search term (YouTube's "Live" filter: streams live right now). 0 = none.

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

YouTube URLs: videos, Shorts, channels (@handle, /channel/UC..., /c/..., /user/...; a channel tab URL like /@name/shorts gets only that tab), playlists, search result pages (with their filters) and hashtag pages. Bare video ids, @handles and channel ids work too.

## `sortingOrder` (type: `string`):

How YouTube orders the search results. YouTube doesn't follow "Upload date" strictly.

## `dateFilter` (type: `string`):

Only videos uploaded within this time.

## `videoType` (type: `string`):

Videos (default) or movies.

## `lengthFilter` (type: `string`):

Only videos of this length.

## `isHD` (type: `boolean`):

Only videos with the "HD" feature.

## `hasSubtitles` (type: `boolean`):

Only videos with the "Subtitles/CC" feature.

## `hasCC` (type: `boolean`):

Only videos with the "Creative Commons" feature.

## `is3D` (type: `boolean`):

Only videos with the "3D" feature.

## `isLive` (type: `boolean`):

Only streams that are live now.

## `isBought` (type: `boolean`):

Only videos with the "Purchased" feature.

## `is4K` (type: `boolean`):

Only videos with the "4K" feature.

## `is360` (type: `boolean`):

Only videos with the "360°" feature.

## `hasLocation` (type: `boolean`):

Only videos with the "Location" feature.

## `isHDR` (type: `boolean`):

Only videos with the "HDR" feature.

## `isVR180` (type: `boolean`):

Only videos with the "VR180" feature.

## `oldestPostDate` (type: `string`):

A date (2026-01-31) or an age ("7 days", "2 weeks", "3 months", "1 year"). Works for every source. On a channel listed newest first the run stops at the first older video; elsewhere older videos are skipped.

## `sortVideosBy` (type: `string`):

The order of a channel's videos, Shorts and streams (the channel page's Latest / Popular / Oldest buttons).

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

On: each video's likes, comment count, exact publish time, full description with its links, hashtags, category, tags and more. Off: only what the list shows (title, views, duration, "3 days ago"), several times faster. Video URLs are always opened.

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

The channel's subscribers, total views, video count, country, joined date, description, links, avatar and banner, on every row (one small request per channel).

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

How many videos are fetched in parallel.

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

Apify Proxy (datacenter) works well and is on by default: YouTube refuses some Apify servers' own IP addresses.

## Actor input object example

```json
{
  "searchQueries": [
    "Crawlee"
  ],
  "maxResults": 10,
  "maxResultsShorts": 0,
  "maxResultStreams": 0,
  "startUrls": [],
  "sortVideosBy": "NEWEST",
  "includeVideoDetails": true,
  "includeChannelInfo": true,
  "maxConcurrency": 10,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `all` (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": [
        "Crawlee"
    ],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("wulfcare/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": ["Crawlee"],
    "maxResults": 10,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

```

## MCP server setup

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