# Facebook Groups Scraper (`karamelo/facebook-groups-scraper`) Actor

Collects publicly visible posts from Facebook group URLs.

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

## Pricing

from $1.50 / 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.

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 Groups Scraper: collect public group posts

Facebook Groups Scraper collects publicly visible posts from one or more Facebook group URLs and saves each post as a structured dataset row. Use it when you need a repeatable export of a group’s visible conversation for research, content analysis, community reporting, or an internal monitoring workflow. Provide public group URLs, choose a conservative post limit, and download results from the default Apify dataset when the run finishes.

Each row combines the group and post URLs with publication time, author name, post text, visible engagement counters, associated links, media URLs, mentioned names, and the time the row was collected. Fields that Facebook does not expose for a particular post are returned as `null`, so downstream tools can distinguish unavailable data from an empty string or a zero count.

This Actor is intended for public group pages only. It has a single, focused collection mode: group URLs in, post records out. That makes it a practical fit for analysts, researchers, community teams, and data teams who want a small, bounded batch before deciding whether to collect more.

### When this Actor is useful

- **Community research** — review recent visible discussions, authors, and engagement signals before preparing a research brief or editorial plan.
- **Content analysis** — export post text and publication times for theme coding, topic clustering, or a human-led content review.
- **Community operations** — capture a time-stamped slice of public group activity and compare it with a later collection.
- **Journalism and academic research** — create a structured working dataset from public posts, then apply your own sourcing, consent, and verification standards.
- **Data workflows** — retrieve a consistent set of fields in JSON, CSV, spreadsheet, or another Apify-supported dataset export format for further analysis.
- **Small-batch monitoring** — run a short collection first, inspect coverage and nullable fields, then schedule a later run only when that scope is appropriate for your use case.

The Actor caps each supplied group at 100 saved posts. With several group URLs, each group receives its own `maxItems` allowance.

### Start with a small collection

Open the Actor in Apify Console, add one public group URL, keep the default limit of 20 posts, and start the run. When it finishes, open the Dataset tab to inspect the saved rows. A one-group run is the best way to confirm that the group is publicly visible and that its current post format includes the fields relevant to your work.

```json
{
  "groupUrls": [
    "https://www.facebook.com/groups/nationalgeographic/"
  ],
  "maxItems": 20
}
```

The URL must use `https`, point to `facebook.com` or `www.facebook.com`, and use a `/groups/` path. Query parameters and fragments are not needed. Do not include account credentials in a URL.

### Collect from several groups

Use a list when you want one bounded batch from a defined set of public communities. `maxItems` applies separately to every URL. For example, this run can save at most 30 unique posts from each of the two supplied groups (up to 60 rows total).

```json
{
  "groupUrls": [
    "https://www.facebook.com/groups/nationalgeographic/",
    "https://www.facebook.com/groups/opensourcecommunity/"
  ],
  "maxItems": 30
}
```

Start small whenever you add a new group. Public visibility, post formats, and which optional fields are displayed can vary from group to group and over time. After reviewing the first dataset, you can choose a new limit from 1 through 100 for a later run.

### Input reference

| Parameter | Type | Default | Required | Description |
|---|---|---:|:---:|---|
| `groupUrls` | array of strings | — | Yes | One or more public Facebook group URLs. Each URL must be an HTTPS `facebook.com` or `www.facebook.com` URL whose path begins with `/groups/`. |
| `maxItems` | integer | `20` | No | Maximum number of posts saved for each supplied group URL. Choose a value from `1` to `100`. |
| `viewOption` | string | `TOP_POSTS` | No | Feed order: `TOP_POSTS`, `CHRONOLOGICAL`, or `RECENT_ACTIVITY`. |
| `search` | string | — | No | Case-insensitive text filter. Every word must appear; wrap the value in double quotes for an exact phrase. |
| `onlyPostsNewerThan` | string | — | No | Lower date bound as `YYYY-MM-DD`, an ISO timestamp, or a relative period such as `30 days`. |
| `onlyPostsNewerThanHours` | integer | — | No | Lower date bound in hours; overrides `onlyPostsNewerThan` when supplied. |
| `onlyPostsOlderThan` | string | — | No | Upper date bound using the same formats. Combine with `onlyPostsNewerThan` for a closed range. |
| `proxyConfiguration` | object | Apify Proxy enabled with US residential settings | No | Optional Apify platform proxy settings for the run. Leave the default in place for the verified configuration, or adjust the available Apify settings to suit your account and use case. |

`groupUrls` is the only required input. An omitted `maxItems` uses 20. A value of `0`, a negative value, or a value above 100 is not accepted. Repeated versions of the same normalized group URL are processed once within a run.

### What a dataset row represents

Each saved row represents one visible Facebook group post. The dataset uses a flat record shape so it can be exported and mapped into spreadsheets, databases, and analytics tools without unpacking a nested author or comment object. `postId` is the most useful row-level deduplication key when combining repeated exports. `postUrl` is the direct post link when Facebook exposes one.

This is an illustrative, schema-valid record. It uses invented names and text; values returned by a live run reflect the selected public group at collection time.

