Facebook Group Posts & Comments Scraper avatar

Facebook Group Posts & Comments Scraper

Pricing

from $3.40 / 1,000 posts

Go to Apify Store
Facebook Group Posts & Comments Scraper

Facebook Group Posts & Comments Scraper

Every post from any public Facebook group with its complete comment thread — nested replies included and correctly attached to their parent comment. No login, no cookies, no second Actor. Large groups resume across runs, so you never pay twice for the same post.

Pricing

from $3.40 / 1,000 posts

Rating

0.0

(0)

Developer

Amr Abdo

Amr Abdo

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

Extract posts from any public Facebook group together with their complete comment threads — nested replies included, correctly attached to the comment they answer — in a single run.

No login. No cookies. No account to get banned. Just the group link.


The problem this solves

Try to get Facebook group posts with their comments today and you hit one of two walls:

  • The two-Actor chain. One scraper collects posts, a second collects comments from every post URL, and you join the two datasets yourself. Two runs, two schemas, two bills, and a join key you have to trust.
  • The all-in-one that isn't. It returns posts with a comments field, and the field holds the three or four "top comments" Facebook bundles into the feed. A post with 400 comments gives you 4. Nothing tells you the rest exist.

And when either one does return replies, they arrive as top-level comments. Two comments and five replies become seven flat rows with nothing saying which reply answered which comment. The conversation is gone; only the sentences are left.

This Actor does the whole job in one run, and keeps the thread structure intact.

Two-Actor chainTypical all-in-oneThis Actor
Runs needed2 + a manual join11
Comments per postAllOnly "top comments"All
RepliesFlat, unattributedUsually missingNested, with parentCommentId
Cost for 200 posts / 3,000 comments~$6.80~$0.30 (incomplete)~$2.60
Login or cookiesNoVariesNever
OutputTwo schemas, raw API fieldsVariesOne clean schema

Proven at scale

Two production runs on 7 August 2026, across two different public groups, logged out:

Group AGroup BTotal
Posts17,5999,61227,211
Comments46,22423,99870,222
Comments with no identifiable parent000

Every one of those 70,222 comments landed in the right place. Not one orphan across either run.

More than a third of them were replies — comments that answer another comment rather than the post. A scraper that flattens replies hands you those as if they were standalone opinions. On a Facebook group, that is a third of the conversation silently misattributed.

It keeps going when Facebook goes quiet

Facebook never announces the end of an anonymous feed. It just stops returning posts — and a temporary block looks exactly the same as a finished feed.

In the 17,599-post run above, Facebook went silent five separate times. Each time, this Actor waited and asked again, and each time the posts resumed. A scraper that took the first silence at face value would have stopped at 11,149 posts and reported success — losing 36% of the run without a single error message.


Quick start

  1. Paste one or more public Facebook group URLs into Facebook group URLs.
  2. Set Max posts per group — or 0 for every post the group feed serves.
  3. Set Max comments per post — or 0 for every comment.
  4. Click Start.

That's the whole setup. No cookies to export, no session to keep alive, no account to warm up.

Any public group URL form works:

https://www.facebook.com/groups/874728723021553
https://www.facebook.com/groups/my-group-slug/
https://m.facebook.com/groups/123?ref=share

What you get

Exactly two things: posts, and their comments. No group metadata dumps, no marketplace fields, no leftover API internals.

{
"postId": "9988776655",
"postUrl": "https://www.facebook.com/groups/12345/posts/9988776655/",
"groupId": "12345",
"groupName": "North Side Neighbours",
"text": "Looking for a plumber in the north side, any recommendations?",
"publishedAt": "2026-08-02T16:00:00.000Z",
"author": {
"id": "user-1",
"name": "Dana Whitfield",
"profileUrl": "https://www.facebook.com/dana"
},
"stats": { "reactions": 42, "comments": 17, "shares": 3 },
"comments": [
{
"commentId": "1001",
"parentCommentId": null,
"text": "Rivera Plumbing did our bathroom, very fair.",
"publishedAt": "2026-08-02T17:00:00.000Z",
"author": { "id": "u2", "name": "Sam Okafor", "profileUrl": "https://www.facebook.com/sam" },
"likes": 5,
"replyCount": 1,
"replies": [
{
"commentId": "1002",
"parentCommentId": "1001",
"text": "Seconding Rivera, they came out same day.",
"publishedAt": "2026-08-02T17:24:00.000Z",
"author": { "id": "u3", "name": "Priya Raman", "profileUrl": null },
"likes": 2,
"replyCount": 0,
"replies": []
}
]
}
],
"scrapedCommentsCount": 17,
"declaredCommentsCount": 17
}

Every reply is nested under the comment it answers and names its parent outright, so the thread survives being flattened, filtered, or re-sorted downstream.

Working in spreadsheets? Turn on One row per comment and every comment becomes its own row, with parentCommentId and depth preserving the structure.


Large groups: resume from where you stopped

This is the feature that makes big groups possible.

Facebook does not hand an anonymous visitor a 500,000-post archive, and no single run will get one. What matters is what happens next — and for most scrapers, the answer is: you run it again and pay to collect the same posts a second time.

This Actor records the feed position on every run. Turn on Resume from where the last run stopped and the next run continues from that exact position.

It works. Group B above delivered 3,678 posts in an earlier run and 9,612 more in the next one — 13,290 posts from a single group, with no post collected or paid for twice.

