Facebook Group Posts & All Photos Scraper avatar

Facebook Group Posts & All Photos Scraper

Pricing

from $2.24 / 1,000 facebook group posts

Go to Apify Store
Facebook Group Posts & All Photos Scraper

Facebook Group Posts & All Photos Scraper

Scrape public Facebook group posts with all recoverable photo URLs, including hidden +N grids. Get text, timestamps, authors, engagement, stable post URLs, and agent-ready JSON for Apify API, MCP, monitoring, and research workflows.

Pricing

from $2.24 / 1,000 facebook group posts

Rating

5.0

(1)

Developer

Sergei Belostotskii

Sergei Belostotskii

Maintained by Community

Actor stats

1

Bookmarked

6

Total users

2

Monthly active users

8 days ago

Last modified

Share

Run on Apify Source on GitHub AI agents LICENSE

Scrape latest or historical posts from public Facebook groups into clean, agent-ready JSON. Get post text, Facebook creation timestamps, authors, engagement counters, stable post URLs, and all recoverable photo URLs, including photos hidden behind Facebook +N grids.

One paid result = one Facebook group post. Expanded photo URLs are included in the same dataset row and the same per-post price. No Facebook account or cookie input is required from the user.

Best forNot designed for
Public Facebook groups, latest-post monitoring, historical backfills, photo-heavy posts, AI agents, MCP, API and scheduled pipelinesPrivate/login-only groups, Facebook Pages or profiles, Marketplace search, global keyword search, expanded comments, video downloads or transcripts

Start here: Run the Actor · Latest-post task · All-photos task · GitHub · Agent guide · Output schema

Table of contents

Why this Actor

The missing-photo problem

Facebook often renders five preview images and a +N overlay, while the remaining photos live behind a separate media set. A preview-oriented scraper can return a valid post row and still silently lose most of the product, apartment, event, or evidence photos.

This Actor treats photo completeness as part of the post, not as an optional afterthought:

  1. Collect the public group feed and post metadata.
  2. Detect media-set tokens and suspicious +N layouts.
  3. Expand recoverable photo sets.
  4. Retry suspicious rows before final output.
  5. Report media quality fields instead of quietly declaring victory over an incomplete album.

Feature comparison

CapabilityPreview-only extractionThis Actor
Public group post textUsuallyYes, including recoverable nested/attached-story text
Stable post ID and permalinkVariesYes, when Facebook exposes them
Facebook creation timeVariescreated_at plus evidence in created_at_source
Author identityVariesID, name and profile URL when exposed
Feed preview photosYesYes
Hidden +N photo setOften truncatedExpansion and fallback attempts
Photo auditabilityRarePreview/final counts, risk flag and review severity
Latest-post monitoringOften reprocesses old rowsknownPostIds, onlyPostsNewerThan and checkpoints
Date-window collectionOften requires downstream cleanupNative lower and upper Facebook timestamp boundaries
Historical continuationVariesCursor-backed 1,000-post chunks
AI-agent selectionGeneric input/outputExplicit schemas, MCP prompts, llms.txt and diagnostics
Result grainCan be unclearExactly one dataset item per post

Measured validation

The public release was validated in Apify cloud against real photo-heavy public groups. These are observed test results, not a promise that Facebook will behave identically every Tuesday.

ValidationUnique postsFinal photo URLsPhotos recovered beyond feed previewDuplicate post IDsMedium/high media-review rows
6 groups x 200 posts1,2006,3071,97500
1 group x 1,000 posts1,0004,9591,66200

The deepest validated single post contained 24 photos. Historical collection has also been tested beyond 5,000 posts through cursor continuation. See benchmark guidance.

Trust signals

SignalDetails
Open sourceReview the implementation on GitHub.
CI testedParser, media-quality, proxy-session and settings tests run in GitHub Actions.
No user Facebook credentialsThe Actor accepts public group URLs, not Facebook cookies or passwords.
Structured outputOne post per row with stable IDs, timestamps, media and quality fields.
Explicit diagnosticsSUMMARY distinguishes complete runs, date/known-post boundaries, partial runs and temporary login walls.
Agent documentationllms.txt, Agent guide and MCP guide.

Quick start

Run from Apify Console

  1. Open the Actor input page.
  2. Paste one or more public Facebook group URLs.
  3. Keep New posts first and Expand all photos enabled.
  4. Set the number of posts per group and click Start.

