# Facebook Groups Posts Scraper (`nice_dev/facebook-groups-posts-scraper`) Actor

Scrape the posts of public Facebook groups, newest first, no account needed: text, author, date, reactions by type, comments, photos, videos, links, sale listings. Filters by date, keywords and reactions, new posts only. Export to JSON, CSV or Excel.

- **URL**: https://apify.com/nice_dev/facebook-groups-posts-scraper.md
- **Developed by:** [Nice Dev](https://apify.com/nice_dev) (community)
- **Categories:** Social media, Lead generation, MCP servers
- **Stats:** 68 total users, 29 monthly users, 97.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 posts

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

### 💬 What is Facebook Groups Posts Scraper?

**Facebook Groups Posts Scraper** extracts **posts from public [Facebook groups](https://www.facebook.com/groups/)**: **text, author, date, reactions by type, comments, photos, videos, links and buy-and-sell listings**, newest first. It works as a **Facebook Groups API alternative**: run it on a schedule, call it from your code, or plug it into Make, Zapier or n8n.

Paste a **group URL** (or type its ID or name), click **Start**, and download the posts in JSON, CSV or Excel. **No Facebook account needed** for public groups, nothing to set up: the proxy is included in the price.

### 📋 What data can you extract from Facebook groups?

One item per post, 42 fields:

| Category | What you get |
| --- | --- |
| 📝 **Post** | full text, link to the post, type (photo, album, video, link, shared…), hashtags, people tagged — `Meet Archie, a one-year-old beagle mix` |
| 👤 **Author** | name, profile link, profile photo, verified badge |
| 🕒 **Date** | publication time, exact to the second — `2026-10-06T01:00:58.000Z` |
| ❤️ **Engagement** | reactions in total and by type (like, love, care, haha, wow, sad, angry), comment and share counts — `243 reactions, 19 comments, 5 shares` |
| 💬 **Comments** | the 1-3 comments Facebook shows under each post, free; as an option, every comment and its replies |
| 📷 **Photos and videos** | full-size photo links, photo descriptions, direct video link, link cards; as an option, every photo of an album |
| 🛒 **Buy and sell** | title, price, currency, sold or not, item description; as an option, where the item is (city, place, coordinates) |
| 📞 **Contacts** | as an option, e-mail addresses and phone numbers written in the post, and the WhatsApp or Messenger button |
| 👥 **Group** | group name, ID and link; as an option, exact member count, posts per day, creation date, rules and admins |

Every field, with an example, is listed in the **Output** section below.

### ✅ Why use Facebook Groups Posts Scraper?

- 🔓 **No account, no cookies**: paste the link of a public group and start.
- 🗂️ **Many groups in one run**: up to 1,000 groups, with a cap per group so that one busy group cannot eat the whole budget. The same group pasted twice (by link, ID, name or a post URL) is read once.
- 🎯 **Filters you rarely find elsewhere**: keywords, minimum reactions, minimum comments, posts with a contact only, before / after a date, buy-and-sell listings only.
- 🔔 **Monitoring built in**: tick **Only new posts**, schedule the Actor, and each run returns (and charges) only the posts it has never delivered before.
- 💬 **Every comment and reply**, **every photo of an album**, **where each item for sale is**, **group details** and **contacts**: options you tick only when you need them, charged only when delivered.
- 🔌 API, scheduling, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.

### 🚀 How to scrape Facebook groups

1. Create a free Apify account.
2. Open **Facebook Groups Posts Scraper** and paste one or more group links into **Facebook groups** (e.g. `https://www.facebook.com/groups/selftaughtprogrammers/`), or type group IDs or names into **Group IDs or names**. The link from Facebook's **Share** / **Copy link** button (`https://www.facebook.com/share/g/…`) works as well.
3. Set **Max posts** (100 by default, 0 = no limit) — and **Max posts per group** when you read several groups.
4. Tick the options you want (all comments, complete albums, item locations, group details, contacts) and the filters, then click **Start**.
5. Download the dataset in JSON, CSV, Excel or via API.

### 💰 How much does it cost to scrape Facebook groups?

This Actor uses **pay per event** pricing: you pay **per post saved**, plus a small fee per run start. Higher Apify plans pay less. The options are off by default and charged only when they deliver something:

| What | Free | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| 📝 **Post** saved (one row) | **$0.63** per 1,000 | $0.62 | $0.61 | $0.60 |
| 🚀 **Run start** | $0.01 per run | $0.01 | $0.01 | $0.01 |
| 🔎 **Filter check**: a post read and dropped by one of your filters (keywords, reactions, comments, contact, listings only; the dates: only a post without a date) | $0.59 per 1,000 | $0.58 | $0.57 | $0.56 |
| 💬 **Comment** or reply saved (option **All comments**) | $0.54 per 1,000 | $0.51 | $0.45 | $0.32 |
| 📷 **Complete album**: an album completed past the photos of the feed (option **Complete albums**) | $1.39 per 1,000 albums | $1.38 | $1.37 | $1.36 |
| 📍 **Listing location**: an item for sale saved with its location (option **Listing location**) | $0.78 per 1,000 | $0.77 | $0.76 | $0.75 |
| 📞 **Contact found**: a post where an e-mail or a phone number was found, WhatsApp button included (option **Contacts found in posts**) | $7.19 per 1,000 posts with a contact | $7.18 | $7.17 | $7.16 |
| 👥 **Group details**, once per group (option **Group details**) | $10.99 per 1,000 groups | $9.59 | $8.39 | $7.19 |

Examples:

- **1,000 posts of one group** on the Gold plan: $0.60 + $0.01 = **$0.61** ($0.64 on the Free plan).
- **200 posts with All comments**, 5 comments per post on average, on the Gold plan: $0.12 for the posts + $0.32 for the 1,000 comments + $0.01 = **$0.45**.
- **A daily monitor** (**Only new posts**) that finds 30 new posts a day on the Free plan: about **$0.03 a day**, $0.87 a month.

A post a filter keeps costs its normal price, a post a filter drops costs only the filter check. Facebook has no search inside a group without a login, so a rare keyword reads far back and pays the filter check on each post it drops: add **Posted after** to bound it. A post **Only new posts** already delivered costs nothing, and a group that gives no post is never charged for its details. Platform usage (compute, proxy) is included in the price.

### ⚙️ Input

The simplest run, one group:

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/groups/selftaughtprogrammers/" }],
    "maxItems": 200
}
```

Several groups, a cap per group, last week only, posts that mention a job:

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/groups/selftaughtprogrammers/" }],
    "groupIds": ["dogspotting", "1010440649638729"],
    "maxItemsPerQuery": 100,
    "postedAfter": "7 days",
    "keywords": ["hiring", "job offer"],
    "minReactions": 5
}
```

Items for sale with where they are, in a buy-and-sell group:

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/groups/chothuenhanguyencannhatrang/" }],
    "maxItems": 50,
    "onlyListings": true,
    "includeListingLocation": true
}
```

Every comment and reply, complete albums, group details, only new posts on each scheduled run:

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/groups/dogspotting/" }],
    "maxItems": 50,
    "includeComments": true,
    "includeReplies": true,
    "expandAlbums": true,
    "includeGroupDetails": true,
    "onlyNew": true,
    "stateKey": "dogspotting-daily"
}
```

