# YouTube Scraper - No Rate Limits, Pay Per Result (`plainjson/youtube-scraper-no-rate-limits`) Actor

Scrape YouTube video metadata by keyword, channel, playlist or a single video URL. No concurrency caps, no minimum items per query, no waiting between runs. No login, no cookies, no browser.

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

## Pricing

from $1.20 / 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.

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 — No Rate Limits, Pay Per Result

Extract YouTube video metadata by **keyword**, **channel**, **playlist**, or **a single video URL**.
No login, no cookies, no browser rendering, and **no artificial limits on how you run it**.

A YouTube Data API alternative for people who ran out of quota, and a replacement for scrapers that
ration you.

***

### What makes this one different

Most YouTube scrapers on the market work, but they fence you in. This one removes four specific
fences, and each is checkable against any competitor's own documentation:

| | Common restriction elsewhere | Here |
|---|---|---|
| **Concurrent runs** | "max 1 concurrent run at the same time" | **Unlimited.** Run as many as your plan allows |
| **Single video URL** | "single video URLs are not allowed" | **Allowed.** One URL, one result, no batching required |
| **Minimum per query** | "you should fetch min of 10 items" | **No minimum.** Ask for 1 item and get 1 item |
| **Waiting between runs** | "wait a couple of minutes between your runs" | **No wait.** Fire when you want |

If you have ever seen `Rate limited immediately after renewal` in your logs, this is the tool that
does not do that to you.

**To be precise about what "no rate limits" means:** this Actor imposes none of its own. It does not
throttle you, does not cap concurrency, and does not make you wait. What it cannot do is change how
YouTube itself responds — if YouTube throttles a request, you will see it, and the row lands in your
dataset with an `error` field instead of disappearing. Turning on `proxyConfiguration` resolves that
in practice.

***

### What you get per video

```json
{
  "id": "BBz-Jyr23M4",
  "url": "https://www.youtube.com/watch?v=BBz-Jyr23M4",
  "title": "Guitar Lesson 1 - Absolute Beginner? Start Here!",
  "viewCount": 38622892,
  "viewCountText": "38,622,892 views",
  "durationSeconds": 971,
  "durationText": "16:11",
  "publishedText": "9 years ago",
  "description": "Feeling stuck, inconsistent…",
  "isLive": false,
  "channel": {
    "name": "Andy Guitar",
    "id": "UC...",
    "url": "https://www.youtube.com/channel/UC..."
  },
  "thumbnail": "https://i.ytimg.com/vi/BBz-Jyr23M4/hq720.jpg",
  "source": "search:guitar lesson",
  "scrapedAt": "2026-08-20T11:35:25.405Z"
}
```

`viewCount` and `durationSeconds` come as **numbers**, not strings you have to clean up.
`source` tells you which input produced the row, so a mixed run stays traceable.

***

### Input

```json
{
  "searchQueries": ["guitar lesson", "bass tutorial"],
  "startUrls": [
    "https://www.youtube.com/watch?v=BBz-Jyr23M4",
    "https://www.youtube.com/@AndyGuitar",
    "https://www.youtube.com/playlist?list=PL..."
  ],
  "maxItems": 100,
  "country": "US",
  "language": "en"
}
```

Every field is optional except that you need at least one of `searchQueries` or `startUrls`.

**Accepted URL shapes:** `/watch?v=`, `youtu.be/`, `/shorts/`, `/@handle`, `/channel/`, `/c/`,
`/user/`, `/playlist?list=`, and `/results?search_query=`.

***

### What happens when something fails — and what it costs you

**Nothing. You are never charged for an input that did not produce data.**

Failures do not go into the main dataset, because the main dataset is what you are billed on. They
go into a separate `failed-inputs` dataset (Storage tab) and are shouted into the run log, one line
each. Never silent, never billed:

```json
{ "input": "https://www.youtube.com/watch?v=deleted", "error": "extraction_failed",
  "message": "video deleted: videoDetails missing (private, removed, or age-restricted)" }
```

So a run where you paste 100 URLs and 30 are dead costs you 70 videos, not 100.

| `error` | Meaning |
|---|---|
| `invalid_url` | The URL is not a YouTube URL this Actor recognises |
| `extraction_failed` | The video is private, removed, or age-restricted |
| `search_failed` | The search page did not return usable data |

