# YouTube Scraper - Videos, Search, Subtitles & Comments (`gganbukim/youtube-scraper`) Actor

Paste any YouTube URL or a search term and get the videos back as rows: title, channel, views, duration and date. Switch on subtitles, comments or sponsor deals to add them in the same run. No API key, no login, no Data API quota.

- **URL**: https://apify.com/gganbukim/youtube-scraper.md
- **Developed by:** [DONGMIN KIM](https://apify.com/gganbukim) (community)
- **Categories:** Videos, Social media, AI
- **Stats:** 1 total users, 1 monthly users, 99.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 video rows

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

## YouTube Scraper — Any URL or Search, One Actor

![YouTube Scraper — Any URL or Search, One Actor](https://raw.githubusercontent.com/gganbukim1/apify-actor-images/master/cards/youtube-scraper.png)

Paste a **video, channel, @handle or playlist URL** — or just a search term — and get the
videos back as rows: title, channel, views, duration, date, thumbnail.

Then switch on what else you want **in the same run**: subtitles, comments, sponsor deals.
No second Actor, no second input list, no stitching two datasets together afterwards.

No API key, no login, no Data API quota.

### What it does well

- **One input for everything** — mix video URLs, channels, playlists and search terms in one run. It works out what each one is.
- **Add-ons instead of separate runs** — subtitles, comments and sponsor deals attach to the videos you already asked for.
- **Filters run before billing** — narrow by views or age and the ones you dropped cost nothing.
- **Honest date filtering** — a live stream carries no publish date, so it is dropped rather than passed through unchecked. You are never billed for a row that was not tested against your filter.
- **Cheap and stable** — starts on datacenter proxies and escalates only once blocking is proven.

### Input

```jsonc
{
  "startUrls": ["@veritasium", "https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "searchQueries": ["how to make sourdough"],
  "maxResults": 50,
  "publishedWithinDays": 90,
  "includeTranscript": true,
  "transcriptFormat": "text"
}
```

The same wording you see in the Apify console, with the JSON key for API and MCP callers.

| Option | What it does | Default |
|---|---|---|
| **YouTube URLs** — `startUrls` | Any mix of video, channel, @handle and playlist URLs. Channels and playlists are expanded into their videos; a video URL you pass is always kept, filters or not, because you already chose it. | — |
| **Search terms** — `searchQueries` | Or search instead. Each term is run separately and every result becomes a row tagged with the term that found it. Use this with URLs or on its own. | — |
| **Max videos** — `maxResults` | Total videos across everything you passed. This is the main cost control. **URLs are served first and search fills what is left** — you named the channel, the search is us guessing — so raise this when you pass both. Leave empty to take everything. | `100` |
| **Which videos from a channel** — `channelTab` | Only applies to channel URLs. Shorts carry no date or length on their shelf, so switch on subtitles or sponsors if you need those fields for them. | `"videos"` |
| **Sort search results by** — `searchSortBy` | Only applies to search terms. Relevance or most viewed — **two and not four on purpose**, because YouTube's upload-date and rating params were measured returning relevance order anyway, and a sort that quietly does not sort is worse than none. | `"relevance"` |
| **Only videos newer than (days)** — `publishedWithinDays` | 0 disables. Applies to videos found by search or by reading a channel or playlist. Accurate to about a day, and rows with no date at all are dropped. Filtered videos are not billed. | `0` |
| **Minimum views** — `minViewCount` | 0 disables. Applies to videos found by search or by reading a channel or playlist. Filtered videos are not billed. | `0` |
| **Add subtitles** — `includeTranscript` | Fetch each video's caption track and put it on the row. Billed per transcript delivered, and only when text came back. | `false` |
| **Subtitle format** — `transcriptFormat` | Plain text, SRT, VTT, or chunks for a vector store. | `"text"` |
| **Preferred subtitle languages** — `transcriptLanguages` | In order of preference. Falls back to any available track. | `["en"]` |
| **Add comments** — `includeComments` | Write each video's top comments as their own rows. Billed per comment. | `false` |
| **Max comments per video** — `maxCommentsPerVideo` | Comments arrive about 20 per request, so this is what a comments run costs. | `20` |
| **Add sponsor deals** — `includeSponsorships` | Read descriptions and spoken captions for brand deals. Billed per deal found — videos with no deal are free. | `false` |
| **Add a channel summary row** — `includeChannelRow` | One row per channel you passed: subscribers, total video count, description, keywords, avatar, RSS URL. The record arrives with the listing, so it costs no extra request. | `false` |
| **Add a creator lead row** — `includeCreatorLead` | One row per channel with the business email it publishes, audience and upload bands, and — with sponsor deals also on — which brands already pay it. | `false` |
| **Concurrency** — `concurrency` | How many videos to read in parallel. | `5` |
| **Proxy** — `proxyConfiguration` | Leave the default. Starts on datacenter proxies and escalates only once blocking is proven. | `{"useApifyProxy":true}` |

### Output

Row types share one dataset and are told apart by `type`. Video rows always appear; the
`comment`, `sponsorship`, `channel` and `lead` rows only when you switch those add-ons on.
Channel and lead rows are one per channel you passed, not per video.

| Field | Row | What it is |
|---|---|---|
| `type` | all | `"video"`, `"comment"` or `"sponsorship"`. |
| `videoId` | all | The 11-character id. |
| `url` | video | `watch?v=` URL. |
| `title` | video | Video title. |
| `channelName` | video · sponsorship | Channel display name. |
| `channelId` | video | The `UC…` id. |
| `channelUrl` | video | Canonical channel URL. |
| `viewCount` | video | Views, parsed from the display text. |
| `durationSeconds` | video | Length in seconds. |
| `publishedText` | video | YouTube's relative text, e.g. `"9 days ago"`. Empty on live streams. |
| `publishedAt` | video | Exact upload timestamp. Only present when an add-on made us read the video. |
| `approxAgeDays` | video | Age in days, from `publishedAt` when we have it, otherwise from `publishedText`. |
| `thumbnailUrl` | video | Largest available thumbnail, derived from the id when the listing gave none. |
| `isShort` | video | `true` when YouTube served the item as a Short. |
| `description` | video | Full description. Only when an add-on made us read the video. |
| `keywords` | video | The channel's tags for the video, same condition. |
| `foundBy` | video | How it got into the run: `"url"`, `"search"` or `"listing"`. |
| `query` | video | The search term that found it, when `foundBy` is `"search"`. |
| `scrapedAt` | video · sponsorship | ISO timestamp of the run. |
| `transcript` | video | Plain text, with `transcriptFormat: "text"`. |
| `srt` / `vtt` | video | Subtitle file contents, with the matching format. |
| `chunks` | video | Overlapping passages sized for a vector store, with `"chunks"`. |
| `transcriptLanguage` | video | Language code of the track that was used. |
| `transcriptWordCount` | video | Words in the transcript. |
| `commentId` | comment | The comment's id. |
| `text` | comment | Comment body. |
| `publishedTimeText` | comment | Relative time, as YouTube shows it. |
| `likeCount` / `replyCount` | comment | Engagement on the comment. |
| `replyLevel` / `isReply` | comment | Where it sits in the thread. |
| `authorName` | comment | Commenter's display name. |
| `authorChannelId` / `authorChannelUrl` | comment | Commenter's channel. |
| `authorAvatarUrl` | comment | Commenter's avatar. |
| `authorIsVerified` / `authorIsCreator` | comment | Badges YouTube shows. |
| `isPinned` | comment | Pinned by the creator. |
| `commentUrl` | comment | Deep link to the comment. |
| `videoUrl` / `videoTitle` | comment · sponsorship | The video the row belongs to. |
| `brand` | sponsorship | The sponsor, as a domain. |
| `brandDomain` / `sponsorUrl` | sponsorship | Where the deal points. |
| `campaign` / `trackedLink` | sponsorship | The tracked campaign path, when there is one. |
| `code` / `offer` | sponsorship | Discount code and the offer text. |
| `source` | sponsorship | `"description"`, `"transcript"` or both. |
| `evidence` / `spokenEvidence` | sponsorship | The text the deal was read out of. |
| `readAtSeconds` | sponsorship | Where in the video it was spoken. |
| `handle` / `handleUrl` | channel · lead | The `@handle` and its URL. |
| `subscriberCount` | channel · lead | Subscribers, parsed. |
| `subscriberCountText` | channel · lead | The unparsed original, e.g. `"21.1M subscribers"`. |
| `videoCount` | channel · lead | Total uploads the channel reports — not the number scraped. |
| `avatarUrl` | channel · lead | Largest available avatar. |
| `rssUrl` | channel · lead | The channel's RSS feed, useful for cheap polling between runs. |
| `videosFound` | channel | How many videos this run returned for the channel. |
| `email` / `emails` | lead | The address the channel publishes. Business addresses rank first. |
| `hasBusinessEmail` | lead | `true` when the address is `partnerships@`-style rather than personal. |
| `instagram` / `twitter` / `tiktok` / `facebook` / `discord` / `patreon` | lead | One key per network found in the description. |
| `audienceBand` | lead | `nano` · `micro` · `mid` · `large` · `mega`. A band, not a score. |
| `uploadBand` | lead | `active` · `slowing` · `dormant` · `stale`. |
| `daysSinceNewestVideo` | lead | How long since the last upload. |
| `uploadsLast30Days` / `uploadsLast90Days` | lead | Output volume in the videos this run read. |
| `scannedVideos` | lead | The denominator behind `sponsorshipRate`. Zero when sponsor deals were off. |
| `sponsoredVideos` / `sponsorshipRate` | lead | How many carried a deal, and the percentage. Empty when sponsor deals were off. |
| `sponsorCount` / `sponsorBrands` | lead | Distinct brands found, and which. |
| `trackedCampaignCount` | lead | Deals carrying a tracked campaign link. |
| `takesSponsorships` | lead | `true` when any deal was found. |

### What it costs

Video rows are the base. Everything else is an add-on you switch on, and each one is
billed only for what it actually delivers:

| You get | Price |
|---|---|
| A video row | $0.50 per 1,000 |
| A subtitle track on that row | $2.00 per 1,000 |
| A comment | $0.50 per 1,000 |
| A sponsor deal found | $8.00 per 1,000 |
| A channel summary row | $5.00 per 1,000 |
| A creator lead row | $6.00 per 1,000 |

A 100-video run with nothing switched on is 5 cents. The same run with subtitles is 25.

### Who this is for

- **Anyone who does not want to pick a tool first** — paste the URL, get the data, add what else you need with a checkbox.
- **AI and RAG engineers** — search a topic, take the transcripts as chunks, done in one run.
- **Competitive and market researchers** — a channel, its videos, what its viewers say and who pays for its content, in one dataset.
- **Data teams** — one Actor to integrate instead of four, one dataset to read.

### Common uses

- **Topic research** — search a niche, filter to the last 90 days, pull transcripts of what ranks.
- **Channel audit** — one @handle, the whole catalogue, with sponsor deals attached.
- **Comment mining** — search your category and read what the audience asks under the top results.
- **Feeding a vector store** — `transcriptFormat: "chunks"` goes straight in.

### Pricing

Pay only for rows you receive. Filtered videos, failed videos and every discovery step are
free, and a video with no captions costs nothing extra even with subtitles switched on.

Starting a run costs $0.00001 — the platform's $0.00001 minimum, charged once per GB of
memory, and these Actors run on 512 MB.

### Other Actors in this family

Same engines, same billing, no account or API key on any of them.

**YouTube & video**

- [Download YouTube Subtitles in Bulk — SRT, VTT & Text](https://apify.com/gganbukim/youtube-transcript-scraper) — Bulk subtitles from videos, channels or playlists — text, SRT, VTT or RAG chunks.
- [Export YouTube Comments to CSV — Replies and Likes](https://apify.com/gganbukim/youtube-comments-scraper) — Every comment and reply thread, with likes, authors and creator flags.
- [List Every Video on a YouTube Channel — Export to CSV](https://apify.com/gganbukim/youtube-channel-scraper) — A channel's whole back catalogue plus a subscriber and RSS summary row.
- [Find YouTube Sponsors — Brand Deals, Codes & Links](https://apify.com/gganbukim/youtube-sponsorship-finder) — Which brands pay which creators, with the campaign link, the code and the timestamp.
- [YouTube Search API — Bulk Results, No Quota](https://apify.com/gganbukim/youtube-search-scraper) — Many search terms at once, every result as a row, filtered before you are billed.
- [Track Deleted YouTube Videos & Title Changes](https://apify.com/gganbukim/youtube-channel-monitor) — What a channel quietly changed: deleted videos, rewritten titles, view velocity.
- [YouTube Creator Email Finder & Sponsor Lookup](https://apify.com/gganbukim/youtube-creator-leads) — A channel list into leads: the published email, audience bands, and who already sponsors them.
- [Export a YouTube Playlist to CSV — Every Video](https://apify.com/gganbukim/youtube-playlist-scraper) — Any playlist as a table, with each video position in it.

**Search demand**

- [AnswerThePublic Alternative — Autocomplete Keyword API](https://apify.com/gganbukim/long-tail-keyword-scraper) — One seed into hundreds of real keywords from Google, YouTube and Amazon autocomplete.
- [Google Trends API — Today's Trending Searches, No Key](https://apify.com/gganbukim/google-trends-scraper) — Today's trending searches by country, with traffic bands and the news behind them.

**E-commerce**

- [Export Any Shopify Store's Products to CSV or JSON](https://apify.com/gganbukim/shopify-product-scraper) — Any Shopify catalogue: variants, SKUs, live prices, stock, images, collections.
- [New Shopify Product Alerts — Competitor Drop Tracker](https://apify.com/gganbukim/shopify-new-arrivals-monitor) — Only what a store launched since the last run. Scanning is free.
- [Shopify Store Email Finder — Qualified B2B Leads](https://apify.com/gganbukim/shopify-store-leads) — A domain list into qualified leads: contact email, size, price band, and whether the shop still trades.
- [Website Tech Stack & Email Finder for B2B Lists](https://apify.com/gganbukim/website-leads-scraper) — Any domain list into leads: contact email, what the site runs on, and the marketing tags it carries.

**Hiring**

- [Greenhouse, Lever & Ashby Job Scraper — No API Key](https://apify.com/gganbukim/ats-job-scraper) — Paste a company domain, get its open roles from Greenhouse, Ashby, Lever or SmartRecruiters.
- [Ghost Job Detector — Track Reposts, Closures & Edits](https://apify.com/gganbukim/job-lifecycle-monitor) — What changed on a careers page: opened, closed, quietly reposted, or a ghost job.

### Run it from code

Nothing here needs a login to the source, only your Apify token.

**HTTP** — start a run and wait for the rows:

```bash
curl -X POST "https://api.apify.com/v2/acts/gganbukim~youtube-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "content-type: application/json" \
  -d @input.json
```

**JavaScript**

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('gganbukim/youtube-scraper').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("gganbukim/youtube-scraper").call(run_input=input)
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

**Scheduled or event-driven** — attach a [schedule](https://docs.apify.com/platform/schedules) to run it on a cron, or a [webhook](https://docs.apify.com/platform/integrations/webhooks) to push each finished run into your own endpoint. It also connects through Apify's [Zapier, Make, n8n and LangChain integrations](https://docs.apify.com/platform/integrations), and is reachable from an [MCP server](https://docs.apify.com/platform/integrations/mcp) if you are driving it from an agent.

**Standby / API mode** — the run above is synchronous: one call in, rows out, no polling. That is the shape to use if you are calling this per request rather than in a batch.

### Errors, limits and what you are charged for

- **You pay for delivered rows only.** A row your filters removed, a page that failed, a retry — none of it is billed. Starting a run costs $0.00001: the platform minimum, charged once per gigabyte, and this Actor runs on 512 MB.
- **A run that delivers nothing still costs the start fee and nothing else.** If the input resolved to zero items, the run fails loudly with the reason rather than finishing green on an empty dataset.
- **Blocking is handled by changing address, not by waiting.** The Actor starts on cheap datacenter proxies and moves up only after a tier has actually been refused several times in a row, then drops back down once the cheap tier answers cleanly again. You are not paying for residential bandwidth that was never needed.
- **Rate limits belong to the source, not to this Actor.** Very large inputs are worked through in batches; the run reports how many items succeeded, were filtered, and failed, so a partial result is never presented as a complete one.
- **Dataset retention follows your Apify plan.** Export what you need, or push it out with a webhook, if you want it past that window.

### Is this legal?

This Actor reads pages and public endpoints that anyone can open in a browser without an account. It does not log in, does not defeat a paywall, and does not touch anything behind authentication.

Scraping public data is broadly lawful in the US and the EU, and courts have repeatedly said so — but "public" is not the same as "unrestricted", and what you may then *do* with the data is a separate question from whether you may collect it. Personal data pulls in the GDPR and similar regimes whatever the source, so if your rows contain people, you need a lawful basis for keeping them.

Apify publishes a fuller treatment in [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/) and an [ethical scraping guide](https://docs.apify.com/academy/web-scraping-for-beginners/crawling/scraping-ethics). None of this is legal advice; if the use is commercial and the data is personal, ask someone qualified.

### Something wrong, or missing?

Open an issue on the [Actor's Issues tab](https://apify.com/gganbukim/youtube-scraper/issues) — it goes straight to the developer and is the fastest route. Include the run ID; it carries the input and the log, which is usually enough to reproduce the problem without another round trip.

Sources change without warning, and a field that quietly goes null is worth reporting even if the run succeeded. A broken parser looks exactly like a quiet day in the data until someone says so.

### FAQ

**Will I get blocked, or my account banned?** There is no account to ban — this Actor
never signs in and sends no cookies. It reads the same endpoints the player and the search
page use, on cheap datacenter proxies, escalating to residential only once datacenter is
provably blocked. YouTube signals throttling with an HTTP 200 and an error body rather than
an error code, which is the trap that silently halves most scrapers' results; this one
detects it by reason and rotates.

**Is it legal to scrape YouTube?** It reads the same public pages a browser requests, with
no login and nothing bypassed, and the fields are facts about videos rather than the videos
themselves. Public-data scraping is broadly treated as lawful in the US and EU. What you do
with the data afterwards — republishing transcripts wholesale, or handling personal data
from comments — is a separate question with its own rules.

**Does this need a YouTube API key?** No. Nor a Google account, nor the 10,000-unit daily
Data API quota.

**Why would I use this instead of the single-purpose Actors?** Convenience, mostly. If you
only ever want transcripts, the dedicated transcript Actor has more knobs for them — any
language, empty-result rows, segment output. This one is for when you want a few things at
once, or do not want to think about which tool to reach for.

**What if a video has no captions?** The row still arrives, with `transcript: null`, and
you are not billed for a transcript.

**How much does 1,000 videos cost?** $0.50, plus $0.00002 for the run itself. Add-ons are
priced separately in the table above and only bill for what arrives.

**Can I export the results to Excel or Google Sheets?** Yes. Every run's dataset exports as
CSV, XLSX, JSON or XML from the console, or through the API.

**Can I connect it to Zapier, Make or n8n?** Yes — Apify publishes integrations for all
three, plus webhooks that fire when a run finishes.

**Do I need to write code?** No. Fill the form in the console and press Start.

**Can I run it on a schedule?** Yes, from the Schedules tab. Pair it with a webhook to push
new rows wherever you want them.

# Actor input Schema

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

Any mix of video, channel, @handle and playlist URLs. Channels and playlists are expanded into their videos; a video URL you pass is always kept, filters or not, because you already chose it.

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

Or search instead. Each term is run separately and every result becomes a row tagged with the term that found it. Use this with URLs or on its own.

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

Total videos across everything you passed. This is the main cost control. URLs are served first and search fills what is left, so raise this when you pass both. Leave empty to take everything the input resolves to.

## `channelTab` (type: `string`):

Only applies to channel URLs. Shorts carry no date or length on their shelf, so switch on subtitles or sponsors if you need those fields for them.

## `searchSortBy` (type: `string`):

Only applies to search terms. Two options and not four on purpose: YouTube's upload-date and rating sort params were measured returning relevance order anyway, and a sort that quietly does not sort is worse than no sort at all.

## `publishedWithinDays` (type: `integer`):

0 disables. Applies to videos found by search or by reading a channel or playlist. Listing pages publish relative dates ("3 weeks ago"), so this is accurate to about a day, and rows with no date at all — live streams, Shorts — are dropped rather than passed through unchecked. Filtered videos are not billed.

## `minViewCount` (type: `integer`):

0 disables. Applies to videos found by search or by reading a channel or playlist. Filtered videos are not billed.

## `includeTranscript` (type: `boolean`):

Fetch each video's caption track and put it on the row. Billed per transcript delivered, and only when text actually came back — a video with no captions is free.

## `transcriptFormat` (type: `string`):

Plain text for reading and AI, SRT or VTT for re-uploading, chunks for a vector store.

## `transcriptLanguages` (type: `array`):

In order of preference. Falls back to any available track, auto-generated included.

## `includeComments` (type: `boolean`):

Write each video's top comments as their own rows. Billed per comment delivered.

## `maxCommentsPerVideo` (type: `integer`):

Comments arrive about 20 per request, so this is what a comments run costs.

## `includeSponsorships` (type: `boolean`):

Read descriptions and spoken captions for brand deals: the sponsor, the tracked link, the discount code and the timestamp it was read at. Billed per deal found — videos with no deal are free.

## `includeChannelRow` (type: `boolean`):

One row per channel you passed, with subscribers, total video count, description, keywords, avatar and RSS URL. The record arrives with the listing, so this costs no extra request.

## `includeCreatorLead` (type: `boolean`):

One row per channel with the business email it publishes, audience and upload bands, and — when Add sponsor deals is also on — which brands already pay it. Switch that on too or the sponsor columns stay empty rather than reading as a zero.

## `concurrency` (type: `integer`):

How many videos to read in parallel.

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

Leave the default. Runs start on cheap datacenter proxies and escalate to residential only once blocking is proven.

## Actor input object example

```json
{
  "startUrls": [
    "@veritasium"
  ],
  "maxResults": 100,
  "channelTab": "videos",
  "searchSortBy": "relevance",
  "publishedWithinDays": 0,
  "minViewCount": 0,
  "includeTranscript": false,
  "transcriptFormat": "text",
  "transcriptLanguages": [
    "en"
  ],
  "includeComments": false,
  "maxCommentsPerVideo": 20,
  "includeSponsorships": false,
  "includeChannelRow": false,
  "includeCreatorLead": false,
  "concurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per video, plus a row per comment and per sponsor deal when those add-ons are on. Tell them apart by the `type` field.

## `runSummary` (type: `string`):

Counts for this run: videos delivered, transcripts, comments and sponsor deals added, how many were filtered out before billing, how many had no readable date, and the proxy tier used.

# 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 = {
    "startUrls": [
        "@veritasium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gganbukim/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 = { "startUrls": ["@veritasium"] }

# Run the Actor and wait for it to finish
run = client.actor("gganbukim/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 '{
  "startUrls": [
    "@veritasium"
  ]
}' |
apify call gganbukim/youtube-scraper --silent --output-dataset

```

## MCP server setup

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