Minimal input:

{
"groupUrls": [
"https://www.facebook.com/groups/1564552680458634/"
],
"maxPostsPerGroup": 50,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": true
}

Each result in the default dataset is one post. After the run, also read the SUMMARY record from the default key-value store for coverage, photo totals, warnings and continuation state.

Supported group URL formats

https://www.facebook.com/groups/1564552680458634/
https://www.facebook.com/groups/chothuenhanguyencannhatrang/
1564552680458634

Use a group root URL or group ID. Do not submit a post permalink, profile URL, Page URL or Marketplace URL.

Multiple groups in one run

{
"groupUrls": [
"https://www.facebook.com/groups/chothuenhanguyencannhatrang/",
"https://www.facebook.com/groups/1564552680458634/",
"https://www.facebook.com/groups/nhamatbangnhatrang/"
],
"maxPostsPerGroup": 100,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": true,
"mediaExpansionConcurrency": 3
}

Up to 10 public groups can be processed in one run. Inspect SUMMARY.groups[] because one temporarily unavailable group should not erase healthy results from the others.

What data you get

One normalized row per post

DataMain fields
Identitysource_post_id, source_url, group_id, group_url, group_name
Timecreated_at, created_at_source
Textraw_text, text_source, text_missing_reason
Authorauthor.source_user_id, author.name, author.url
Engagementstats.likes, stats.comments, stats.shares when available
Photosmedia[].source_media_id, source_url, thumbnail_url, dimensions and source
Media auditmedia_preview_count, media_final_count, media_completeness, media_review_severity, media_plus_n_risk
Run contextcoverage_status, warnings, pagination

Run-level SUMMARY

The default key-value store receives a SUMMARY record. Agents and production pipelines should inspect it before accepting a run as complete.

FieldWhy it matters
coverageStatusSays whether the target, known-post boundary or date boundary was reached, or whether the run was partial/blocked.
outputPostsNumber of post rows produced.
groups[]Per-group status for multi-group runs.
extraPhotosFoundPhotos recovered beyond feed previews.
mediaReviewSeverityCountsDistribution of media-audit severity.
pointer.nextCursorCursor for the next older backfill chunk.
outputCheckpointState that can be stored by monitoring pipelines.
warningsNon-fatal conditions that deserve inspection.

See the complete output schema and coverage statuses.

Export formats

Use Apify Dataset to export results as JSON, JSONL, CSV, Excel or XML, or send them onward through an integration or webhook.

How all-photo recovery works

When expandAllPhotos=true, media is resolved in stages:

Public group feed
-> preview photos
-> media-set token detection
-> photo-set expansion
-> permalink fallback for suspicious +N rows
-> deferred retry
-> final media list + quality flags
FieldInterpretation
media_preview_countPhotos visible in the group feed payload.
media_expanded_countPhotos found through media-set expansion.
media_final_countFinal deduplicated photo count returned.
media_completenessFinal recovery classification.
media_review_severitynone, low, medium or high; review medium and high.
media_plus_n_risktrue when evidence suggests Facebook hid more photos than were recovered.

The Actor returns source photo URLs, not binary image files. Facebook CDN URLs can expire, so download files promptly if your workflow needs durable storage.

Run recipes

GoalRecommended configuration
Small evaluation10-20 posts, CHRONOLOGICAL, cursor_page, all photos enabled
Latest postsStart at the top with CHRONOLOGICAL and no startCursor
Daily monitoringStart at the top and stop with knownPostIds or onlyPostsNewerThan
Specific time periodCombine onlyPostsNewerThan and onlyPostsOlderThan
Photo-complete collectionKeep expandAllPhotos=true and concurrency at 3
Fast preview-only testSet expandAllPhotos=false
Historical historyUp to 1,000 posts per group, then continue with pointer.nextCursor
Multi-group monitoringUp to 10 group URLs; inspect every entry in SUMMARY.groups[]

Latest public posts

{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 50,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": true
}

Do not pass an old startCursor when the goal is the newest posts. A backfill cursor points toward older history.

Daily incremental monitoring

Start from the newest feed on every scheduled run and stop when a post already stored by your system appears:

{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 200,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"knownPostIds": ["LAST_STORED_POST_ID"],
"expandAllPhotos": true
}