How to use it

  1. First run — leave the option off. Set Max posts per group to 0. Let it run until it stops.

  2. Read the last line of the log. It names exactly how the run ended:

    In the logWhat it meansRun again?
    [exhausted] / [end-of-feed]Facebook served the whole feed.No — nothing is left.
    [feed-empty] / [feed-repeating]Facebook went quiet. The position is saved.Yes
    [throttled] / [error]Blocked, or a request failed. The position is saved.Yes
    [max-posts] / [too-old]You got exactly what you asked for.Only to go deeper.
  3. Next run — turn Resume from where the last run stopped on. Repeat until you see [exhausted].

Schedule it and the crawl gets deeper every day, without ever re-collecting a post you already paid for.

Details worth knowing

  • It resumes deeper into older posts — it is not how you pick up newly published ones. For that, use Only posts newer than.
  • The position is recorded whether or not the option is on, so you can switch it on after a run has already stopped and still carry on from there.
  • The position is only discarded when Facebook confirms the feed is finished. An ambiguous ending keeps it, because losing a valid position costs re-walking the whole feed, while keeping a dead one costs a single wasted page.
  • A stale position self-heals. If Facebook no longer honours the saved position, the run says so and the next one starts fresh.
  • Platform migrations are handled automatically. If Apify relocates your run to another server mid-crawl, it picks up where it left off — and Max posts per group still means what you asked for across the whole run.

How far can one run get?

Measured at 3,200–3,500 posts an hour with full comment threads, across both runs above.

Two things end a long run before the group does. Facebook may go quiet for good — that is what the log codes are for. And your own Apify residential proxy quota may run out first; that is what ended the 17,599-post run. Either way the position is saved, so the next run continues rather than restarting.


Input

FieldTypeDefaultWhat it does
startUrlsarrayPublic group URLs. Required.
maxPostsinteger20Posts per group. 0 = no limit.
maxCommentsPerPostinteger0Comments per post, replies included. 0 = all.
includeRepliesbooleantrueExpand nested reply threads.
onlyPostsNewerThanstring2026-07-01 or 7 days. Stops paginating past it.
sortByenumCHRONOLOGICALFeed order: chronological, recent activity, or top posts.
commentsOrderenumALLALL is the most complete; NEWEST and TOP mirror Facebook's own orderings.
flattenCommentsbooleanfalseOne row per comment instead of nested.
resumeFromLastRunbooleanfalseContinue a group's feed from where the last run stopped.
maxConcurrencyinteger8Posts processed in parallel.
maxRequestRetriesinteger4Retries before an item is given up on.
proxyConfigurationobjectResidentialResidential proxies strongly recommended.

Pricing

Pay per event — you pay for data you actually receive, and nothing else.

EventPrice
Post scraped$0.004
Comment scraped$0.0006
Actor start$0.00005

What that works out to:

JobCost
100 posts + 500 comments$0.70
200 posts + 3,000 comments$2.60
1,000 posts + 1,000 comments$4.60
1,000 posts + 2,500 comments$5.50

Comments are priced separately from posts because they are the work: a post is one unit, and its comments are however many more it takes to reach the end of the thread. Pricing them together would mean quiet posts subsidising busy ones.

Cheaper on higher Apify plans

FreeStarterScaleBusiness
1,000 posts$4.00$3.80$3.60$3.40
1,000 comments$0.60$0.57$0.54$0.51

Platform usage is included

You pay the prices above and nothing else. This Actor does not pass Apify platform usage — compute units, proxy traffic, storage operations — on to you as a separate line. Many scrapers do, which is what turns a quoted price into a surprise at the end of the month.

What is never charged

  • Failed groups. A private group, a login wall, a group that no longer exists — each returns an error record naming the cause, and none of them are billed.
  • Retries and blocked requests. The charge happens when a post is written to your dataset, not when a request is made. A run that fights through throttling to deliver 500 posts bills you for 500 posts.
  • Comments Facebook never served. scrapedCommentsCount is what was actually captured; you are not charged for the gap against Facebook's own counter.

Spending limits are honoured

Set a maximum cost per run and this Actor respects it: the run stops cleanly at the limit and keeps everything collected up to that point, rather than being killed mid-write and losing the tail of the data you already paid for.

Your feed position is saved either way, so a run that stops at its limit can be continued by the next one with Resume from where the last run stopped — you never pay twice for the same post.


FAQ

scrapedCommentsCount is lower than declaredCommentsCount. Is something missing? No. Facebook's own counter includes comments that are deleted, hidden by moderators, or filtered as spam — counted but never served to visitors. scrapedCommentsCount is what genuinely existed and was captured. Both numbers are in the output so the difference is always visible.

Can it scrape private groups? No, and that is deliberate. Private groups require a logged-in account. Cookie-based scrapers break constantly, get accounts locked, and produce the worst reliability of any approach. This Actor only reads what a logged-out visitor can see — which is why it has no account to lose.

How far back can it go? As far as Facebook's group feed serves, which is often several months on an active group. There is no archive search behind the feed, so Only posts newer than filters what the feed returns rather than unlocking older history.

A very large group stopped before I got everything. Expected — see Large groups. Check the last log line, turn on resume, run again.

A group returned an error record instead of posts. Each error record names the cause in plain language — private group, login wall, group not found. One bad group never fails the run, and error records are never billed.

Can I use it with AI agents? Yes. The Actor declares its output schema, so agents reaching it through Apify's MCP server or the API know what to expect and can chain it into a workflow without guesswork.

Is it fast? 3,200–3,500 posts an hour with every comment thread attached, measured across the runs above. There is no browser to start, no page to render, and no screenshot to wait for.