# Facebook Group Posts & Comments Scraper (`fanndev/facebook-group-posts-scraper`) Actor

Read what a public Facebook group is talking about: every post with text, author, timestamp, reactions, comment count, shares and media, paginated past the handful Facebook renders by default. Private and missing groups are reported as such instead of returning empty successes. No login, no proxy.

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

## Pricing

from $0.30 / 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

## Facebook Group Posts & Comments Scraper

Read what a public Facebook group is actually talking about: every post with its
text, author, timestamp, reaction count, comment count, share count and media —
paginated past the handful Facebook renders by default.

Built for market researchers, product teams and brand managers who want the
community's own words rather than a summary of them.

### Read this before you buy

**Facebook publishes the comment *count* on a group post to logged-out clients,
but not the comment *text*.** Measured 2026-09-20: a group post permalink
returns `comments.total_count: 29` and `comments.edges: []`, served by Meta's
`Unauthenticated` comment renderer. Every post this actor returns carries its
real `commentCount`; `commentsServed` tells you how many comment bodies Facebook
actually handed over, which for group posts is currently zero.

The `scrapeComments` option is there if you want to verify that for yourself, or
to pick the bodies up automatically if Meta's gating changes. It is **off by
default** because switching it on costs one request per post and, today, returns
nothing.

Everything else on this page is real and tested.

### What you get

One row per post:

| Field | Example |
| --- | --- |
| `groupName`, `groupId`, `groupPrivacy` | `React.JS developers -2023`, `1873009809663604`, `OPEN` |
| `groupMemberCount`, `groupMemberCountLabel` | `117400`, `117.4K members` |
| `authorName`, `authorId`, `authorUrl` | `Mohamed Hafez`, … |
| `text` | the post body |
| `createdAtIso` | `2026-07-09T01:41:29Z` |
| `reactionCount`, `commentCount`, `shareCount` | `43`, `29`, `4` |
| `attachmentUrls`, `attachmentCount` | photo/video CDN links |
| `postUrl` | permalink |

Plus a `RUN_SUMMARY` row: posts collected, distinct authors, total reactions,
total comments, newest and oldest post.

### Which groups can be read

Only groups that Facebook serves to a signed-out visitor. A private group, a
deleted group and a typo all return the same HTTP 200 with a ~330 KB shell, so
this actor checks the payload rather than the status code and gives you a
specific error row:

> `group_unavailable — Facebook served the empty shell (~330 KB). The group is
> private, does not exist, or is not readable without logging in.`

That is deliberately honest: a status-code check would report every private
group as a successful empty scrape.

### Input example

```json
{
  "groups": [
    "React.JSdevelopers",
    "https://www.facebook.com/groups/1873009809663604"
  ],
  "maxPostsPerGroup": 100,
  "minComments": 5,
  "postedAfter": "2026-01-01",
  "exportFormats": ["csv"]
}
```

### Filters

`textContains`, `minReactions`, `minComments` and `postedAfter` are applied after
fetching, so the run summary still reflects everything that was read. Posts whose
reaction count or timestamp Facebook withheld are **dropped** by a numeric
filter rather than admitted on a guess — an unknown number cannot be shown to
clear a bar.

### Running it on Apify: use a residential proxy for pagination

Facebook serves the **rendered first page** to any IP, including Apify's. But the
**first pagination request from a datacenter IP** comes back with
`Rate limit exceeded`, so a platform run with no proxy stops at the first batch of posts.

The rate limit is on Facebook's GraphQL endpoint, which every actor in this
family uses for its second page onwards. It was measured on 2026-09-20 with the
Ad Library actor, three runs of the same search within a minute:

| Run | Result |
| --- | --- |
| Apify, no proxy | 30 results, 1 page — log: `Rate limit exceeded` |
| Apify, `RESIDENTIAL` proxy | 60 results, 4 pages |
| Local machine, no proxy | 70 results, 5 pages |

So: switch the Apify proxy on and pick the **RESIDENTIAL** group whenever you
want more than the first page. Running from your own machine needs no proxy at
all.

The actor logs a warning naming the rate limit when it hits one, so a short run
is never silently mistaken for a short result set.

### Notes

- `authorId` is an opaque `pfbid…` token for newer accounts rather than a
  number. That is Facebook's own identifier format, not a parsing artefact.
- `attachmentUrls` are signed CDN links and expire. Download what you need during
  or shortly after the run.
- Group membership questions and their answers are shown only to admins and are
  not part of any public surface, so they are not returned.
- Member counts are abbreviated by Facebook (`117.4K`); the parsed number is
  approximate and the raw label is kept beside it.

# Actor input Schema

## `groups` (type: `array`):

One group per line: a full URL (https://www.facebook.com/groups/<slug-or-id>), a slug, or a numeric group id. Only groups that are readable without logging in can be scraped - private groups return an error row explaining why.

## `startUrls` (type: `array`):

Group URLs, for pasting a list straight out of another actor's dataset.

## `maxPostsPerGroup` (type: `integer`):

Facebook streams a group feed in batches; this actor replays its pagination query until the cap is reached or the feed runs out.

## `scrapeComments` (type: `boolean`):

Opens each post's permalink and emits whatever comment text Facebook serves. Important: Meta publishes the comment COUNT on a group post to logged-out clients but NOT the comment text (measured 2026-09-20), so this normally returns nothing for group posts while costing one request per post. The count is reported on every post regardless, without this option. Leave it off unless you want to check for yourself.

## `maxPostsForComments` (type: `integer`):

Caps the extra requests made when comment fetching is on.

## `onlyWithText` (type: `boolean`):

Drop posts that carry only a photo or video. Useful when you are analysing what the community says rather than what it shares.

## `minReactions` (type: `integer`):

Posts whose reaction count Facebook withheld are dropped, since an unknown count cannot be shown to clear the bar.

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

Filters on the comment count, which is published even though the comment text is not.

## `textContains` (type: `string`):

Case-insensitive substring match against the post body.

## `postedAfter` (type: `string`):

YYYY-MM-DD. Posts with no timestamp are dropped.

## `emitSummary` (type: `boolean`):

Append one RUN\_SUMMARY record: posts collected, distinct authors, total reactions and comments, newest and oldest post.

## `exportFormats` (type: `array`):

Also write the results to the key-value store in these formats. The dataset is always produced regardless.

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

Apify Proxy. RESIDENTIAL is the default and you should keep it: Facebook serves datacentre addresses a reduced page with no feed and no counts, so a run without a residential proxy usually collects nothing. The exit country also decides which country's results you get.

## Actor input object example

```json
{
  "groups": [
    "React.JSdevelopers"
  ],
  "maxPostsPerGroup": 40,
  "scrapeComments": false,
  "maxPostsForComments": 20,
  "onlyWithText": false,
  "emitSummary": true,
  "exportFormats": [],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Posts and comments collected from each public group, plus the run summary and error rows.

# 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 = {
    "groups": [
        "React.JSdevelopers"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fanndev/facebook-group-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 = {
    "groups": ["React.JSdevelopers"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fanndev/facebook-group-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 '{
  "groups": [
    "React.JSdevelopers"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call fanndev/facebook-group-posts-scraper --silent --output-dataset

```

## MCP server setup

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