# YouTube Community Post & Poll Scraper (`fanndev/youtube-community-posts-scraper`) Actor

Scrape the YouTube Community tab that video-metric scrapers ignore. Returns each post's full text, like count, publish time, outbound links (unwrapped from YouTube's redirect), attached images and videos, plus poll options and total votes. No login.

- **URL**: https://apify.com/fanndev/youtube-community-posts-scraper.md
- **Developed by:** [Faisal Ahdan naufal](https://apify.com/fanndev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Community Post & Poll Scraper

Scrape the YouTube tab that every video-metric scraper ignores.

Give it channel handles. It returns each community post's **full text, like
count, publish time, outbound links, attached images, attached videos, and poll
questions with their options and total votes**.

This is audience-research material no other YouTube endpoint carries: what a
creator asks their audience, what they promote between uploads, and which posts
land.

No login, no cookies, no API key.

### What you get

```json
{
  "channelHandle": "@veritasium",
  "feedPosition": 1,
  "postId": "UgkxtIWAwjGL7mY_dtJC9fQX",
  "text": "Mayor of New York City, Zohran Mamdani, releases files exposing that New Yorkers were lied to about the quality of the air after the September 11th attacks…",
  "likeCountText": "1.3K",
  "likeCountApprox": 1300,
  "publishedTimeText": "8 days ago",
  "isEdited": false,
  "links": ["https://sept11documents.cityofnewyork.us/"],
  "attachmentType": "video",
  "video": {
    "videoId": "cMx139eTxoc",
    "title": "What does asbestos do to a person?",
    "url": "https://www.youtube.com/watch?v=cMx139eTxoc"
  }
}
```

Links are **unwrapped**: YouTube rewrites every outbound URL in a post to
`youtube.com/redirect?…&q=<real url>`. You get the destination, not the tracker.

### Attachment types

| `attachmentType` | What you get |
| --- | --- |
| `multi-image` | Up to 10 image URLs, highest resolution available (measured up to 5464×5464) |
| `image` | One image URL |
| `video` | `videoId`, title, URL, view-count text |
| `poll` | Options, total votes, poll type |
| `none` | Text-only post |

### Two limits, stated up front

**1. Like counts are abbreviated, never exact.** YouTube publishes `"1.9M"` and
nothing more precise to a logged-out client. You get `likeCountText: "1.9M"` and
`likeCountApprox: 1900000`, with `likeCountIsApprox: true` on every record. No
Actor can give you the exact figure without being signed in.

**2. Polls give the options and the total, not the split.** A poll returns its
exact wording and `totalVotesText: "1.7M votes"`. The per-option percentage is
shown only to a signed-in account that has voted — in the logged-out payload
there is a sign-in prompt where the percentage would be. `votesPerChoice` is
therefore `null` by design, with a note explaining why, rather than a fabricated
number.

Post **comment threads** are also out of scope: they need a separate InnerTube
continuation fetch.

### Four outcomes, not one

A scraper that reports "0 results" for all of these is hiding information:

| `recordType` | Meaning |
| --- | --- |
| `POST` | A parsed post |
| `EMPTY` | The page rendered fine and this channel has never posted |
| `ERROR` + `community-unavailable` | YouTube says the tab is off. **A channel setting, not a scrape failure** |
| `REDUCED` | The page never listed posts after 5 attempts — retry or use a proxy |

### Known limits

- **10 posts per channel.** That is one page. Older posts sit behind a
  continuation token this Actor does not follow.
- Publish times are **relative** (`"5 months ago"`), because that is all YouTube
  renders.

### The path that does not work

`/@handle/community` returns a full 761 KB page reading *"This Community isn't
available"* with zero posts — for **every** channel, including ones that post
constantly. It is not a per-channel setting and no consent cookie changes it.
The working path is `/@handle/posts`. This Actor uses the latter; see
[`CRAWLING_METHOD.md`](CRAWLING_METHOD.md).

### Input example

```json
{
  "mode": "channel",
  "channels": ["@MrBeast", "@veritasium", "@mkbhd"],
  "postsPerChannel": 10
}
```

# Actor input Schema

## `mode` (type: `string`):

channel = every community post listed for each handle. reference = probe the surface and report what it yielded, including what it cannot give you.

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

Channel handles such as @MrBeast, or full channel URLs.

## `postsPerChannel` (type: `integer`):

Capped at 10 because that is what one posts page lists. Older posts sit behind a continuation this Actor does not follow - see the README.

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

Parallel channel fetches. One request per channel.

## `tlsProfile` (type: `string`):

Optional. All four answered 200 during recon; this is a tuning knob.

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

Optional. Useful if pages start coming back as REDUCED.

## Actor input object example

```json
{
  "mode": "channel",
  "channels": [
    "@MrBeast",
    "@veritasium"
  ],
  "postsPerChannel": 10,
  "concurrency": 3,
  "tlsProfile": "chrome124"
}
```

# Actor output Schema

## `results` (type: `string`):

Every post, empty, reduced and error record produced by this run.

# 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": [
        "@MrBeast",
        "@veritasium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/youtube-community-posts-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": [
        "@MrBeast",
        "@veritasium",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("fanndev/youtube-community-posts-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": [
    "@MrBeast",
    "@veritasium"
  ]
}' |
apify call fanndev/youtube-community-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fanndev/youtube-community-posts-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/uz4B8Y2CYVOohNOMh/builds/AUKBjRt50p0FNLrI8/openapi.json
