# YouTube Gap Finder (`moillamas/youtube-gap-finder`) Actor

Finds underserved content topics in a YouTube niche — high competitor views, low supply. Ideal for creators looking for their next video idea.

- **URL**: https://apify.com/moillamas/youtube-gap-finder.md
- **Developed by:** [moises llamas](https://apify.com/moillamas) (community)
- **Categories:** Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

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

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.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/platform/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 Gap Finder

Finds underserved content topics in any YouTube niche — topics where competitors get high views but the overall supply of videos is low.

### What it does

This Actor analyses YouTube competitor channels and niche search results to identify **content gaps**: topics with proven audience demand (high competitor views) but low competition (few videos/channels covering them).

Each result includes:

- **Topic** — 2-3 word phrase representing the gap
- **Opportunity score** — 0–10 combining demand signal and competition level
- **Competition level** — `low`, `medium`, or `high`
- **Suggested video title** — a ready-to-use title for the gap
- **Example competitor titles** — existing videos that prove demand

### Sample output

```json
{
  "topic": "budgeting apps review",
  "opportunity_score": 7.8,
  "competition": "low",
  "avg_niche_views": 420000,
  "competitor_videos_on_topic": 3,
  "niche_videos_on_topic": 1,
  "niche_channels_on_topic": 1,
  "suggested_title": "Best Budgeting Apps Review 2026 (The Underrated Guide)",
  "example_competitor_titles": [
    "Best budgeting apps review 2026",
    "Top 5 budgeting apps for beginners"
  ]
}
````

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `niche` | string | `""` | The content niche (e.g. `personal finance`, `Python programming`) |
| `competitor_channels` | array | `[]` | YouTube channel URLs to analyse as competitors |
| `niche_search_queries` | array | `[]` | Extra search queries to build the competition dataset |
| `max_competitor_videos` | integer | `30` | Max videos to fetch per competitor channel |
| `max_niche_videos` | integer | `50` | Total niche search videos for competition analysis |
| `min_opportunity_score` | number | `4.0` | Minimum score to include in results |
| `max_results` | integer | `30` | Maximum gaps to return |

### How it works

1. **Competitor analysis** — fetches recent videos from the provided competitor channels using yt-dlp
2. **Topic extraction** — extracts 2-3 word topic phrases from competitor video titles, filtering stopwords
3. **Competition mapping** — for each topic, counts how many videos and channels in the broader niche cover it
4. **Gap scoring** — scores each topic: `demand (avg competitor views) + low competition bonus → 0-10`
5. **Output** — returns gaps sorted by opportunity score with a suggested title for each

### Use cases

- **Content creators** — find your next video idea before the niche gets saturated
- **YouTube strategists** — identify whitespace in a client's niche
- **Agencies** — audit competitor coverage vs. niche supply for channel growth reports
- **Marketers** — discover high-demand, low-competition topics for YouTube ads targeting

### Algorithm details

**Opportunity score formula:**

- Demand (0–5): `min(avg_niche_views / 100,000, 1.0) × 5`
- Competition bonus (0–5): `4.0` for 0 niche videos, down to `0.0` for 10+ niche videos
- Final score capped at 10.0

**Competition level:**

- `low` — ≤1 channel AND ≤3 videos on the topic
- `medium` — ≤3 channels OR ≤8 videos
- `high` — more than the above

### Running locally

```bash
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python main.py
```

Custom input:

```bash
ACTOR_INPUT='{"niche":"personal finance","competitor_channels":["https://www.youtube.com/@GrahamStephan"],"max_competitor_videos":20}' python main.py
```

### Notes

- Uses yt-dlp to fetch YouTube metadata — no API key required
- Channel videos are fetched in flat/extract mode (no downloads)
- yt-dlp must be kept up to date (`pip install -U yt-dlp`) as YouTube periodically changes its internal API
- A ~1s delay is applied between requests to avoid rate limiting

# Actor input Schema

## `niche` (type: `string`):

The content niche to analyse (e.g. 'personal finance', 'Python programming', 'vegan cooking').

## `competitor_channels` (type: `array`):

YouTube channel URLs to analyse as competitors (e.g. https://www.youtube.com/@MrBeast). If empty, the top niche search results are used as a proxy.

## `niche_search_queries` (type: `array`):

Extra search queries to build the competition dataset. If empty, queries are auto-generated from 'niche'.

## `max_competitor_videos` (type: `integer`):

How many recent videos to fetch per competitor channel.

## `max_niche_videos` (type: `integer`):

Total videos to fetch from niche search queries (competition dataset size).

## `min_opportunity_score` (type: `number`):

Only return gaps with a score >= this value (0–10).

## `max_results` (type: `integer`):

Maximum number of content gaps to return.

## Actor input object example

```json
{
  "niche": "personal finance",
  "competitor_channels": [
    "https://www.youtube.com/@GrahamStephan",
    "https://www.youtube.com/@AndreiJikh"
  ],
  "niche_search_queries": [],
  "max_competitor_videos": 50,
  "max_niche_videos": 80,
  "min_opportunity_score": 4,
  "max_results": 30
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("moillamas/youtube-gap-finder").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("moillamas/youtube-gap-finder").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 '{}' |
apify call moillamas/youtube-gap-finder --silent --output-dataset

```

## MCP server setup

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

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "YouTube Gap Finder",
        "description": "Finds underserved content topics in a YouTube niche — high competitor views, low supply. Ideal for creators looking for their next video idea.",
        "version": "0.1",
        "x-build-id": "OkiTwAyPU214Ma7IK"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/moillamas~youtube-gap-finder/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-moillamas-youtube-gap-finder",
                "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/moillamas~youtube-gap-finder/runs": {
            "post": {
                "operationId": "runs-sync-moillamas-youtube-gap-finder",
                "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/moillamas~youtube-gap-finder/run-sync": {
            "post": {
                "operationId": "run-sync-moillamas-youtube-gap-finder",
                "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",
                "properties": {
                    "niche": {
                        "title": "Niche",
                        "type": "string",
                        "description": "The content niche to analyse (e.g. 'personal finance', 'Python programming', 'vegan cooking').",
                        "default": ""
                    },
                    "competitor_channels": {
                        "title": "Competitor channel URLs",
                        "type": "array",
                        "description": "YouTube channel URLs to analyse as competitors (e.g. https://www.youtube.com/@MrBeast). If empty, the top niche search results are used as a proxy.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "niche_search_queries": {
                        "title": "Additional niche search queries",
                        "type": "array",
                        "description": "Extra search queries to build the competition dataset. If empty, queries are auto-generated from 'niche'.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "max_competitor_videos": {
                        "title": "Max videos per competitor channel",
                        "minimum": 5,
                        "maximum": 200,
                        "type": "integer",
                        "description": "How many recent videos to fetch per competitor channel.",
                        "default": 50
                    },
                    "max_niche_videos": {
                        "title": "Max niche search videos",
                        "minimum": 10,
                        "maximum": 200,
                        "type": "integer",
                        "description": "Total videos to fetch from niche search queries (competition dataset size).",
                        "default": 80
                    },
                    "min_opportunity_score": {
                        "title": "Minimum opportunity score",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "number",
                        "description": "Only return gaps with a score >= this value (0–10).",
                        "default": 4
                    },
                    "max_results": {
                        "title": "Max results",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Maximum number of content gaps to return.",
                        "default": 30
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