Alternative date boundary:

{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 200,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"onlyPostsNewerThan": "2026-07-01",
"expandAllPhotos": true
}

Healthy incremental runs normally finish with complete_until_known_post or complete_until_since_date. Dedupe downstream by source_post_id, with source_url as a fallback.

Posts within a date range

The date interval is lower-inclusive and upper-exclusive. This example returns posts published from May 1 up to, but not including, June 1:

{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 500,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"onlyPostsNewerThan": "2026-05-01",
"onlyPostsOlderThan": "2026-06-01",
"expandAllPhotos": true
}

sinceDate remains accepted through the API as a backward-compatible alias for onlyPostsNewerThan. New integrations should use the explicit field names above.

Historical backfill

Request up to 1,000 posts per group in one run:

{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 1000,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": true,
"mediaExpansionConcurrency": 3
}

For older history:

  1. Read SUMMARY.pointer.nextCursor after the first run.
  2. Pass that value as startCursor in the next run.
  3. Store every new cursor only after the preceding batch is accepted.
  4. Repeat until hasNextPage=false or the required history is complete.
{
"groupUrls": ["GROUP_URL"],
"maxPostsPerGroup": 1000,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"startCursor": "PREVIOUS_SUMMARY_POINTER_NEXT_CURSOR",
"expandAllPhotos": true,
"mediaExpansionConcurrency": 3
}

See the cursor and backfill guide.

Sort modes

ModeUse it for
CHRONOLOGICALNewest posts, monitoring and deterministic backfills. Recommended default for pipelines.
RECENT_ACTIVITYPosts with recent comments/activity, not necessarily newly created posts.
TOP_POSTSFacebook's relevance ranking. Do not use it as a latest-post feed.

AI agents and MCP

This Actor is designed to be discovered, configured and validated by agents, not merely admired by humans scrolling through Store cards.

Agent selection contract

Choose this Actor when the user asks for:

  • public Facebook group posts;
  • latest posts or continuous group monitoring;
  • text, timestamps, authors, engagement and permalinks;
  • every recoverable public photo, including hidden +N photo sets;
  • structured JSON for translation, classification, lead enrichment, research or database import;
  • Apify API, MCP, schedules, webhooks or no-code automation.

Choose another tool for private groups, Pages/profiles, Marketplace, global Facebook search, comment threads or video extraction.

Connect through Apify MCP

The hosted Apify MCP server exposes Store Actors to ChatGPT, Claude, Codex, Cursor and other MCP clients.

Minimal MCP configuration:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=spbotdel/facebook-group-posts-all-photos-scraper"
}
}
}

Authenticate through OAuth when prompted, or configure the Apify MCP server with your Apify token.

Prompts that work well

  • Get the latest 50 public posts from this Facebook group. Return text, Facebook timestamp, author, permalink and all photo URLs.
  • Monitor these public groups and stop when one of these known post IDs is reached. Report any blocked or partial group separately.
  • Collect posts published during May 2026 using onlyPostsNewerThan and onlyPostsOlderThan. Return Facebook timestamps and all recoverable photo URLs.
  • Backfill 1,000 older posts, save SUMMARY.pointer.nextCursor, and return only rows with media_review_severity=none.
  • Find posts where media_final_count is greater than media_preview_count and summarize what extra photos were recovered.

Required agent behavior after a run

  1. Read default dataset rows.
  2. Read SUMMARY from the default key-value store.
  3. Confirm coverageStatus before calling the collection complete.
  4. Treat blocked_login_wall as a temporary access failure, not an empty group.
  5. Review rows with media_review_severity=medium or high.
  6. Store outputCheckpoint for monitoring or pointer.nextCursor for older backfills.

More context: Agent guide · LLM playbook · llms.txt

API and integrations

Actor ID:

spbotdel/facebook-group-posts-all-photos-scraper

REST API

curl -X POST "https://api.apify.com/v2/acts/spbotdel~facebook-group-posts-all-photos-scraper/runs?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"groupUrls": ["https://www.facebook.com/groups/1564552680458634/"],
"maxPostsPerGroup": 50,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": true
}'

The response contains the run ID. Wait for completion, then read the run's default dataset and the SUMMARY record in its default key-value store.