| Field | Notes |
| --- | --- |
| `startUrls` | Group links (`https://www.facebook.com/groups/selftaughtprogrammers/`), the link of a post of the group (the whole group is read), or a share link (`https://www.facebook.com/share/g/1AbCdEfGhI/`, from Facebook's Share / Copy link button: the run follows it to the group; a share link that leads to a profile, a page or a private group is skipped with a message and listed in `FAILED_REQUESTS`, the other groups are read; paste the address your browser shows, which starts with https://www.facebook.com/groups/, instead). |
| `groupIds` | Group IDs (`387875758242831`) or names (`selftaughtprogrammers`), when you have no link. Up to 1,000 groups with `startUrls`; a group given twice, by link, ID or name, is read once. |
| `maxItems`, `maxItemsPerQuery` | Stop after this many posts for the whole run (`0` = unlimited); cap for EACH group (`0` = no per-group cap). |
| `postedAfter`, `postedBefore` | Publication date range: `2026-09-01`, or a period before now (`7 days`, `2 weeks`, `1 month`, `24 hours`). |
| `keywords`, `excludeKeywords` | Keep the posts whose text contains one of these words (`hiring`); drop those that contain one of the others. Case and accents ignored. The group is read post by post (no search without a login): a rare word reads far back (about 12,000 posts with `maxItems` 100, the whole group with `0`), each dropped one charged the filter fee — add `postedAfter` to bound it. |
| `minReactions`, `minComments` | Only posts with at least this many reactions / comments (`0` = no minimum). |
| `onlyListings` | Groups with a sale section: only the items for sale, with their price. Some groups have no listings feed: such a group is skipped with a message that says so (clear this option to read its posts), and the other groups are read. |
| `extractContacts`, `onlyWithContact` | Find e-mail addresses and phone numbers in the posts (option); keep only the posts that have one (`onlyWithContact` needs `extractContacts`: without it the run is refused). |
| `includeComments`, `maxCommentsPerPost`, `includeReplies`, `maxRepliesPerComment` | Every comment of each post (option), up to `maxCommentsPerPost` (100 by default); with `includeReplies`, the replies under each comment too and the replies to those replies, up to `maxRepliesPerComment` for each (`includeReplies` needs `includeComments`: alone it reads nothing and costs nothing). |
| `expandAlbums` | Every photo of an album: Facebook shows only 5 in the group (option). |
| `includeListingLocation` | Where each item for sale is: city, state, place and coordinates (option; one extra page read per item). |
| `includeGroupDetails` | The group's About page once per group: exact member count, posts per day, creation date, privacy, rules (option). |
| `onlyNew`, `stateKey`, `resetState` | Monitoring: only the posts never delivered under this memory key; `resetState` forgets the memory. |
| Advanced | `proxyConfiguration` (included in the price, leave the default), `maxConcurrency` (groups read at the same time), `maxRequestsPerMinute`, `minRequestIntervalMs`, `maxRequestRetries`, `debugLog`. |

### 📦 Output

A post, shortened (long texts and photo links cut, some columns left out):

```json
{
    "id": "10166280720354467",
    "url": "https://www.facebook.com/groups/dogspotting/posts/10166280720354467/",
    "text": "Ever wonder what would happen if a beagle approached a German shepherd at a college mixer… meet Archie, a one-year-old…",
    "publishedAt": "2026-10-06T01:00:58.000Z",
    "postType": "album",
    "user": { "id": "100001234567890", "name": "Jane Doe", "url": "https://www.facebook.com/jane.doe", "type": "User" },
    "groupId": "10487409466",
    "groupTitle": "Dogspotting",
    "groupUrl": "https://www.facebook.com/groups/dogspotting/",
    "inputUrl": "https://www.facebook.com/groups/dogspotting/",
    "reactionsCount": 243,
    "commentsCount": 19,
    "sharesCount": 5,
    "topComments": [
        {
            "id": "10166280811924467",
            "text": "I once met a GSD x corgi and he looked the same, just shorter",
            "publishedAt": "2026-10-05T19:44:05.000Z",
            "authorName": "John Smith",
            "depth": 0
        }
    ],
    "comments": null,
    "images": ["https://scontent.xx.fbcdn.net/v/t39.30808-6/839389526_….jpg"],
    "imagesCount": 8,
    "attachments": [{ "type": "photo", "id": "10163808721866298", "url": "https://www.facebook.com/photo/?fbid=10163808721866298" }],
    "hashtags": [],
    "mentions": [],
    "emails": [],
    "phones": [],
    "groupDetails": null,
    "scrapedAt": "2026-10-06T12:00:00.000Z"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV or Excel.

#### All 42 fields

| Fields | What you get |
| --- | --- |
| `id`, `url`, `storyId`, `feedbackId` | **Post**: Facebook's post ID, link to the post, and the two internal IDs other Facebook tools ask for |
| `text`, `postType`, `hashtags`, `mentions` | the full text (null for a photo without text), `photo` / `album` / `video` / `link` / `shared` / `text`…, the hashtags, the people and pages tagged (name, id, link) |
| `publishedAt`, `timestamp` | **Date**: ISO 8601 UTC and Unix seconds (for groups that approve posts, the time of approval) |
| `user` | **Author**: id, name, profile link, type (User or Page), profile photo, verified badge — `Anonymous participant` for anonymous posts |
| `groupId`, `groupTitle`, `groupUrl`, `inputUrl` | **Group**: ID, name, link, and the group as you gave it |
| `reactionsCount`, `reactionLikeCount`, `reactionLoveCount`, `reactionCareCount`, `reactionHahaCount`, `reactionWowCount`, `reactionSadCount`, `reactionAngryCount` | **Reactions**: in total and by type (null when Facebook does not send the count, never a made-up 0) |
| `commentsCount`, `sharesCount` | comment and share counts |
| `topComments` | the 1-3 comments Facebook shows under the post: id, link, text, date, author name, ID and profile link — always filled, free |
| `comments` | with `includeComments`: every first-level comment, the same fields plus reactions and reply count, and its `replies` with `includeReplies` (each reply holds its own `replies`, the second level); null when off |
| `images`, `imagesCount` | full-size photo links in the album's order; how many photos Facebook says the post has (complete with `expandAlbums`) |
| `attachments`, `videoUrl` | each photo, video or link card (size, photo description, video length, thumbnail…); direct MP4 link of the first video (expires after a few days) |
| `link`, `linkDomain`, `linkTitle`, `linkDescription` | the outside link shared in the post (Facebook redirect removed) and its card |
| `actionLink` | **Contacts**: the button the author added (WhatsApp, Messenger or a link, with its title and URL), with `extractContacts`; null without it |
| `listing` | **Buy and sell**: id, title, price as shown, amount, currency, price type, sold or not, description, and with `includeListingLocation` its `location` (city, state, the place as the seller shows it, latitude, longitude; null when off) |
| `sharedPost` | the post this one shares: id, link, text, author, and its photos (`images`, `imagesCount`; they are not in the row's own `images`) |
| `emails`, `phones` | **Contacts** found in the text and in the WhatsApp button, with `extractContacts` |
| `groupDetails` | with `includeGroupDetails`: member count, members text, posts in the last day and month, creation date, privacy, visibility, description, location, cover photo, admins, rules |
| `scrapedAt` | ISO timestamp of the extraction |

### 💡 Tips

#### How to get more posts

Set **Max posts** to `0` and add more groups. Facebook serves a group's posts newest first: an old post is reached by reading every newer one before it, so a date filter (`postedAfter`) is the cheapest way to read only a period.

#### How to reduce costs

You pay per post, so the levers are `maxItems`, `maxItemsPerQuery`, the filters (a dropped post costs only the filter fee, lower than a post — but a word that rarely appears makes the run read far back in the group: about 12,000 posts with **Max posts** 100, the whole group with no cap; each dropped one is charged — bound it with `postedAfter`) and `onlyNew` for recurring runs (you never pay twice for the same post). Tick an option only when you need it: comments, albums, item locations, group details and contacts are charged only when they deliver.

#### Monitoring: only the new posts

Tick **Only new posts** (`onlyNew`) and schedule the Actor. The first run returns everything; each later run stops reading a group at the first post it has already delivered to you: older posts are neither saved nor charged. With filters, the group also stops at the first post that a previous run already read and dropped with the same filters, so a monitoring run does not read the whole group again. A post that does not pass a time-dependent filter yet (a relative "posted before" date) is checked again on the next run. The memory lives in a named key-value store of your account (`facebook-groups-posts-scraper-seen`, up to 150,000 posts per key) and is only updated with posts that really reached the dataset, so a failed run never hides anything. Give each schedule its own `stateKey` (two schedules sharing a key would hide each other's posts), and tick `resetState` once to start over. A first run capped by **Max posts** keeps its oldest posts out for good: for a complete history, run once without a cap, then schedule.

#### Filter by publication date

`postedAfter` and `postedBefore` take a date (`2026-09-01`, the whole day is included) or a period before now (`7 days`, `2 weeks`, `1 month`; via the API also `24 hours` or a full ISO date-time). The filter reads `publishedAt`. Posts come newest first, so a group stops at the first post older than `postedAfter`; posts newer than `postedBefore` are read and skipped. Posts outside the dates are not saved, not charged and do not count in `maxItems`; only a post without a date is dropped with the filter fee (see Pricing).

### 🔌 Integrations and API

Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.

### 🤖 Use with AI agents (MCP)

AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the [Apify MCP server](https://mcp.apify.com), billed to their Apify account like any run. It returns one item per post of a public Facebook group. Actor id: `nice_dev/facebook-groups-posts-scraper`; MCP server with this Actor only: `https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/facebook-groups-posts-scraper`.

Smallest input, for a cheap first call:

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/groups/selftaughtprogrammers/" }],
    "maxItems": 10
}
```

Key output fields: `url`, `text`, `publishedAt`, `user`, `reactionsCount`, `commentsCount`, `images`, `groupTitle`.

Cost: per post saved plus a small run-start fee, see the pricing section above. Cap each call with `maxItems` and, through the API, with the run option `maxTotalChargeUsd`.

### ❓ FAQ

#### Is it legal to scrape Facebook groups?

The Actor only reads what Facebook shows publicly to any anonymous visitor of a public group. It logs in to nothing. Results contain personal data (names, profile links, and with the option e-mail addresses and phone numbers written in posts), which is protected by GDPR and similar laws: do not store it without a legitimate reason. You are responsible for using the data in compliance with Facebook's Terms of Service and applicable law. This Actor is not affiliated with Meta or Facebook.

#### Does it need a login or a proxy?

No login for public groups. The proxy is included in the price: leave the default setting. A request Facebook turns away is retried at once on a new proxy session, up to 10 times on top of the retries (without a proxy, after a pause of 5 seconds, doubled at each retry up to 150 seconds), and the group goes on from the exact post where it stood.

#### Can it read private groups?

Private groups are not supported: only public groups can be read without an account. A private group is skipped with a message in the log and nothing is charged for it.

#### A group is "not available on Facebook"?

Facebook shows the same page for a link, ID or name that does not exist, a group that was deleted, and a private group you cannot see. That group is skipped at once (not retried, nothing is charged for it), named in the log and in the run summary, and the other groups go on. If none of your groups can be read, the run ends with "No posts found" and their names (it is not marked as failed): check each link, ID or name (a private group cannot be read).

#### Why do I get fewer comments than `commentsCount`?

Facebook's count includes comments it hides from visitors (spam, removed, filtered by the group). **All comments** returns every comment Facebook shows publicly, in the order they were written. Replies go two levels deep: the replies to a comment, and the replies to those replies (`depth` 2, `replies` null: Facebook shows no deeper). One post's row is kept under 8 MB, the most a dataset item can hold: a post with so many comments and replies that it would pass that keeps the first ones and drops the last, you pay only for what is saved, and the log says how many posts were cut.

#### Where is an item for sale?

Tick **Listing location (buy and sell groups)**: each item for sale gets `listing.location` with the city, the state, the place as the seller shows it (`Nha Trang, Khánh Hòa`), and the latitude and longitude. It is one extra page read per item, and it is charged only for the items whose location is found: an item whose page cannot be read is still saved, without a location and without the charge. The same item posted twice is read once, and charged with each post saved. Posts that are not for sale cost nothing more.

#### Is the data safe to open in Excel or to show on a web page?

Texts are the members' own words, copied as they are. A text can begin with `-`, `+`, `=` or `@` (a phone number, a price such as `-20%`): Excel and Google Sheets may read such a cell of a CSV file as a formula or as a number. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. On a web page, escape every field like any text written by a stranger.

#### Known limitations

- Public groups only; a group's members list and Facebook's in-group search need a login (use `keywords` instead).
- Video links expire after a few days, like on Facebook: download the videos you need soon after the run.
- `onlyNew` remembers post IDs, not their content: a post edited later, or whose reactions grew, is not returned again.
- Two runs sharing the same `stateKey` at the same time may both return the same new post.

**A run that reaches its timeout** stops itself about 45 seconds before it: no new page is asked, what it read is saved and, with `onlyNew`, remembered, and the run ends *Succeeded* with "Stopped before the run's timeout". Resurrect it to go on from there, or give the next run a longer timeout (Run options).

**A run the platform stops without warning** (out of memory)

- Resurrect it: it goes on from where it stood at most a minute before the stop. What it had read since is read again, and the posts already saved are skipped: none is delivered or charged twice, and `maxItems` still counts them.
- With `onlyNew`, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the posts it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the posts of its last 2 seconds once more.

#### Something doesn't work?

The last line of the log counts the posts saved and filtered out, and the requests that failed after every retry. Those requests are listed, with the reason, in the `FAILED_REQUESTS` record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause.

If Facebook changes its pages, you are told instead of paying for blank rows. If the first 20 posts read all lack their publication date, author, group name, reaction count or comment count, the run saves nothing more, stops and fails, and its last message names the missing field: at most those first posts are charged. A post that `postedAfter` / `postedBefore` drops because it has no date at all counts among those 20. A run of fewer posts (a scheduled `onlyNew` run that finds a few new ones, a small **Max posts**) is checked at its end, from 5 posts: its posts are already saved, and the run fails with the missing field instead of ending green.

### 🛟 Support

Open an issue in the **Issues** tab with a link to your run: the run log and the `FAILED_REQUESTS` record of the key-value store show exactly which requests failed and why.

# Actor input Schema

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

Links to public Facebook groups (`https://www.facebook.com/groups/selftaughtprogrammers/`) or to a post of the group (`https://www.facebook.com/groups/selftaughtprogrammers/posts/10166280720354467/`). The link of Facebook's Share / Copy link button (`https://www.facebook.com/share/g/1AbCdEfGhI/`) works too: the run follows it to the group; if it leads to anything but a group, only that link is skipped (message in the log) and you can paste the address the browser ends on instead. Only links here: a group ID or name goes in **Group IDs or names** below. A post link reads its whole group, up to **Max posts**. Private groups cannot be read: they are skipped with a message in the log. Max 1 000 groups.

## `groupIds` (type: `array`):

Groups given by their ID (`387875758242831`) or by their name, the last part of the group link (`selftaughtprogrammers`). Read like the links above, a group given twice is read once. Max 1 000 groups in all, with **Facebook groups**.

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

Maximum number of posts to save for the whole run (after deduplication and filters). 0 = no limit.

## `maxItemsPerQuery` (type: `integer`):

Cap for EACH group, so that the first group cannot use up the whole **Max posts** budget. 0 = no per-group cap.

## `includeGroupDetails` (type: `boolean`):

Read each group's About page once (1 extra page per group) and add `groupDetails` to its posts: exact member count, posts in the last day, creation date, privacy, description, location and rules. Charged once per group (see Pricing).

## `extractContacts` (type: `boolean`):

Look for e-mail addresses and phone numbers in each post's text (and in the text of the post it shares) and return them in `emails` and `phones`. The author's contact button (WhatsApp, Messenger or a link, `actionLink`) is returned only with this option; its WhatsApp number counts as a phone. Charged only for the posts where at least one is found (see Pricing).

## `includeComments` (type: `boolean`):

Read ALL the first-level comments of each post (Facebook gives 3 per request, so a long thread takes a while) and return them in `comments`: text, author, date, reactions and the number of replies (`repliesCount`; tick **Replies to comments** to read the replies themselves, see `includeReplies`). The `topComments` Facebook shows under every post stay free. Charged per comment saved (see Pricing).

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

With **All comments**: the most comments kept for ONE post, from 1 to 5 000. A post with more is cut there (and you pay for the comments saved only). A post's row is also kept under 8 MB: past that the last comments and replies are dropped (and not charged), and the log says how many posts were cut.

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

Also read the replies to each comment, and the replies to those replies (two levels: the deepest Facebook shows), and return them in `replies`, inside the comment and inside each reply (same fields, plus `parentCommentId`). Only works together with **All comments**: ticked alone, nothing more is read. One extra request per comment or reply that has replies, so a busy thread takes longer. Each reply saved, at either level, is charged like a comment (see Pricing).

## `maxRepliesPerComment` (type: `integer`):

With **Replies to comments**: the most replies kept for ONE comment, and for ONE reply (its replies), from 1 to 1 000. A comment or a reply with more is cut there (and you pay for the replies saved only).

## `expandAlbums` (type: `boolean`):

Facebook lists only the first 5 photos of an album in a group's feed. Open the album of each post that has more photos and add the others to `images` and `attachments` (1 extra page per album). Posts with 5 photos or less cost nothing more. Charged once per album opened (see Pricing).

## `includeListingLocation` (type: `boolean`):

For each item for sale in a buy-and-sell group, read where it is and fill `listing.location`: city, state, the place as the seller shows it, latitude and longitude (1 extra page per listing). Posts that are not for sale cost nothing more. Charged per post saved with its location (an item posted twice: its page read once, charged with each post; see Pricing).

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

Only posts published on or after this date: `2026-09-01`, or a period before now such as `7 days`, `2 weeks`, `1 month` (API: `24 hours` and full ISO date-times work too). Reads the post's publication time (for groups that approve posts, the time of approval). Posts come newest first, so the group stops at the first older post.

## `postedBefore` (type: `string`):

Only posts published on or before this date (the whole day is included), or older than a period such as `30 days`. Newer posts are skipped without being saved.

## `onlyListings` (type: `boolean`):

In groups with a sale section, read only the items for sale (Facebook's listings feed, newest first) and fill `listing` (title, price, currency, sold or not). Some groups have no listings feed: such a group is skipped with a message that says so (clear this box to read its posts), and the other groups are read.

## `keywords` (type: `array`):

Only posts whose text (or the text of the post they share) contains at least one of these words or phrases (case and accents ignored). Facebook has no in-group search without a login, so the Actor reads the group and keeps the matching posts: a rare word reads far back (about 12,000 posts with Max posts 100, the whole group with no cap), each dropped post charged the filter fee — add **Posted after** to bound it. Empty = all posts.

## `excludeKeywords` (type: `array`):

Drop the posts whose text contains one of these words (case and accents ignored).

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

Only posts with at least this many reactions (all types). 0 = no minimum.

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

Only posts with at least this many comments. 0 = no minimum.

## `onlyWithContact` (type: `boolean`):

Keep only the posts where an e-mail address or a phone number was found. Needs **Contacts found in posts** to be ticked too: without it the run is refused.

## `onlyNew` (type: `boolean`):

Skip the posts that a previous run (same **Memory key**) already delivered: they are not saved and not charged, and the group stops being read at the first one already seen. First run = everything is new.

## `stateKey` (type: `string`):

Name of the memory used by **Only new posts**. Give each schedule / task its own key (e.g. `crypto-groups`) so that they do not share their memory. Letters, digits, `-` and `_`.

## `resetState` (type: `boolean`):

Forget everything remembered under this **Memory key** before the run: this run returns (and charges) every post again. Untick it afterwards.

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

Facebook only serves group posts to home (residential) IP addresses: keep **Apify Proxy** and the Actor uses residential IPs, included in the price (the proxy group, the country and **No proxy** chosen here are ignored). Your own proxies (`proxyUrls`) must be residential.

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

Maximum number of groups read in parallel (IP addresses taken from a pool).

## `maxRequestsPerMinute` (type: `integer`):

Most requests to Facebook in any 60 seconds, all groups together. A budget, not an even pace (spread them with the minimum delay below).

## `minRequestIntervalMs` (type: `integer`):

Smallest gap between two requests, in milliseconds. 0 = no gap.

## `maxRequestRetries` (type: `integer`):

Retries per request before it is marked as failed (at least 1: a feed call lost for good ends the group there). A request Facebook turns away (login wall, rate limit) is also retried on a new IP address up to 10 times without using up these retries, and the group goes on from where it stopped.

## `debugLog` (type: `boolean`):

Include debug messages in the run log.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/groups/selftaughtprogrammers/"
    }
  ],
  "groupIds": [],
  "maxItems": 20,
  "maxItemsPerQuery": 0,
  "includeGroupDetails": false,
  "extractContacts": false,
  "includeComments": false,
  "maxCommentsPerPost": 100,
  "includeReplies": false,
  "maxRepliesPerComment": 20,
  "expandAlbums": false,
  "includeListingLocation": false,
  "onlyListings": false,
  "keywords": [],
  "excludeKeywords": [],
  "minReactions": 0,
  "minComments": 0,
  "onlyWithContact": false,
  "onlyNew": false,
  "stateKey": "default",
  "resetState": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "maxConcurrency": 4,
  "maxRequestsPerMinute": 120,
  "minRequestIntervalMs": 0,
  "maxRequestRetries": 5,
  "debugLog": false
}
```

# 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 = {
    "startUrls": [
        {
            "url": "https://www.facebook.com/groups/selftaughtprogrammers/"
        }
    ],
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("nice_dev/facebook-groups-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 = {
    "startUrls": [{ "url": "https://www.facebook.com/groups/selftaughtprogrammers/" }],
    "maxItems": 20,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("nice_dev/facebook-groups-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 '{
  "startUrls": [
    {
      "url": "https://www.facebook.com/groups/selftaughtprogrammers/"
    }
  ],
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call nice_dev/facebook-groups-posts-scraper --silent --output-dataset

```

## MCP server setup

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