```json
{
  "groupUrl": "https://www.facebook.com/groups/nationalgeographic/",
  "groupTitle": "Example public photography group",
  "postUrl": "https://www.facebook.com/groups/nationalgeographic/posts/123456789012345/",
  "postId": "123456789012345",
  "postedAt": "2026-09-16T09:30:00.000Z",
  "authorName": "Example member",
  "text": "Illustrative post text for a public-group data workflow.",
  "reactionsCount": 12,
  "commentsCount": 3,
  "sharesCount": 1,
  "linkTitle": null,
  "linkUrl": null,
  "mediaUrls": null,
  "mentionedNames": ["Example colleague"],
  "scrapedAt": "2026-09-16T10:00:00.000Z"
}
```

### Output fields

| Field | Type | Description |
|---|---|---|
| `groupUrl` | string | Canonical URL of the group supplied for the record. |
| `groupTitle` | string or `null` | Group name shown on the page, when available. |
| `postUrl` | string or `null` | Direct URL of the post, when available. |
| `postId` | string | Facebook post identifier used to distinguish records within a run. |
| `postedAt` | string or `null` | Publication time in ISO 8601 format, when available. |
| `authorName` | string or `null` | Author name displayed with the post, when available. |
| `text` | string or `null` | Visible post text, when available. |
| `reactionsCount` | integer or `null` | Visible reaction total, when available. |
| `commentsCount` | integer or `null` | Visible comment total, when available. |
| `sharesCount` | integer or `null` | Visible share total, when available. |
| `linkTitle` | string or `null` | Title of a linked item attached to the post, when available. |
| `linkUrl` | string or `null` | URL of a linked item attached to the post, when available. |
| `mediaUrls` | array of strings or `null` | Public media URLs associated with the post, when available. |
| `mentionedNames` | array of strings or `null` | Names visibly mentioned in the post text, when available. |
| `scrapedAt` | string | ISO 8601 time at which the row was collected. |

Nullable fields are normal. A `null` counter is not the same as a zero counter: it means the value was not available in the collected public page. An empty `mediaUrls` or `mentionedNames` list is also represented as `null` when there is no value to save. Treat counts as source-reported values rather than independently audited measurements.

### Practical workflows

#### Build a public discussion review

For a focused review, collect 20 posts from one group, export the dataset, and sort by `postedAt`. Read `text` alongside `reactionsCount`, `commentsCount`, and `sharesCount` to identify posts that attracted visible engagement. Keep `postUrl` with any notes so a reviewer can return to the original public post. Use `scrapedAt` to document when the snapshot was taken, especially when a report may be revisited later.

This workflow is useful for editorial planning, public-interest research, or community observation. It does not establish why a post performed well, who saw it, or whether every visible post was collected; those require additional evidence beyond a dataset export.

#### Compare a recurring public topic over time

Create separate small runs on different dates using the same group URL and limit. Store each export with its run date, then compare `postId` to identify repeated records and `postedAt` to organize new records chronologically. Use text analysis only after preserving the original row fields that support review: `groupUrl`, `postUrl`, `authorName`, and `scrapedAt`.

Because source visibility can change, treat each collection as a time-bounded snapshot rather than a complete archive. If a field is missing in one run, retain the `null` instead of substituting a value from another source.

#### Prepare a spreadsheet for human coding

Export a dataset to CSV or a spreadsheet-compatible format. Add your own columns for theme, reviewer, and confidence, while retaining the source identifiers and timestamps. The flat structure makes it straightforward to keep `text`, engagement counters, and URLs in one table. For posts that include several media URLs or mentioned names, decide whether to keep the arrays in a single cell or split them in a secondary analysis step.

Avoid using the dataset alone to make claims about individual people. Public availability does not remove privacy, contextual, or legal responsibilities.

### API and automation