Python

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor(
"spbotdel/facebook-group-posts-all-photos-scraper"
).call(run_input={
"groupUrls": ["https://www.facebook.com/groups/1564552680458634/"],
"maxPostsPerGroup": 50,
"sortMode": "CHRONOLOGICAL",
"paginationMode": "cursor_page",
"expandAllPhotos": True,
})
posts = client.dataset(run["defaultDatasetId"]).list_items().items
summary = client.key_value_store(
run["defaultKeyValueStoreId"]
).get_record("SUMMARY")["value"]
print(len(posts), summary["coverageStatus"])

Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client
.actor('spbotdel/facebook-group-posts-all-photos-scraper')
.call({
groupUrls: ['https://www.facebook.com/groups/1564552680458634/'],
maxPostsPerGroup: 50,
sortMode: 'CHRONOLOGICAL',
paginationMode: 'cursor_page',
expandAllPhotos: true,
});
const { items: posts } = await client.dataset(run.defaultDatasetId).listItems();
const summaryRecord = await client
.keyValueStore(run.defaultKeyValueStoreId)
.getRecord('SUMMARY');
console.log(posts.length, summaryRecord.value.coverageStatus);

Apify CLI

$apify call spbotdel/facebook-group-posts-all-photos-scraper -p '{"groupUrls":["https://www.facebook.com/groups/1564552680458634/"],"maxPostsPerGroup":50,"sortMode":"CHRONOLOGICAL","paginationMode":"cursor_page","expandAllPhotos":true}'

Schedules, webhooks and no-code tools

  • Use Apify Schedules for daily or hourly monitoring.
  • Add a webhook on ACTOR.RUN.SUCCEEDED to import the dataset into your application.
  • Connect the Actor through Apify integrations in Make, n8n or Zapier.
  • Export datasets directly to JSON, CSV or Excel for manual workflows.

Reliability and troubleshooting

SituationRecommendation
First run on a new groupStart with 10-20 posts and verify output.
Normal all-photo runKeep mediaExpansionConcurrency=3, adaptive expansion and deferred retry enabled.
Latest-post automationUse CHRONOLOGICAL, cursor_page, no startCursor.
Deep historyUse cursor chunks and persist the cursor only after a successful import.
Several groupsInspect each group in SUMMARY.groups[]; isolate retries by group.
Durable image storageDownload CDN URLs soon after collection.

Coverage statuses

SUMMARY.coverageStatusMeaningAction
complete_target_reachedRequested post count was collected.Accept the dataset.
complete_until_known_postMonitoring reached a supplied known post ID.Accept as a healthy incremental run.
complete_until_since_dateMonitoring reached the requested date boundary.Accept as a healthy incremental run.
blocked_login_wallFacebook temporarily returned a login wall for a public group.Retry that group in a fresh run/session.
bootstrap_failedThe public group page could not be initialized reliably.Verify the URL and retry; inspect bootstrap attempts.
partial_before_targetCollection started but stopped before target.Inspect stopReason, warnings and retry history.
partial_target_not_reachedFewer rows than requested were returned.Check whether the group ended or Facebook interrupted pagination.

Why did I receive fewer posts than requested?

Common reasons:

  • the group has fewer publicly available posts;
  • knownPostIds or onlyPostsNewerThan intentionally stopped the run;
  • the paid-result or max-cost limit was reached;
  • Facebook temporarily returned an empty page or login wall;
  • a post was deleted, unavailable or lacked a stable post identity;
  • pagination ended before the requested target.

The run should not be judged by dataset length alone. Read SUMMARY.coverageStatus, stopReason, warnings, bootstrapAttempts[] and groups[].

A public group returned zero posts

Do not immediately classify it as empty. If coverageStatus=blocked_login_wall, retry the same group with a new Actor run. For scheduled multi-group jobs, accept healthy groups and retry only the blocked source.

Photo URLs stopped working later

Facebook CDN URLs are tokenized and can expire. The Actor returns source URLs at collection time; your pipeline is responsible for downloading files when long-term storage is required.

Pricing

$2.49 per 1,000 Facebook group posts, plus a $0.00005 Actor-start event.

ResultsPost-result charge
20 posts$0.0498
100 posts$0.2490
1,000 posts$2.4900

The billable result is Facebook group post:

  • one dataset item equals one public Facebook group post;
  • all recovered photo URLs are included in that same result;
  • there is no per-photo event charge;
  • platform usage is included under the current Store pricing configuration.

