# YouTube Competitor Intelligence & Content Gap Analyzer (`feeng/youtube-channel-competitor-intelligence`) Actor

📺 Compare YouTube channels, rank videos, reveal content gaps, monitor growth signals, and export decision-ready competitor intelligence without a YouTube API key.

- **URL**: https://apify.com/feeng/youtube-channel-competitor-intelligence.md
- **Developed by:** [Daniel S.](https://apify.com/feeng) (community)
- **Categories:** Videos, Social media, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 analyzed youtube 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/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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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 Channel Competitor Intelligence & Content Gap Analyzer

Compare public YouTube channels, rank recent videos, reveal content whitespace,
and monitor observable changes without a YouTube Data API key.

This self-contained Apify Actor turns public YouTube channel HTML, embedded JSON,
video pages, caption tracks, and RSS feeds into decision-ready competitive
intelligence for creators, content agencies, media teams, sponsorship teams, and
brands.

### What it delivers

- 🏆 **Competitor leaderboard** with relative channel strength, publishing
  cadence, average views, view velocity, engagement proxy, formats, and topics.
- 🎬 **Video performance table** with title, URL, date, views, likes, comments,
  duration, keywords, topics, estimated views/day, and a 0-100 performance score.
- 💡 **Content gap analysis** showing which topics perform for some competitors
  but are missing from others.
- 📈 **Monitoring changes** including new videos and observable view,
  like, comment, subscriber, and cadence deltas versus the previous run.
- 📝 **Public transcripts** when requested and exposed by YouTube, returned on a
  best-effort basis.
- 📊 **JSON, CSV, Apify Dataset views, and a visual HTML report** ready for
  spreadsheets, dashboards, Make, Zapier, n8n, webhooks, or custom APIs.

No paid data provider, YouTube API credential, browser cookie, or third-party
Apify Actor is required.

### Quick start

Use two to five comparable channels for the clearest content-gap analysis:

```json
{
  "channelUrls": [
    "https://www.youtube.com/@veritasium",
    "https://www.youtube.com/@mkbhd",
    "https://www.youtube.com/@LinusTechTips"
  ],
  "maxVideosPerChannel": 20,
  "lookbackDays": 90,
  "keywords": [
    "artificial intelligence",
    "AI tools",
    "robotics"
  ],
  "includeVideoDetails": true,
  "includeTranscripts": false,
  "historyKey": "tech-creators-weekly",
  "resetHistory": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
````

Start the same input weekly with the same `historyKey` to unlock change
monitoring. Set `resetHistory` to `true` once when you intentionally want a new
baseline.

### Inputs

| Field | Required | Default | Purpose |
|---|---:|---:|---|
| `channelUrls` | Yes | Example channels | One or more YouTube channel URLs, `/channel/` IDs, or `@handles` |
| `maxVideosPerChannel` | No | `20` | Maximum retained recent videos per channel |
| `lookbackDays` | No | `90` | Exclude known dates older than this; `0` disables |
| `keywords` | No | `[]` | Strategic topics used for coverage and gap analysis |
| `includeVideoDetails` | No | `true` | Fetch public watch pages for richer metrics |
| `includeTranscripts` | No | `false` | Try public caption tracks; unavailable transcripts stay `null` |
| `historyKey` | No | `default` | Stable identity for a recurring competitor watchlist |
| `resetHistory` | No | `false` | Delete the prior baseline before this run |
| `requestTimeoutSecs` | No | `30` | Per-request timeout |
| `retryCount` | No | `2` | Retries for transient failures and rate limits |
| `maxConcurrency` | No | `4` | Concurrent video detail requests |
| `proxyConfiguration` | No | Direct | Optional Apify or custom proxy configuration |

#### Fast versus detailed runs

Use `includeVideoDetails: false` for a quick channel/RSS scan. Likes, comments,
exact duration, metadata keywords, and captions will usually be unavailable.

Use `includeVideoDetails: true` for the commercial intelligence workflow. It
adds one public watch-page request per retained candidate video and enriches any
metrics YouTube exposes.

Transcripts are intentionally disabled by default because availability varies
by channel, video, language, consent, geography, and rate limits.

### Output structure

The default dataset contains four explicit `rowType` values:

| `rowType` | Meaning | Charged PPE event |
|---|---|---|
| `channel` | One intelligence summary for each successfully analyzed channel | `channel-analysis` |
| `video` | One recent video with evidence and calculated metrics | `video-analysis` |
| `content_gap` | Derived topic opportunity with supporting evidence | Included |
| `change` | Difference from the previous successful history snapshot | Included |

Practical dataset views are provided:

1. **🏆 Competitor leaderboard**
2. **🎬 Video performance**
3. **💡 Content opportunities**
4. **📈 Changes since previous run**
5. **All structured data**

#### Example video row

```json
{
  "rowType": "video",
  "status": "partial",
  "channelTitle": "Example Science",
  "videoId": "abc123",
  "videoTitle": "Quantum AI Explained",
  "videoUrl": "https://www.youtube.com/watch?v=abc123",
  "publishedAt": "2026-07-20T10:00:00.000Z",
  "views": 130000,
  "likes": 6500,
  "comments": 420,
  "durationSeconds": 754,
  "format": "standard",
  "viewVelocityPerDay": 65000,
  "engagementProxyPercent": 5.3231,
  "performanceScore": 88,
  "matchedKeywords": ["artificial intelligence"],
  "detectedTopics": ["artificial intelligence", "quantum computing"],
  "transcript": null,
  "transcriptStatus": "not_requested",
  "dataSources": ["channel_html", "rss", "video_html"],
  "dataLimitations": []
}
```

#### Example content opportunity

```json
{
  "rowType": "content_gap",
  "status": "ok",
  "opportunityScore": 78,
  "topic": "quantum computing",
  "gapType": "cross_channel_whitespace",
  "channelsUsingTopic": ["Example Science"],
  "channelsMissingTopic": ["Example Tech"],
  "supportingVideoCount": 3,
  "averageTopicViewVelocity": 22400,
  "supportingVideoUrls": [
    "https://www.youtube.com/watch?v=abc123"
  ],
  "rationale": "3 supporting video(s) across 1/2 channel(s), 22400 average views/day; missing from 1 competitor channel(s)."
}
```

These examples demonstrate structure only. They are not claims about live
channels.

### How the metrics work

#### View velocity

`viewVelocityPerDay = public view count / time since publication`

The minimum denominator is one hour to avoid unstable division immediately
after publication. It is an estimate from public counters, not private YouTube
Analytics.

#### Engagement proxy

`engagementProxyPercent = (available likes + available comments) / views × 100`

The `engagementSignalsAvailable` array states which public signals were used.
If neither likes nor comments is exposed, the value is `null`.

#### Publishing cadence

Cadence uses the median interval between videos with known publish dates. This
is more robust than dividing an incomplete initial-page sample by the full
lookback window.

#### Performance score

The deterministic 0-100 score compares a video only with videos in the current
run:

- 45 points: relative view velocity
- 25 points: relative views
- 20 points: relative public engagement proxy
- 10 points: freshness

Missing metrics contribute zero rather than fabricated values.

#### Opportunity score

The deterministic 0-100 content-gap score combines:

- observed topic view velocity,
- observable engagement,
- whitespace across compared channels,
- repeated evidence,
- freshness,
- an explicit boost for user-supplied strategic keywords.

A requested keyword with no observed supporting video is clearly labeled
`requested_keyword_without_observed_coverage`; its demand metrics stay `null`.
The score prioritizes research. It does not guarantee future performance.

### History and monitoring

History is stored in the named key-value store
`youtube-competitor-intelligence-history`. The user-facing `historyKey` is
hashed into a stable record key, so multiple watchlists can coexist.

The compact snapshot stores only:

- channel identity and public subscriber count,
- cadence,
- retained video IDs, titles, URLs, dates, and public counters,
- capture timestamp.

When a video disappears from the recent sample, the Actor reports it as
`noLongerInWindowCount`. It does **not** claim the video was deleted.

### Reliability and source policy

The Actor uses:

1. public channel HTML and `ytInitialData`,
2. the public YouTube channel RSS feed as fallback and date enrichment,
3. public watch-page `ytInitialPlayerResponse` and `ytInitialData`,
4. a public caption-track URL when requested and exposed.

Requests have bounded timeouts, retries with backoff, rotating proxy URLs when
configured, per-channel isolation, and source-specific status fields.

YouTube controls its public markup and may change or withhold fields. The Actor
therefore follows these rules:

- unavailable data is `null`,
- source text is preserved where useful,
- every video lists `dataSources` and `dataLimitations`,
- one failed video detail never discards its channel/RSS metadata,
- one failed channel does not discard successful competitors,
- the run fails only when no channel yields usable public data.

Use this Actor only for lawful purposes and in accordance with applicable
terms, privacy rules, and rate limits. Do not use it to collect private,
restricted, or personal data.

### Run locally

Node.js 20 or newer is required.

```bash
npm install
export APIFY_INPUT_KEY=INPUT
npm test
npm run smoke
npm start
```

For a local Actor run, place input at
`storage/key_value_stores/default/INPUT.json`, or use the Apify CLI:

```bash
npx --yes apify-cli run --purge --input-file examples/input-fast.json
```

Run the live smoke test against another public channel:

```bash
YOUTUBE_CHANNEL=@mkbhd npm run smoke
```

### Run on Apify

```bash
npx apify-cli login
npx apify-cli push
```

Then:

1. Open the Actor's **Input** tab.
2. Add comparable channels.
3. Start with 10-20 videos and no transcripts.
4. Inspect the dataset and report.
5. Save the input as a Task.
6. Schedule it weekly with the same `historyKey`.
7. Connect a webhook, Make, Zapier, or n8n to consume change rows.

See [APIFY\_PUBLISHING\_CHECKLIST.md](APIFY_PUBLISHING_CHECKLIST.md) for the exact
Store and monetization steps.

### Pricing

The launch price uses pay-per-event billing:

- `apify-actor-start`: **$0.00005** once per run
- `video-analysis`: **$0.002** per successfully exported video
- `channel-analysis`: **$0.02** per successfully exported channel

Content-gap rows, historical changes, JSON/CSV exports, and the HTML report are included. Two channels with ten videos each cost about **$0.08005**. Failed sources do not create channel or video charge events.

See [`PRICING.md`](PRICING.md) for larger examples and margin guardrails.

### Saved records

The default key-value store contains:

- `OUTPUT.json` - complete structured intelligence output
- `OUTPUT.csv` - flattened export
- `REPORT.json` - report-ready structured data
- `REPORT.html` - visual decision report
- `RUN_SUMMARY.json` - counts, configuration, and source policy
- `ERRORS.json` - isolated channel/source failures
- `STATE_SNAPSHOT.json` - compact state saved for the next run

### Cost control

- Start with 10 videos per channel.
- Leave transcripts off unless they directly support the workflow.
- Use direct requests first; add Apify Proxy only when reliability requires it.
- Keep concurrency at four or lower for public YouTube pages.
- Increase the video limit only after confirming the channel exposes enough
  recent items.

See [PRICING.md](PRICING.md) for the proposed PPE economics.

### Development

```bash
npm test
npm run validate
npm run smoke
```

Fixtures cover channel HTML, RSS, video details, captions, input validation,
scoring, content gaps, state snapshots, and change detection.

### Known limitations

- Initial public HTML and RSS do not provide a guaranteed full channel archive.
- YouTube can hide subscriber, like, comment, view, duration, or date fields.
- Public counts can be rounded.
- Captions may be missing or blocked.
- Shorts detection is based on public URL/rendering and duration evidence.
- Private YouTube Analytics metrics such as impressions, click-through rate,
  watch time, retention, traffic source, and revenue are unavailable.
- Deleted/private videos cannot be reliably distinguished from videos that left
  the retained recent sample.

# Actor input Schema

## `channelUrls` (type: `array`):

Required. Add public YouTube channel URLs, /channel/ IDs, or @handles. Duplicates are removed. Example: https://www.youtube.com/@veritasium

## `maxVideosPerChannel` (type: `integer`):

Maximum videos retained per channel after merging public HTML and RSS results. YouTube may expose fewer items in its initial public page.

## `lookbackDays` (type: `integer`):

Videos with a known publish date older than this window are excluded. Use 0 to disable the date filter. Videos with an unavailable date are retained and clearly marked.

## `keywords` (type: `array`):

Optional. Add target topics, products, audience problems, or formats. They are used for coverage and content-gap analysis, not as a strict video filter.

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

Recommended. Fetch each public watch page for exact duration, metadata keywords, likes/comments when exposed, and richer evidence. Disable for a faster metadata-only run.

## `includeTranscripts` (type: `boolean`):

Try to download a public caption track exposed by the video page. Transcripts can be unavailable, disabled, auto-generated, rate-limited, or language-dependent; unavailable values remain null.

## `historyKey` (type: `string`):

Stable identifier for this competitor set, for example ai-tools-weekly. Reuse it in scheduled runs to compare with the previous successful snapshot.

## `resetHistory` (type: `boolean`):

Delete the previous baseline for this history key, then create a fresh baseline.

## `requestTimeoutSecs` (type: `integer`):

Maximum time for one YouTube HTML, RSS, detail, or caption request.

## `retryCount` (type: `integer`):

Retry count for transient network errors, HTTP 429, and server errors.

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

Lower this when running without a proxy or when YouTube begins rate-limiting requests.

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

Optional Apify Proxy or custom proxy URLs. Direct requests are used by default. Residential proxies may improve reliability for larger runs.

## Actor input object example

```json
{
  "channelUrls": [
    "https://www.youtube.com/@veritasium",
    "https://www.youtube.com/@mkbhd"
  ],
  "maxVideosPerChannel": 20,
  "lookbackDays": 90,
  "keywords": [],
  "includeVideoDetails": true,
  "includeTranscripts": false,
  "historyKey": "default",
  "resetHistory": false,
  "requestTimeoutSecs": 30,
  "retryCount": 2,
  "maxConcurrency": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `competitorLeaderboard` (type: `string`):

Ranked channel metrics, formats, topics, cadence, and opportunity.

## `videoPerformance` (type: `string`):

Recent video metrics, velocity, engagement proxies, topics, and deltas.

## `contentOpportunities` (type: `string`):

Ranked content gaps backed by observed competitor evidence.

## `changes` (type: `string`):

New uploads and observable metric changes for this history key.

## `allData` (type: `string`):

No description

## `intelligenceJson` (type: `string`):

No description

## `htmlReport` (type: `string`):

No description

## `reportJson` (type: `string`):

No description

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

No description

## `errors` (type: `string`):

No description

## `stateSnapshot` (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 = {
    "channelUrls": [
        "https://www.youtube.com/@veritasium",
        "https://www.youtube.com/@mkbhd"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("feeng/youtube-channel-competitor-intelligence").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 = { "channelUrls": [
        "https://www.youtube.com/@veritasium",
        "https://www.youtube.com/@mkbhd",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("feeng/youtube-channel-competitor-intelligence").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "channelUrls": [
    "https://www.youtube.com/@veritasium",
    "https://www.youtube.com/@mkbhd"
  ]
}' |
apify call feeng/youtube-channel-competitor-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=feeng/youtube-channel-competitor-intelligence",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "YouTube Competitor Intelligence & Content Gap Analyzer",
        "description": "📺 Compare YouTube channels, rank videos, reveal content gaps, monitor growth signals, and export decision-ready competitor intelligence without a YouTube API key.",
        "version": "0.1",
        "x-build-id": "VjMTJKi57SPU1zLpe"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/feeng~youtube-channel-competitor-intelligence/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-feeng-youtube-channel-competitor-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/feeng~youtube-channel-competitor-intelligence/runs": {
            "post": {
                "operationId": "runs-sync-feeng-youtube-channel-competitor-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/feeng~youtube-channel-competitor-intelligence/run-sync": {
            "post": {
                "operationId": "run-sync-feeng-youtube-channel-competitor-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "channelUrls"
                ],
                "properties": {
                    "channelUrls": {
                        "title": "📺 Competitor channel URLs or @handles",
                        "minItems": 1,
                        "maxItems": 20,
                        "type": "array",
                        "description": "Required. Add public YouTube channel URLs, /channel/ IDs, or @handles. Duplicates are removed. Example: https://www.youtube.com/@veritasium",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "https://www.youtube.com/@veritasium",
                            "https://www.youtube.com/@mkbhd"
                        ]
                    },
                    "maxVideosPerChannel": {
                        "title": "🎬 Maximum recent videos per channel",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Maximum videos retained per channel after merging public HTML and RSS results. YouTube may expose fewer items in its initial public page.",
                        "default": 20
                    },
                    "lookbackDays": {
                        "title": "🗓️ Analyze videos from the last N days",
                        "minimum": 0,
                        "maximum": 3650,
                        "type": "integer",
                        "description": "Videos with a known publish date older than this window are excluded. Use 0 to disable the date filter. Videos with an unavailable date are retained and clearly marked.",
                        "default": 90
                    },
                    "keywords": {
                        "title": "🔎 Strategic keywords and topics",
                        "maxItems": 50,
                        "type": "array",
                        "description": "Optional. Add target topics, products, audience problems, or formats. They are used for coverage and content-gap analysis, not as a strict video filter.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "includeVideoDetails": {
                        "title": "Include individual video detail pages",
                        "type": "boolean",
                        "description": "Recommended. Fetch each public watch page for exact duration, metadata keywords, likes/comments when exposed, and richer evidence. Disable for a faster metadata-only run.",
                        "default": true
                    },
                    "includeTranscripts": {
                        "title": "📝 Include public transcripts (best effort)",
                        "type": "boolean",
                        "description": "Try to download a public caption track exposed by the video page. Transcripts can be unavailable, disabled, auto-generated, rate-limited, or language-dependent; unavailable values remain null.",
                        "default": false
                    },
                    "historyKey": {
                        "title": "📈 History key",
                        "minLength": 1,
                        "maxLength": 100,
                        "type": "string",
                        "description": "Stable identifier for this competitor set, for example ai-tools-weekly. Reuse it in scheduled runs to compare with the previous successful snapshot.",
                        "default": "default"
                    },
                    "resetHistory": {
                        "title": "Reset saved history before this run",
                        "type": "boolean",
                        "description": "Delete the previous baseline for this history key, then create a fresh baseline.",
                        "default": false
                    },
                    "requestTimeoutSecs": {
                        "title": "Request timeout in seconds",
                        "minimum": 10,
                        "maximum": 120,
                        "type": "integer",
                        "description": "Maximum time for one YouTube HTML, RSS, detail, or caption request.",
                        "default": 30
                    },
                    "retryCount": {
                        "title": "Retries per request",
                        "minimum": 0,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Retry count for transient network errors, HTTP 429, and server errors.",
                        "default": 2
                    },
                    "maxConcurrency": {
                        "title": "Concurrent video detail requests",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Lower this when running without a proxy or when YouTube begins rate-limiting requests.",
                        "default": 4
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Optional Apify Proxy or custom proxy URLs. Direct requests are used by default. Residential proxies may improve reliability for larger runs.",
                        "default": {
                            "useApifyProxy": false
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
