# YouTube Channel Feed Scraper: New Uploads, No API Key (`arman-bd/youtube-channel-feed-scraper`) Actor

Monitor YouTube channels through official RSS feeds: video title, ID, publish date, description, thumbnail and channel details. No API key and no daily quota.

- **URL**: https://apify.com/arman-bd/youtube-channel-feed-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Social media, Marketing, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 upload scrapeds

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 Channel Feed Scraper: New Uploads, no credentials

![YouTube Channel Feed: New uploads from any channel: title, publish date, description and thumbnail](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/youtube-channel-feed-scraper.jpg)

**YouTube Channel Feed Scraper** monitors **YouTube** channels through their own official RSS feeds, video title, ID, publish date, description, thumbnail, view count and channel details, with **no credentials and no daily quota**.

YouTube publishes a channel and playlist feed that is publisher-sanctioned, and unlike the official Data API it costs you no daily quota. This Actor reads it directly: **no browser, no proxies, no login.**

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/youtube-channel-feed-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/youtube-channel-feed-scraper.md
```

### What you get

| Output field | Meaning |
|---|---|
| `channelId`, `channelTitle` | Channel the video belongs to, read per video, so playlists spanning many channels stay correct |
| `videoId`, `title` | Video identifier and title |
| `description` | Full description as published in the feed |
| `publishedAt`, `updatedAt` | Upload timestamp and last feed update |
| `videoUrl` | Canonical link, `/watch?v=` or `/shorts/` as YouTube reports it |
| `thumbnailUrl` | `hqdefault` thumbnail |
| `viewCount` | Views at the moment the feed was read |
| `rating` | `{ average, count }` from the feed's star rating |
| `scrapedAt` | Run timestamp |

A `RUN_SUMMARY` record in the key-value store holds per-run counts, the filters used, and any channel that failed.

### Common use cases

- **Competitor alerts.** Run on a schedule and diff on `videoId` to catch new uploads within minutes.
- **Newsletter and digest building.** A curated feed of every creator you follow, filtered by keyword.
- **Brand monitoring.** Watch for uploads mentioning your product in the title.
- **Content research.** Track upload cadence and early view velocity across a set of channels.
- **Quota-free pipelines.** Replace YouTube Data API polling that keeps hitting its daily unit cap.

### Quick start

Two channels, everything in their feeds:

```json
{
 "channels": ["@mkbhd", "UCXuqSBlHAE6Xw-yeJA0Tunw"]
}
```

Recent uploads matching a keyword, capped:

```json
{
 "channels": ["@LinusTechTips", "https://www.youtube.com/@mkbhd"],
 "sinceDate": "2026-07-01",
 "keywordFilter": ["review", "unboxing"],
 "maxVideosPerChannel": 5
}
```

A playlist instead of a channel:

```json
{
 "channels": ["PLj8e95eaxiB9goOAvINIy4Vt3mlWQJxys"]
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `channels` | array | - | **Required.** Channel IDs (`UC…`), `@handles`, playlist IDs (`PL…`), or any youtube.com URL wrapping one of those. Mixed input is fine. |
| `maxVideosPerChannel` | integer | `0` | Cap saved videos per channel **after** filtering. `0` = no limit. |
| `sinceDate` | string | - | Keep only videos published on or after this date. `YYYY-MM-DD` or a full ISO timestamp. |
| `keywordFilter` | array | `[]` | Keep only titles containing one of these terms (case-insensitive). Empty = all. |

Filters combine with AND: a video must be new enough *and* match a keyword.

### Output example

```json
{
 "channelId": "UCBJycsmduvYEL83R_U4JriQ",
 "channelTitle": "Marques Brownlee",
 "videoId": "_xjxwl1zLMc",
 "title": "Framework 13 Pro: The Modular Laptop is Real!",
 "description": "The modular Macbook Pro for Linux users. Color me impressed.\n\nMKBHD Merch: http://shop.MKBHD.com…",
 "publishedAt": "2026-07-27T15:38:21+00:00",
 "updatedAt": "2026-08-06T05:26:11+00:00",
 "videoUrl": "https://www.youtube.com/watch?v=_xjxwl1zLMc",
 "thumbnailUrl": "https://i4.ytimg.com/vi/_xjxwl1zLMc/hqdefault.jpg",
 "viewCount": 2983132,
 "rating": { "average": 5, "count": 95523 },
 "scrapedAt": "2026-08-06T11:47:09.151Z"
}
```

### Finding a channel ID

| What you have | What to paste |
|---|---|
| `youtube.com/@mkbhd` | `@mkbhd`, resolved automatically |
| `youtube.com/channel/UC…` | The whole URL, or just the `UC…` part |
| `youtube.com/user/LinusTechTips` | The whole URL, legacy names are resolved too |
| A playlist URL with `?list=PL…` | The whole URL, or just the `PL…` part |

Handles and vanity names are resolved by reading the canonical channel link off the channel page, once per run and then cached.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~youtube-channel-feed-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "channels": ["@mkbhd"],
 "maxVideosPerChannel": 5
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/youtube-channel-feed-scraper').call({
 channels: ['@mkbhd', '@LinusTechTips'],
 sinceDate: '2026-07-01',
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const v of items) console.log(`${v.channelTitle}, ${v.title} (${v.viewCount} views)`);
```

### Notes

- **Feeds hold the latest 15 uploads only.** That is YouTube's limit, not the Actor's. For continuous coverage, schedule the run more often than the channel publishes.
- **Bad channels don't kill the run.** An unknown ID returns 404 and is recorded in `RUN_SUMMARY.failures`; the Actor only errors out if *every* channel fails.
- **The channel ID is read per entry.** The feed-level `<yt:channelId>` drops the `UC` prefix, a long-standing quirk, so the per-video value and the canonical channel link are used instead. Playlist feeds spanning several creators therefore attribute each video to its real channel.
- **Shorts keep their URL.** `videoUrl` is whatever YouTube marks canonical, so a short stays `/shorts/…` rather than being rewritten.
- **Entities are decoded twice.** Atom escapes every reserved character and YouTube double-encodes some ampersands, so titles and descriptions come out as real text, not `&amp;quot;`.
- **Transient errors are retried.** 429 and 5xx get four attempts with exponential backoff and jitter.
- **No dependencies.** The feed schema is fixed and tiny, so parsing is done in-house rather than pulling in an XML library.
- **Public data only.** No authentication, no personal data, no access-control bypass.

### FAQ

**Do I need a YouTube API key?** No. You supply no credentials.

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Can I get more than 15 videos per channel?** Not from the feed. That is its hard limit. Schedule regular runs and accumulate into a dataset instead.

**Does it include likes or comments?** The feed exposes `viewCount` and a star `rating` only. Comment counts are not published there.

**Why is `viewCount` lower than on the video page?** The feed's counters refresh on YouTube's own schedule and lag the page slightly.

**What happens if a channel is unavailable?** It is reported in `RUN_SUMMARY.failures` and the run continues.

**Can I schedule it?** Yes, it is designed for scheduled runs. Diff on `videoId` to alert only on new uploads.

**Can I integrate it with something else?** Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.

# Actor input Schema

## `channels` (type: `array`):

Channel IDs (UC…), @handles, playlist IDs (PL…) or any youtube.com URL wrapping one of those. Handles and legacy /user/ names are resolved to channel IDs automatically.

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

Cap the number of videos saved per channel after filtering. Set 0 for no limit. The feed itself only ever holds the latest 15 uploads.

## `sinceDate` (type: `string`):

Keep only videos published on or after this date. Accepts YYYY-MM-DD or a full ISO timestamp. Leave empty for everything in the feed.

## `keywordFilter` (type: `array`):

Keep only videos whose title contains at least one of these terms (case-insensitive). Leave empty to keep every video.

## Actor input object example

```json
{
  "channels": [
    "@mkbhd",
    "https://www.youtube.com/channel/UCXuqSBlHAE6Xw-yeJA0Tunw"
  ],
  "maxVideosPerChannel": 0,
  "sinceDate": "2026-01-01",
  "keywordFilter": [
    "review",
    "unboxing"
  ]
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN\_SUMMARY record from the run's key-value store.

# 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 = {
    "channels": [
        "@LinusTechTips",
        "UCXuqSBlHAE6Xw-yeJA0Tunw"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/youtube-channel-feed-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "channels": [
        "@LinusTechTips",
        "UCXuqSBlHAE6Xw-yeJA0Tunw",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/youtube-channel-feed-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "channels": [
    "@LinusTechTips",
    "UCXuqSBlHAE6Xw-yeJA0Tunw"
  ]
}' |
apify call arman-bd/youtube-channel-feed-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/youtube-channel-feed-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2iU7cWfOUp3wKRGGw/builds/sKMA97opvU2ob6K8I/openapi.json