The pricing card on the Actor page is authoritative if pricing changes later.

FAQ

Do I need a Facebook account or cookies?

No Facebook credentials are requested from the user. The Actor targets content exposed by public Facebook groups. Facebook can still temporarily show a login wall; the Actor reports that condition in SUMMARY instead of pretending the group is empty.

Does it work with private groups?

No. Private, closed or login-only groups are outside the Actor's scope.

Does it really return every photo?

It returns every recoverable public photo URL and specifically expands hidden +N photo sets. Facebook can remove, hide or expire media, so the Actor also returns quality flags rather than making an impossible absolute guarantee.

How can I audit possible missing photos?

Check media_preview_count, media_final_count, media_review_severity, media_plus_n_risk and run-level media counts in SUMMARY.

Are post timestamps the Facebook creation time or scrape time?

created_at is the Facebook post creation time when exposed. created_at_source records the evidence used. The Actor does not replace a missing creation time with the scrape time.

Does it recover text from attached or nested posts?

Yes, when that text is present in the public payload. text_source shows whether text came from the direct message or a nested fallback path.

Yes, through cursor continuation. The current public input limit is 1,000 posts per group per run; save SUMMARY.pointer.nextCursor and continue older history in subsequent runs.

Can it return only new posts every day?

Yes. Schedule a newest-first run and pass knownPostIds, onlyPostsNewerThan or a stored checkpoint as the stop boundary. Do not use yesterday's backfill cursor to search for today's new posts.

Can I collect posts from a specific date range?

Yes. Set onlyPostsNewerThan as the inclusive lower boundary and onlyPostsOlderThan as the exclusive upper boundary. Use CHRONOLOGICAL with cursor_page for predictable traversal.

Can I search posts by keyword?

This Actor collects a public group's feed; it is not Facebook-wide search. Filter raw_text downstream, or use a dedicated Facebook search Actor when the source group is unknown.

Does it scrape comments?

It returns comment counters when Facebook exposes them, not expanded comment bodies or replies.

What about video?

Video downloads, direct video URLs and transcripts are not part of the product guarantee. This Actor is deliberately focused on posts and photos.

Does it download images into Apify storage?

No. It returns image source URLs. Download them in your own pipeline if durable binary storage is needed.

Can I export to CSV or Excel?

Yes. Use the default dataset's export controls in Apify Console or the dataset API.

Limitations and responsible use

BoundaryDetail
Public groups onlyNo private-group membership, cookie or login workflows.
No access bypassThe Actor does not bypass checkpoints, permissions, rate limits or security controls.
Facebook variabilityFields can be absent when Facebook does not expose them.
CommentsCounters only, no expanded threads.
VideosNo guaranteed video URL, download or transcript.
ImagesSource URLs only; URLs can expire.
Marketplace, Pages and profilesUse a target-specific Actor.
Media completenessInspect quality fields for suspicious rows.

Use this Actor only for public data you are allowed to collect and process. Respect Facebook's terms, group rules, privacy expectations and applicable law. Do not use the output for harassment, unauthorized profiling or other harmful activity.

Ready-made task pages

TaskBest for
Latest public Facebook group postsA newest-first sample from one group.
Facebook group posts with all photosPhoto-heavy posts and hidden +N grids.
Daily Facebook group monitoringScheduled incremental collection.
Historical Facebook group backfillOlder posts with cursor continuation.
Monitor public Facebook groupsMulti-group monitoring.

Need posts from public personal profiles instead of groups? Use Facebook Profile Posts & All Photos Scraper.

Documentation

DocumentPurpose
Agent guideTool selection and safe run defaults.
LLM playbookAgent recipes and post-run interpretation.
MCP/API usageAgent and API integration.
Output schemaPost fields and SUMMARY diagnostics.
Cursor backfillIncremental monitoring and historical continuation.
LimitationsSupported and unsupported targets.

Get help

Open an issue on the Apify Actor page or GitHub. Include:

  • public group URL;
  • Apify run ID;
  • approximate post URL when available;
  • whether the problem concerns text, timestamp, photos, pagination or coverage;
  • relevant SUMMARY.coverageStatus, stopReason and warnings.

For private support, contact spbotdel@gmail.com.