The Actor’s technical identifier is `karamelo/facebook-groups-scraper`. You can start a run through the Apify API, wait for it to finish, and then retrieve the default dataset. Use an Apify API token created for your own account; never place a real token in a shared document or source file.

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('karamelo/facebook-groups-scraper').call({
    groupUrls: ['https://www.facebook.com/groups/nationalgeographic/'],
    maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

For an automated workflow, keep the input list under your control, use a conservative per-group `maxItems`, and send the completed dataset to the system where a human or downstream process will review it. Apify schedules, webhooks, and dataset exports can be used for platform-level automation; configure them in your Apify account according to your operational needs.

### Limits, availability, and cost control

This Actor saves at most 100 posts per run. It only accepts public Facebook group URLs and does not accept Facebook credentials, cookies, private-group URLs, or URLs outside the allowed Facebook group format. A group can return no rows when its content is unavailable to a public visitor or when the currently visible page does not expose recognizable posts.

Start with the default 20-post limit to assess the group and the result shape. For a multi-group request, remember that the limit applies independently to each URL. Use `postId` when merging exports to avoid counting the same post twice. Keep `scrapedAt` in every downstream table so you can interpret the dataset as a dated collection rather than current, complete group activity.

Pricing is managed in Apify for the Actor and your account. Review the current price and the applicable Apify platform charges before increasing run volume; this README intentionally does not state a per-post estimate because no verified pricing evidence is part of this Actor’s public contract.

### Troubleshooting

| Symptom | Likely cause | What to inspect or change |
|---|---|---|
| Input is rejected before the run starts | A URL is malformed, not HTTPS, outside the allowed Facebook host, or not a group path. | Use a public URL beginning with `https://www.facebook.com/groups/`. Remove credentials, ports, query parameters, and fragments. |
| The run finishes with no posts | The group or its current content is not publicly available, or no recognizable public posts were available during collection. | Open the group as a public visitor, confirm the URL, then retry with one group and a small limit. |
| Some fields are `null` | The source did not expose those optional details for that post. | Preserve `null` in your export and design downstream mappings to accept missing optional values. |
| Fewer posts are returned than requested | The selected group did not expose enough unique public posts, or a date/search filter removed candidates. | Review the dataset and filter settings, then adjust the per-group limit only after checking coverage. |
| A large batch costs more than expected | More requested results can increase your Actor and platform usage. | Start with a small batch, review the current pricing in Apify, and increase scope gradually. |

### Responsible use

Use this Actor only for legitimate purposes and only with public content you are permitted to collect and use. Results can contain personal data, opinions, and links to copyrighted media. Before collecting, storing, sharing, or contacting people based on the results, assess applicable privacy, intellectual-property, platform-term, and jurisdictional requirements. Do not use this Actor to access private groups, circumvent access controls, or make sensitive decisions about people.

### Frequently asked questions

#### Does it work with private Facebook groups?

No. The public input contract accepts public Facebook group URLs only. It does not accept account credentials, cookies, or other access material.

#### How many posts can I collect in one run?

Set `maxItems` from 1 to 100. The default is 20, and the limit applies separately to every URL in the input.

#### Can I submit more than one group URL?

Yes. Add multiple URLs to `groupUrls`. Begin with a small per-group limit so you can inspect which groups return records and how the results compare.

#### Why are a post URL, publication time, or engagement count sometimes missing?

Facebook does not display every optional value for every public post. The Actor returns `null` for an unavailable optional field rather than inventing a value.

#### Can I export the results to CSV or a spreadsheet?

Yes. The Actor saves rows to the default Apify dataset, which can be exported using the dataset options available in Apify.

#### How should I avoid duplicate rows across several runs?

Retain `postId` and use it as the primary row-level comparison key. Keep `groupUrl` and `scrapedAt` as context when the same post appears in more than one dated snapshot.

#### Can I automate recurring runs?

Yes. Use the Actor through your Apify account’s scheduling or API capabilities. Keep the group list and result limit explicit in each automated input so the collection scope stays reviewable.

#### What should I do first?

Run one public group with the default limit of 20, inspect the dataset fields and nullable values, then decide whether a larger or recurring collection is appropriate for your use case.

# Actor input Schema

## `groupUrls` (type: `array`):

One or more public Facebook group URLs.

## `maxItems` (type: `integer`):

The maximum number of posts to save for each supplied group URL.

## `viewOption` (type: `string`):

How Facebook orders the group feed. TOP\_POSTS is the default and returns Facebook's most relevant posts; CHRONOLOGICAL returns newest posts; RECENT\_ACTIVITY returns posts with the newest activity.

## `search` (type: `string`):

Optional case-insensitive filter. Every word must appear in the post text; wrap the value in double quotes to require an exact phrase. The actor keeps paginating until it collects maxItems matching posts or the feed ends.

## `onlyPostsNewerThan` (type: `string`):

Optional lower date bound. Accepts YYYY-MM-DD, an ISO timestamp, or a relative period such as 30 days. Posts without a verifiable timestamp are excluded when a date filter is active.

## `onlyPostsNewerThanHours` (type: `integer`):

Optional lower date bound in hours. When set, it overrides onlyPostsNewerThan.

## `onlyPostsOlderThan` (type: `string`):

Optional upper date bound. Accepts YYYY-MM-DD, an ISO timestamp, or a relative period such as 30 days. Combine with onlyPostsNewerThan for a closed range.

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

Optional Apify Proxy settings for this run.

## Actor input object example

```json
{
  "groupUrls": [
    "https://www.facebook.com/groups/nationalgeographic/"
  ],
  "maxItems": 20,
  "viewOption": "TOP_POSTS",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (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 = {
    "groupUrls": [
        "https://www.facebook.com/groups/nationalgeographic/"
    ],
    "maxItems": 20,
    "viewOption": "TOP_POSTS",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("karamelo/facebook-groups-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 = {
    "groupUrls": ["https://www.facebook.com/groups/nationalgeographic/"],
    "maxItems": 20,
    "viewOption": "TOP_POSTS",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("karamelo/facebook-groups-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 '{
  "groupUrls": [
    "https://www.facebook.com/groups/nationalgeographic/"
  ],
  "maxItems": 20,
  "viewOption": "TOP_POSTS",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call karamelo/facebook-groups-scraper --silent --output-dataset

```

## MCP server setup

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