***

### Proxy

**Not needed for search, channels or playlists.** Those read the data YouTube renders server-side
over plain HTTP — no headless browser, no proxy. Verified running on Apify's own infrastructure.

**Single video lookups are the one exception.** From a datacenter IP, YouTube's player endpoint
answers `Sign in to confirm you're not a bot`, so this Actor falls back to a second public path that
is not restricted, and still returns the full record — title, exact view count, duration, channel.
You do not have to do anything; it happens automatically and is reported in the run log.

Turning `proxyConfiguration` on makes single video lookups take the direct, cheaper path again. It
is optional, and everything works without it.

***

### Honest limits

- **Channel pages return the channel's recent uploads**, not the full lifetime archive in a single
  run. Increase `maxItems` and paginate for deeper history.
- **`viewCount` on channel and playlist listings is YouTube's rounded figure** (e.g. `1.2K`).
  For an exact count, pass the video URL directly — single video lookups return the precise number.
- **Age-restricted and private videos cannot be read** without a login, and this Actor does not log
  in. They come back as `extraction_failed`.
- YouTube changes its page structure from time to time. If a field goes empty, open an issue — that
  is the fastest way to get it fixed.

***

### What this Actor is for, and what it is not for

**For:** competitive research, content analytics, trend tracking, media monitoring, academic work —
anything where you need the public metadata of public videos.

**Not for:** building profiles of individuals, compiling contact details, or enriching people-datasets.
This Actor returns no emails, no phone numbers, no subscriber lists, no comment authors, and nothing
that sits behind a login. If a person-level dataset is what you need, this is the wrong tool and it
will not become the right one.

### Legal

This Actor reads **publicly available, logged-out** pages. It does not log in, does not use cookies,
and does not bypass any technical access control — when YouTube restricts an endpoint, this Actor
falls back to a public path rather than working around the restriction.

**On personal data:** video metadata carries the public channel name and channel URL of whoever
published the video. That is returned as YouTube shows it. Nothing else about people is collected,
and no profile is built. **You are the data controller for whatever you do with the output.**

**On what it talks to:** the only host this Actor contacts is `www.youtube.com`. There is no callback
to the author, no telemetry, and no third-party endpoint. Your data stays in your account.

# Actor input Schema

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

Keywords to search on YouTube. There is no minimum number of items per keyword.

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

Video, Shorts, channel, playlist or search URLs, one per line. A SINGLE video URL is allowed - you are not forced to batch. Plain URLs work; {"url": "..."} objects work too.

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

Upper bound on videos returned. Set it to 1 if that is all you need.

## `country` (type: `string`):

Two-letter country code used for the search locale.

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

Two-letter language code used for the search locale.

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

Usually NOT needed - the data path is plain HTTP. Turn it on only if you hit HTTP 429.

## Actor input object example

```json
{
  "searchQueries": [
    "guitar lesson"
  ],
  "startUrls": [
    "https://www.youtube.com/watch?v=BBz-Jyr23M4"
  ],
  "maxItems": 20,
  "country": "US",
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `videos` (type: `string`):

One record per video: title, exact view count, duration in seconds, channel, publish date and thumbnail. Inputs that produced no data are not in here and are not charged - they go to the "failed-inputs" dataset instead.

# 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": [
        "guitar lesson"
    ],
    "startUrls": [
        "https://www.youtube.com/watch?v=BBz-Jyr23M4"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plainjson/youtube-scraper-no-rate-limits").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": ["guitar lesson"],
    "startUrls": ["https://www.youtube.com/watch?v=BBz-Jyr23M4"],
}

# Run the Actor and wait for it to finish
run = client.actor("plainjson/youtube-scraper-no-rate-limits").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": [
    "guitar lesson"
  ],
  "startUrls": [
    "https://www.youtube.com/watch?v=BBz-Jyr23M4"
  ]
}' |
apify call plainjson/youtube-scraper-no-rate-limits --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plainjson/youtube-scraper-no-rate-limits"
        }
    }
}

```

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/mrCFHS4vUYc2sLKXY/builds/pRk3XvByyBsp8D5Xi/openapi.json
