# YouTube View Generator (`one_house/youtube-view-generator`) Actor

Generate real browser views on YouTube videos: rotating residential proxies, unique fingerprints, configurable watch time, ad skipping, and search or referrer traffic sources. Use only on videos you own or are authorized to promote.

- **URL**: https://apify.com/one\_house/youtube-view-generator.md
- **Developed by:** [One House](https://apify.com/one_house) (community)
- **Categories:** Social media, Videos, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 completed views

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 View Generator

Generate **real browser views** on YouTube videos. Every view runs in its own Chromium instance, on its own proxy IP, with its own device fingerprint — and actually plays the video for a watch time you choose.

> **Authorized use only.** Run this Actor only on videos and channels you own or have explicit permission to promote. Artificially inflating public metrics breaks YouTube's Terms of Service.

### What it actually does

A headless page opened on a YouTube URL does nothing: the player sits paused at 0:00 and no watch time exists. This Actor drives the player the way a viewer would — starts playback, keeps it running, skips ads, and stays for the configured duration.

It also records YouTube's own telemetry for every view, so you can see what reached YouTube rather than guessing:

| Field | Meaning |
|---|---|
| `playbackPings` | YouTube's `/api/stats/playback` call, sent once when a play session opens |
| `watchtimePings` | `/api/stats/watchtime`, repeated as the video advances |
| `watchedSecs` | Seconds the video was genuinely playing, ads excluded |
| `country` | The country YouTube itself resolved for the view's IP |

**What this does not promise:** YouTube validates views server-side and discards ones it considers artificial. Sending the playback and watch-time signals is not the same as a view appearing on the public counter, and no tool can guarantee that outcome.

### Features

- **Real playback** — the player is started and kept running, not just loaded
- **Unique viewer identity** — one proxy IP, one browser, one incognito context per view
- **Watch time you control** — a fixed number of seconds, or a share of the video's length
- **Traffic sources** — direct link, YouTube search (finds the video in results and clicks it), or an external referrer
- **Ad skipping** — presses Skip as soon as it appears
- **144p enforcement** — video is the biggest proxy cost, and a view counts the same at any resolution
- **Real parallelism** — views run at the concurrency you set, from the first view onward
- **Verification built in** — playback and watch-time pings recorded per view
- **Cost guard** — the run stops itself if YouTube blocks several views in a row

### Input

| Field | Description |
|---|---|
| `videoUrls` | Watch links, youtu.be links, Shorts, embeds, or bare video IDs |
| `views` | How many playbacks to generate (1–1000), spread across the videos |
| `concurrency` | How many play at once (1–20) |
| `watchMode` | `seconds` or `percent` of the video's length |
| `minWatchSeconds` / `maxWatchSeconds` | Watch time range in seconds |
| `minWatchPercent` / `maxWatchPercent` | Watch time range as a share of the video |
| `trafficSource` | `direct` | `youtube-search` | `external` |
| `searchKeyword` | Search phrase, required for the YouTube search source |
| `referer` | Referrer URL, required for the external source |
| `lowestQuality` | Pin playback to 144p to cut proxy traffic |
| `skipAds` | Press Skip when it appears |
| `device` | `desktop` | `mobile` | `tablet` | `auto` |
| `delayBetweenViewsSecs` | Pause before starting the next view |
| `maxRetries` | Retries on a fresh proxy IP when a view is refused or stalls |
| `proxyConfiguration` | Apify Proxy or custom (Residential strongly recommended) |

```json
{
    "videoUrls": [{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }],
    "views": 20,
    "concurrency": 2,
    "watchMode": "percent",
    "minWatchPercent": 40,
    "maxWatchPercent": 80,
    "trafficSource": "youtube-search",
    "searchKeyword": "never gonna give you up",
    "lowestQuality": true,
    "skipAds": true,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": ["RESIDENTIAL"]
    }
}
```

### Output

Each view pushes one dataset item:

```json
{
    "viewId": "view-0-1700000000000",
    "videoId": "dQw4w9WgXcQ",
    "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "videoTitle": "Rick Astley - Never Gonna Give You Up",
    "status": "success",
    "playbackStarted": true,
    "watchedSecs": 84.0,
    "targetWatchSecs": 84,
    "videoDurationSecs": 213.0,
    "percentWatched": 39.4,
    "playbackPings": 1,
    "watchtimePings": 8,
    "adsSkipped": 1,
    "quality": "tiny",
    "trafficSource": "youtube-search",
    "enteredViaSearch": true,
    "device": "desktop",
    "country": "US",
    "error": null,
    "timestamp": "2026-08-10T21:00:00.000Z"
}
```

`status` is one of:

| Value | Meaning |
|---|---|
| `success` | The video played for the full target watch time |
| `partial` | Playback started but stalled before reaching the target |
| `failed` | The player never started |
| `blocked` | YouTube refused the request on every retry |

A run-level summary is stored in the default key-value store under **`SUMMARY`**, with the success rate, how many playbacks started, and total watch time generated.

### Pricing

You are charged for each **completed view** — one that played for its full target watch time. Views that fail, stall, or get blocked cost nothing, and they are still written to the dataset so you can see what happened.

On top of that you pay the Apify platform usage the run consumes: compute time and, above all, residential proxy traffic. Proxy bandwidth is by far the largest part, so the settings below are what actually drive your bill.

### Watch time and cost

Two settings drive almost all of your cost. **Watch time** is time a proxy connection and a browser stay open, and **quality** decides how much video travels through that proxy. Leaving `lowestQuality` on pins the stream to 144p instead of the 480p YouTube picks by default, which is the single biggest saving available.

As a measured reference point, a 24-second view at 144p through residential proxy moves about 6.4 MB — roughly half of it the YouTube page itself, which every view pays regardless of length. A 60-second view costs about 1.5x that, and a five-minute view roughly six times. Set the watch time you actually need; the difference is real money.

You can cap what a run may spend with the **max cost per run** setting in the Apify Console. Once that cap is reached the Actor stops starting new views instead of running up work nobody pays for.

Watch time is measured from how far the player's own clock advanced. Ads are excluded, because YouTube plays them through the same player and counting them would overstate what the video received, and a buffering stream earns nothing either — if the picture is frozen, the time does not count.

What limits concurrency is CPU rather than memory, and on Apify memory is how you buy it: roughly one core per 4 GB. A browser decoding video wants a fair share of a core, so allow about 2 GB per parallel view — 4 GB for two, 8 GB for four. You can run three players on 4 GB and they will finish, but they take noticeably longer to get going and stall more often, which costs proxy traffic. The concurrency you set is applied from the first view rather than ramped up, so a short run finishes in the number of batches you would expect.

### Traffic sources

**Direct** opens the watch page straight away — the simplest and most reliable.

**YouTube search** loads the results for your keyword, scrolls until it finds your video, and clicks it, so the play arrives from a search rather than out of nowhere. If the video is not among the first results the view falls back to opening the link directly, and `enteredViaSearch` in the dataset tells you which happened. Pick a phrase your video actually ranks for.

**External** sends a referrer header from the address you supply, as if the viewer followed a link from another site.

### Proxies

| Proxy | When to use |
|---|---|
| **Apify Residential** (default) | Recommended — each view arrives from a distinct home IP |
| Apify Datacenter | Cheaper, but YouTube treats datacenter ranges with far more suspicion |
| Custom proxies | Your own provider contract |

Running without a proxy makes every view arrive from the same address, which looks like one viewer replaying a video.

### When views are refused

YouTube sometimes answers with a block instead of a page, and some residential addresses are throttled so hard that the player opens but the video never advances. Both cases retry on a fresh proxy IP, with a backoff so the new address does not walk into the same rate limit. A view is only reported as successful — and only billed — once the video's own clock has actually run for the target watch time, so a stalled stream never counts as a view. If several views in a row are refused, the run stops itself rather than spending the rest of your proxy budget against a wall.

# Actor input Schema

## `videoUrls` (type: `array`):

Video links or IDs. Watch links, youtu.be share links, Shorts, and embeds all work.

## `views` (type: `integer`):

How many separate playbacks to generate. Views are spread evenly across the videos you listed.

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

How many videos play at the same time. What limits this is CPU, and memory is how you buy it here — about one core per 4 GB. Allow roughly 2 GB per parallel view: 4 GB for two, 8 GB for four. Packing more players onto the same memory works, but they start slowly and stall more often.

## `watchMode` (type: `string`):

Set the watch time as a fixed number of seconds, or as a share of the video's length.

## `minWatchSeconds` (type: `integer`):

Used when the watch time mode is Seconds.

## `maxWatchSeconds` (type: `integer`):

Each view picks a random length between the minimum and maximum, so the plays are not identical.

## `minWatchPercent` (type: `integer`):

Used when the watch time mode is Percent of video length.

## `maxWatchPercent` (type: `integer`):

Watching a larger share of the video takes proportionally more time and proxy traffic.

## `trafficSource` (type: `string`):

How each view reaches the video. Search finds it in YouTube results and clicks the result; External arrives with a referrer from another site.

## `searchKeyword` (type: `string`):

Required for the YouTube search traffic source. Use a phrase your video actually ranks for — if it is not in the first results the view falls back to opening the link directly.

## `referer` (type: `string`):

Required for the External traffic source. The address the view should appear to come from.

## `lowestQuality` (type: `boolean`):

Pin playback to 144p. Video is the largest source of proxy traffic and a view counts the same at any resolution, so leaving this on cuts cost sharply.

## `skipAds` (type: `boolean`):

Press Skip as soon as it appears. Time spent on an ad is not counted as watch time either way.

## `device` (type: `string`):

Device profile for the browser fingerprint.

## `delayBetweenViewsSecs` (type: `integer`):

Seconds to wait before starting another view. Spacing plays out looks more natural than a burst.

## `maxRetries` (type: `integer`):

How many times to retry a view on a different proxy IP when YouTube refuses the request or the stream stalls. Retries are what turn a bad proxy address into a delivered view, so lowering this mostly lowers the success rate.

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

Residential proxies are strongly recommended: without them every view arrives from the same address and looks like a single viewer.

## Actor input object example

```json
{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "views": 5,
  "concurrency": 2,
  "watchMode": "seconds",
  "minWatchSeconds": 30,
  "maxWatchSeconds": 90,
  "minWatchPercent": 30,
  "maxWatchPercent": 70,
  "trafficSource": "direct",
  "lowestQuality": true,
  "skipAds": true,
  "device": "desktop",
  "delayBetweenViewsSecs": 0,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# 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 = {
    "videoUrls": [
        {
            "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
        }
    ],
    "views": 5,
    "concurrency": 2,
    "minWatchSeconds": 30,
    "maxWatchSeconds": 90,
    "minWatchPercent": 30,
    "maxWatchPercent": 70,
    "delayBetweenViewsSecs": 0,
    "maxRetries": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("one_house/youtube-view-generator").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 = {
    "videoUrls": [{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }],
    "views": 5,
    "concurrency": 2,
    "minWatchSeconds": 30,
    "maxWatchSeconds": 90,
    "minWatchPercent": 30,
    "maxWatchPercent": 70,
    "delayBetweenViewsSecs": 0,
    "maxRetries": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("one_house/youtube-view-generator").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 '{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "views": 5,
  "concurrency": 2,
  "minWatchSeconds": 30,
  "maxWatchSeconds": 90,
  "minWatchPercent": 30,
  "maxWatchPercent": 70,
  "delayBetweenViewsSecs": 0,
  "maxRetries": 2
}' |
apify call one_house/youtube-view-generator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,one_house/youtube-view-generator"
        }
    }
}

```

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/fJp1Ck6KQnlfH9iuG/builds/xYcg3qxIy3vnHxcFt/openapi.json
