# Facebook Comment Scraper — Public Comments & Replies (`crowdpull/facebook-comment-scraper`) Actor

Extract comments from any public Facebook post. No login or cookies needed. Supports 30+ fields per comment, per-reaction breakdowns, threaded replies, and Smart Scrape dedup.

- **URL**: https://apify.com/crowdpull/facebook-comment-scraper.md
- **Developed by:** [Crowd Pull](https://apify.com/crowdpull) (community)
- **Categories:** Social media, Marketing, Lead generation
- **Features:** Limited permissions
- **Stats:** 134 total users, 5 monthly users, 82.5% runs succeeded, 2 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.75 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## CrowdPull FB Comment Scraper

Extract comments and replies from Facebook posts, reels, pages, and groups. Logged-out extraction works where Facebook exposes comment data; optional cookies are required for signed-in-only surfaces.

Works with group posts, page posts, permalinks, and share links. No browser automation — just fast, lightweight HTTP requests.

### Why this scraper

- **No browser** — pure HTTP, no Playwright overhead. Faster and cheaper to run than browser-based alternatives.
- **Reaction breakdown** — per-type counts (like, love, haha, wow, sorry, anger, support) for sentiment analysis without NLP. Most scrapers only give you a total count.
- **Spam & moderation signals** — Facebook's own spam classification, disabled/hidden status, and reply-locked threads. Filter junk before it hits your pipeline.
- **Group context** — group ID, anonymous posting flags, and member profile status for cross-group audience analysis.
- **Smart Scrape dedup** — skip posts already scraped in previous runs. No other comment scraper offers incremental extraction.
- **@handle extraction** — mentions and URLs parsed from comment text, ready for lead gen or network mapping.
- **Structured dataset output** — typed schema with table preview in the Apify Console. Browse results without downloading.

### Features

- **Anonymous + authenticated modes** — starts logged out and reports when Facebook requires `fbCookies`
- **Reply expansion** — automatically fetches nested reply threads
- **Pagination** — loads additional comment pages when Facebook exposes them
- **Multi-post support** — process multiple posts in a single run with concurrent workers
- **Comment scraper chaining** — trigger automatically from the feed scraper via webhook

### What you get per comment

| Field | Description |
|-------|-------------|
| `commentId` | Unique Facebook comment ID |
| `authorName` | Commenter's display name |
| `authorId` | Facebook user ID |
| `authorProfileUrl` | Link to commenter's profile |
| `authorProfilePicture` | Profile picture URL |
| `authorGender` | `MALE`, `FEMALE`, or empty |
| `authorType` | `User` or `Page` |
| `text` | Full comment text |
| `timestamp` | ISO 8601 timestamp |
| `isReply` | Whether this is a reply to another comment |
| `depth` | Nesting depth (0 = top-level, 1 = reply, 2+ = nested) |
| `parentCommentId` | ID of the parent comment (for threading) |
| `replyCount` | Number of replies to this comment |
| `reactionCount` | Total reaction count |
| `reactionBreakdown` | Per-type counts: like, love, haha, wow, sorry, anger, support |
| `spamDisplayMode` | Facebook's spam classification |
| `isDisabled` | Whether the comment has been hidden/removed |
| `groupCommentInfo` | Group ID, anonymous posting, nickname usage, member status |
| `commentUrl` | Direct permalink to the comment |
| `extractedUrls` | URLs found in comment text |
| `extractedHandles` | @handles and mentions found in comment text |
| `scrapedAt` | ISO 8601 timestamp of when extraction occurred |

### Automatic chaining via webhook

The easiest way to run the comment scraper is to trigger it automatically when the [CrowdPull FB Group Posts Scraper](https://apify.com/crowdpull/facebook-group-posts-scraper) finishes. Set up an Apify webhook on the posts scraper:

1. Go to the posts scraper actor/task **Integrations** tab
2. Add integration: **Apify (Connect Actor or Task)** → select this comment scraper task
3. Event: **Run succeeded**
4. Input mapping: `{"datasetId": "{{resource.defaultDatasetId}}", "minComments": 0}`

The comment scraper loads the posts dataset, filters by `minComments`, and extracts comments from all matching posts. No API tokens or manual URL copying needed.

### How it works

1. Fetches the post page anonymously using lightweight HTTP requests
2. Extracts all available comments from the page response
3. Paginates to load additional comments beyond the initial batch
4. Expands nested reply threads automatically
5. Deduplicates comment records across the available response sources

### Anonymous vs authenticated mode

| Capability | Anonymous (default) | With cookies |
|-----------|-------------------|-------------|
| Top-level comments | Only when exposed to logged-out visitors | Available to the supplied Facebook session |
| Reply threads | Varies by post and surface | Usually higher, but still privacy-dependent |
| Failure behavior | Per-post `failed`/`unavailable`/`partial` status in `OUTPUT` and `SUMMARY` | Per-post `failed`/`unavailable`/`partial` status in `OUTPUT` and `SUMMARY` |

Facebook can return an empty public response for comments that are visible to a signed-in browser. In that case the Actor does not claim a true zero: it records the coverage failure in `OUTPUT` and `SUMMARY`, and signed-in-only queries are reported with an actionable request for `fbCookies`.

### Smart Scrape (incremental dedup)

Enable **Smart Scrape** to skip posts already scraped in previous runs. Ideal for recurring monitoring jobs — you only pay for new posts.

1. On first run, all posts are scraped normally and cached in a persistent store
2. On subsequent runs, cached posts are skipped (charged at a reduced `cache-check` rate)
3. Optionally set a **refresh window** to re-scrape recent posts that may have gained new comments

#### Cost comparison (50 posts/week, ~10 comments/post average)

| Scenario | Without Smart Scrape | With Smart Scrape |
|----------|---------------------|-------------------|
| Week 1 (all new) | 500 comments extracted | 500 comments extracted |
| Week 2 (10 new posts) | 500 comments extracted | 100 comments + 40 cache checks |
| Week 4 (10 new/week) | 2,000 comments total | 800 comments + 120 cache checks |
| **Monthly cost** | **Full price** | **~55% cheaper** |

Built-in incremental extraction — only pay for new comments on recurring runs.

### Use cases

- **Market research** — analyze customer sentiment and questions in niche groups
- **Lead generation** — extract URLs, @handles, and active community members
- **Content analysis** — use reaction breakdowns to measure sentiment without NLP
- **Competitive intelligence** — monitor competitor post discussions
- **Community health** — track spam rates, disabled comments, and moderation patterns

### Input example

```json
{
  "postUrls": [
    "https://www.facebook.com/groups/homebakeryforbeginners/posts/903456049205823/"
  ],
  "maxCommentsPerPost": 50,
  "includeReplies": true,
  "extractUrls": true
}
```

### Output example

```json
{
  "postUrl": "https://www.facebook.com/groups/homebakeryforbeginners/posts/903615849189843/",
  "commentId": "903620162522745",
  "authorName": "Sandy D Crusan",
  "authorId": "pfbid0zfQhxbUdLXKEU...",
  "authorGender": "FEMALE",
  "authorType": "User",
  "text": "I wish NJ allowed alcohol infused products with a Cottage License.",
  "timestamp": "2026-02-25T23:20:03.000Z",
  "isReply": false,
  "depth": 0,
  "replyCount": 0,
  "reactionCount": 1,
  "reactionBreakdown": { "like": 1, "love": 0, "haha": 0, "wow": 0, "sorry": 0, "anger": 0, "support": 0 },
  "spamDisplayMode": "none",
  "isDisabled": false,
  "groupCommentInfo": {
    "group": { "id": "683303171221113" },
    "is_author_anonymous": false,
    "is_author_nickname": false,
    "is_author_with_member_profile": true
  },
  "extractedUrls": [],
  "extractedHandles": [],
  "scrapedAt": "2026-02-26T00:33:54.010Z"
}
```

### Other CrowdPull Facebook scrapers

- **[Facebook Group Posts Scraper](https://apify.com/crowdpull/facebook-group-posts-scraper)** — extract posts from any public Facebook group with Smart Scrape dedup and automatic comment scraper chaining
- **[Facebook Marketplace Scraper](https://apify.com/crowdpull/facebook-marketplace-scraper)** — extract listings from Facebook Marketplace by location with price filtering and keyword search

### Cost estimate

This Actor uses **no browser** — just lightweight HTTP requests.

| PPE Event | Description | Cost |
|-----------|-------------|------|
| `apify-actor-start` | One-time per run | $0.005 |
| `apify-default-dataset-item` | Per comment pushed to dataset | $0.002/comment on Free; $0.0015/$0.001/$0.00075/$0.0006/$0.0005 on Bronze through Diamond |
| `cache-check` | Per post skipped by Smart Scrape | $0.001 |

At the currently applied Free-tier rate, 50 comments cost about $0.105 including the run-start event. Higher Apify tiers cost less; verify the Store pricing panel for current rates.

### Limitations

- Facebook may require authentication even for comments shown on otherwise public posts or reels
- Private groups and posts require a Facebook session that is authorized to view them
- Very new posts (minutes old) may not have comments indexed yet
- Facebook may change internal APIs; report issues if extraction stops working
- `reactionBreakdown` only populates when comments have reactions (zeros otherwise)
- `authorGender` depends on the user's Facebook privacy settings
- Anonymous feed API returns `commentCount: 0` for all posts — when using webhook chaining, set `minComments: 0` to process all posts

### Is it legal to scrape Facebook comments?

This Actor extracts **publicly visible** data from Facebook posts. Here are the key legal considerations:

- **Access boundary**: Anonymous mode only receives data Facebook exposes to logged-out visitors. Cookie mode is limited to content the supplied account may view.
- **Authentication is optional, not invisible**: The Actor starts anonymously and reports signed-in-only query failures instead of silently returning an empty dataset.
- **Terms of Service**: Web scraping of public data has been upheld in multiple court rulings (hiQ Labs v. LinkedIn, 2022). However, Facebook's Terms of Service restrict automated data collection. Use this tool responsibly and in compliance with applicable laws.
- **GDPR/CCPA**: Extracted data may contain personal information (names, profile pictures, comment text). If you process this data in the EU/EEA or for California residents, ensure you have a lawful basis under GDPR/CCPA. Consider anonymizing or pseudonymizing personal data before storage.
- **Rate limiting**: The scraper includes built-in delays and respects Facebook's rate limits to minimize server impact.

**You are responsible for ensuring your use of this data complies with all applicable laws and regulations in your jurisdiction.**

### Recovery and completeness

Provide `postUrls` or `datasetId`. Duplicate URLs are normalized and processed once per run, even with Smart Scrape disabled. Transport failures report a safe error code without cookies or proxy credentials. Requests share pacing and bounded rate-limit retries; login-required and unavailable content remain explicit coverage gaps.

`OUTPUT.posts[].status` can be `partial` when comments were collected but pagination or replies failed. `partialPosts` counts these targets. Their rows remain available, and incomplete targets are not marked complete in Smart Scrape. Cache entries created before completeness tracking are rechecked once.

The input form uses a public post checked during the September 2026 release. Replace it with your intended public post URL; Facebook may later remove or restrict any example. Unavailable/private surfaces are reported in `OUTPUT` and are not evidence of successful comment extraction.

### Reliability update — September 21, 2026

Smart Scrape completion now belongs to the requested comment limit, reply setting, sort, URL extraction and authentication mode. Increasing the limit or changing those settings rechecks the post. Older entries without scope are rechecked once. A cap is a valid requested result, not proof that Facebook has no more comments; diagnostics and cache entries record `limitedByCap`.

Partial extraction remains retryable.

### Run outcome — October 2026

A run fails only when its input is unusable: neither `postUrls` nor `datasetId` was provided, or none of the URLs is a Facebook post URL. Deleted, private, login-walled and otherwise failing posts are per-target outcomes, so the run finishes as succeeded and records what happened to each post. A malformed URL beside valid ones is reported as that target's error while the valid posts are scraped.

Every run writes a `SUMMARY` record to the default key-value store with the overall `status`, per-status counts, `targetErrors` and `targetWarnings` (each with the post URL, a `code` such as `content_unavailable`, `login_required`, `authentication_required` or `invalid_url`, and a message) and run-level `warnings`. `OUTPUT` keeps the detailed per-post coverage. Set `failOnEmpty` if a run that extracts no comments should fail instead.

# Changelog

This Actor's version history is a separate document: https://apify.com/crowdpull/facebook-comment-scraper/changelog.md

# Actor input Schema

## `postUrls` (type: `array`):

Facebook post URLs to extract comments from. Supports group posts, page posts, permalinks, and share links. Optional when datasetId is provided.

## `datasetId` (type: `string`):

Apify dataset ID from a Posts Scraper run. When provided, post URLs are loaded from the dataset and postUrls input is ignored.

## `minComments` (type: `integer`):

When loading from a dataset, only process posts with at least this many comments. Set to 0 to process all posts (recommended since anonymous API often returns commentCount=0). Ignored when postUrls is provided directly.

## `maxCommentsPerPost` (type: `integer`):

Maximum comments to extract per post (including replies). Set to -1 for unlimited. Useful for capping costs on very large threads.

## `sortOrder` (type: `string`):

How comments are sorted. RANKED = most relevant first (Facebook default). RECENT = newest comments first. ALL = all top-level comments in chronological order.

## `includeReplies` (type: `boolean`):

Expand and extract reply threads under each comment. Facebook may limit anonymous comment and reply coverage; cookies can improve coverage.

## `extractUrls` (type: `boolean`):

Parse comment text for URLs and @handles. Useful for lead generation from link-share threads.

## `enableDedup` (type: `boolean`):

When enabled, posts scraped in previous runs are skipped. Only new or refreshed posts are scraped. Skipped posts incur a reduced 'cache-check' fee instead of full per-comment charges.

## `refreshWindowDays` (type: `integer`):

Re-scrape posts first seen within the last N days to capture new comments. Set to 0 to never refresh (only scrape brand-new posts). Set to 7 to re-scrape posts from the last week.

## `fbCookies` (type: `string`):

Optional Facebook auth cookies for improved comment and reply coverage. Coverage can still vary by post privacy and Facebook response. Format: 'c_user=XXX; xs=XXX; datr=XXX'. Get from browser DevTools → Application → Cookies → facebook.com.

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

Number of post URLs processed in parallel. Lower this if Facebook rate-limits requests.

## `failOnEmpty` (type: `boolean`):

Mark the run failed when every target produces zero verified comments. Without it, unavailable or failing posts are reported in SUMMARY and the run succeeds.

## `debugMode` (type: `boolean`):

Save the first Facebook HTML and GraphQL responses to the run key-value store for troubleshooting. Leave off for normal runs.

## `proxyConfig` (type: `object`):

Proxy settings. Residential proxies recommended for Facebook.

## Actor input object example

```json
{
  "postUrls": [
    "https://www.facebook.com/zuck/posts/10117964356277811:1542144927680617"
  ],
  "minComments": 0,
  "maxCommentsPerPost": 50,
  "sortOrder": "RANKED",
  "includeReplies": true,
  "extractUrls": true,
  "enableDedup": false,
  "refreshWindowDays": 0,
  "maxConcurrency": 3,
  "failOnEmpty": false,
  "debugMode": false,
  "proxyConfig": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

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

No description

## `summary` (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 = {
    "postUrls": [
        "https://www.facebook.com/zuck/posts/10117964356277811:1542144927680617"
    ],
    "maxCommentsPerPost": 50,
    "sortOrder": "RANKED"
};

// Run the Actor and wait for it to finish
const run = await client.actor("crowdpull/facebook-comment-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 = {
    "postUrls": ["https://www.facebook.com/zuck/posts/10117964356277811:1542144927680617"],
    "maxCommentsPerPost": 50,
    "sortOrder": "RANKED",
}

# Run the Actor and wait for it to finish
run = client.actor("crowdpull/facebook-comment-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 '{
  "postUrls": [
    "https://www.facebook.com/zuck/posts/10117964356277811:1542144927680617"
  ],
  "maxCommentsPerPost": 50,
  "sortOrder": "RANKED"
}' |
apify call crowdpull/facebook-comment-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crowdpull/facebook-comment-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/YFIsEMsNEyjvnEkPu/builds/TdYbyDtf4tP7EniTy/openapi.json
