Facebook Page Scraper – Posts, Photos & Details avatar

Facebook Page Scraper – Posts, Photos & Details

Pricing

from $1.00 / 1,000 posts

Go to Apify Store
Facebook Page Scraper – Posts, Photos & Details

Facebook Page Scraper – Posts, Photos & Details

Scrape any public Facebook page: its latest posts with full text, reactions, comments, shares and media (52 fields each), or the page itself — About block, follower count, photo albums and video library. No login and no cookies. Unofficial.

Pricing

from $1.00 / 1,000 posts

Rating

5.0

(1)

Developer

Simple Actors

Simple Actors

Maintained by Community

Actor stats

0

Bookmarked

12

Total users

8

Monthly active users

17 days ago

Last modified

Share

Scrape any public Facebook page two ways from one input. mode: "posts" (the default) returns the page's latest posts — full text, publish date, reactions, comments, shares, photos and video, newest first, 52 fields per post. mode: "details" returns the page itself: its About block with category, email, phone, website and address, its follower count, profile and cover photo, its photo albums and its video library. The two read different parts of Facebook and neither substitutes for the other. Built for social media monitoring, brand and competitor tracking, lead lists and CRM enrichment.

Built for the common case: the last handful of posts. One run is two requests and finishes in seconds.

No login, no cookies, no account of yours involved. It reads the same post data Facebook already server-renders for logged-out visitors, so the login box you see on the page is never in the way.

Unofficial. Not affiliated with, endorsed by, or sponsored by Facebook or Meta.

Features

  • Two modes, one input — latest posts, or the page's own About block, photos and videos.
  • 52 fields per post — text, SEO title, hashtags, links, publish time, reactions broken out per type, comments, shares, view counts and media.
  • Reaction breakdown as columnsreactionLikeCount, reactionLoveCount, reactionHahaCount, reactionWowCount, reactionSadCount, reactionAngryCount, reactionCareCount, ready for a spreadsheet.
  • Photos and video separatedimages with dimensions, videos with separate SD and HD URLs, thumbnail, duration and a captions track when one exists, plus the combined media.
  • Outbound links unwrapped — the real destination, not the l.facebook.com redirect Facebook rewrites links into.
  • Drop-in field namestime/timestamp, likes/comments/shares, media, textReferences, so existing code reads this Actor unchanged.
  • Page About block for lead data — category, email, phone, website, address and hours, plus aboutFields carrying every value the page states with its type.
  • Incremental pollingonlyPostsNewerThan takes 20 hours, last 3 days, 90 minutes or an ISO date.
  • URL forms normalised — vanity, profile.php?id=… and m.facebook.com links all work.
  • One bad page never costs you the good ones — an unreadable page comes back as its own row and the run still succeeds.

Input

{
"startUrls": [{ "url": "https://www.facebook.com/CoJCTN" }], // public page URLs
"mode": "posts", // "posts" for latest posts, "details" for the page itself
"maxPosts": 5, // latest posts to return, newest first (1–8)
"onlyPostsNewerThan": "20 hours", // optional window: "last 3 days", "90 minutes", "2026-08-01"
"includeRaw": false, // attach Facebook's untouched post object under `raw`
"proxy": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }
}

startUrls is the only required field. Everything else has a working default.

FieldTypeDefaultWhat it does
startUrlsarrayRequired. Public Facebook page URLs. Vanity, numeric and m.facebook.com forms all work.
modestringpostsposts = one row per post. details = one row per page (About, photos, videos).
maxPostsinteger5Latest posts to return. Maximum 8 — one request is all Facebook serves. Posts mode only.
onlyPostsNewerThanstringKeep only posts after a window or ISO date. Posts mode only.
includeRawbooleanfalseAttach Facebook's raw post object. Makes items much larger. Posts mode only.
includePhotosbooleantrueRead the Photos tab and album list. Details mode only.
includeVideosbooleantrueRead the Videos tab. Details mode only.
proxyobjectresidentialResidential is required — Facebook answers the posts request from a datacenter IP with a rate-limit error.

Reading the page itself, not its posts

Set mode to details:

{
"startUrls": [{ "url": "https://www.facebook.com/nasa" }],
"mode": "details"
}

That returns one row per page instead of one per post:

{
"resultType": "page-details",
"pageName": "NASA - National Aeronautics and Space Administration",
"pageId": "100044561550831",
"isVerified": true,
"category": "Government organisation",
"email": "public-inquiries@hq.nasa.gov",
"websites": ["http://www.nasa.gov/", "https://www.nasa.gov/nasa-app/"],
"followersText": "28M followers",
"followers": 28000000,
"followersAreExact": false,
"coverPhoto": "https://scontent.../cover.jpg",
"aboutFields": [
{ "section": "category", "sectionTitle": "Categories", "type": "category", "value": "Government organisation" },
{ "section": "about_contact_info", "sectionTitle": "Contact info", "type": "profile_email", "value": "public-inquiries@hq.nasa.gov" }
],
"photos": [{ "id": "...", "url": "https://www.facebook.com/photo.php?fbid=...", "image": "https://scontent.../photo.jpg" }],
"albums": [{ "title": "Cover photos", "url": "https://www.facebook.com/media/set/?set=a...", "coverImage": "..." }],
"videos": [{
"url": "https://www.facebook.com/NASA/videos/.../27340367028974495/",
"title": "NASA's Curious Universe - Roman Series - Episode 2",
"durationSeconds": 29,
"time": "2026-08-19T...Z",
"playCount": 251617,
"likes": 1606
}],
"photoCount": 8, "albumCount": 8, "videoCount": 18
}

aboutFields is the field that will not let you down. Facebook states each About value with a type — category, profile_email, website — and a page publishes whichever ones apply to it, so a restaurant carries an address, a phone and opening hours where a government page carries none of them. The named keys (email, phone, website, address, category, hours) are a convenience over aboutFields, and aboutFields carries everything the page stated, including field types that postdate this Actor. If a named key is empty, look there before concluding the page does not publish the value.

Follower counts are Facebook's rounded display figure, and the row says so. The page header carries "28M followers" and no exact number anywhere, so followersText is kept verbatim, followers is parsed from it, and followersAreExact is false. Do not trend followers — a rounded figure does not move for months, and then jumps.

One caveat on hours: measured on a real page it carries Facebook's rendered status — "Closed now" — rather than a weekly schedule, so read it as a display string, not as opening times.

includePhotos and includeVideos each default to true and each cost a page load, so turn them off when you only want the About block — that is the difference between $0.004 and $0.010 a page, and it is why the two are priced separately rather than as one number. A real About-only read of a city government page returned category, address, phone, email, website and hours in six fields for one page load.

Why this is not just "read the latest posts"

The two modes read different parts of Facebook, and neither substitutes for the other:

  • The About block is not in the post feed at all. No category, email, website, phone or follower count appears on a post.
  • The media on a page's latest posts is not its library. A page whose recent posts are text or links returns no media at all, while its Videos tab still holds years of them — NASA's returned 18 videos reaching well past its latest 8 posts, and the Photos tab includes album and cover photos that were never in a post.

Output

One dataset item per post — the full text, when it was posted, how it performed, and every photo or video it carries:

{
"facebookUrl": "https://www.facebook.com/CoJCTN",
"postId": "1369546808618058",
"storyId": "UzpfSTEwMDA2Njg4MjU1MDQ0ODoxMzY5NTQ2ODA4NjE4MD…",
"url": "https://www.facebook.com/reel/946342075161908/",
"postType": "video",
"title": "Assistant City Manager Nick Geis provides information about the City's Stormwater Division and recent rain events in this video",
"text": "Assistant City Manager Nick Geis provides information about the City's Stormwater Division and recent rain events in this video. T…",
"hashtags": [],
"links": [],
"time": "2026-08-12T20:58:11.000Z",
"timestamp": 1786568291,
"timeCreated": "2026-08-12T20:58:11.000Z",
"timestampCreated": 1786568291,
"likes": 101,
"topReactions": [
{
"type": "Like",
"count": 93
},
{
"type": "Love",
"count": 4
},
{
"type": "Haha",
"count": 2
},
{
"type": "Wow",
"count": 1
},
{
"type": "Angry",
"count": 1
}
],
"topReactionsCount": 5,
"reactionLikeCount": 93,
"reactionLoveCount": 4,
"reactionHahaCount": 2,
"reactionWowCount": 1,
"reactionSadCount": 0,
"reactionAngryCount": 1,
"reactionCareCount": 0,
"comments": 14,
"shares": 10,
"viewsCount": 4027,
"videoPostViewCount": 4027,
"liveViewerCount": null,
"isVideo": true,
"isShare": false,
"sharedPost": null,
"paidPartnership": false,
"media": [
{
"url": "https://www.facebook.com/reel/946342075161908/",
"sdUrl": "https://video.fmad8-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQPql-…",
"hdUrl": "https://video.fmad8-1.fna.fbcdn.net/o1/v/t2/f2/m366/AQMeSx…",
"thumbnailUrl": "https://scontent.fmad7-1.fna.fbcdn.net/v/t15.5256-10/77312…",
"durationMs": 83797,
"isLive": false,
"captionsUrl": "https://scontent.fmad7-1.fna.fbcdn.net/v/t39.2093-6/773744…",
"publishedAt": "2026-08-12T20:57:35.000Z",
"id": "946342075161908"
}
],
"imageCount": 0,
"videoCount": 1,
"images": [],
"videos": [
{
"url": "https://www.facebook.com/reel/946342075161908/",
"sdUrl": "https://video.fmad8-1.fna.fbcdn.net/o1/v/t2/f2/m412/AQPql-…",
"hdUrl": "https://video.fmad8-1.fna.fbcdn.net/o1/v/t2/f2/m366/AQMeSx…",
"thumbnailUrl": "https://scontent.fmad7-1.fna.fbcdn.net/v/t15.5256-10/77312…",
"durationMs": 83797,
"isLive": false,
"captionsUrl": "https://scontent.fmad7-1.fna.fbcdn.net/v/t39.2093-6/773744…",
"publishedAt": "2026-08-12T20:57:35.000Z",
"id": "946342075161908"
}
],
"link": "http://johnsoncitytn.org/",
"textReferences": [
{
"type": "ExternalUrl",
"id": "NjQyMTgzOTU5MjA4MTA3Omh0dHBcYS8vam9obnNvbmNpdHl0bi5vcmcvOjpEZWZhdWx0Ojo6MTM2OTU0NjgwODYxODA1ODo=",
"url": "https://l.facebook.com/l.php?u=http%3A%2F%2Fjohnsoncitytn.org%2F&h=AUBHhU73r3VsNDED12CG04aEHyH2l9n-I8a-pvaVp_R-qUALOBrliucOL3dbyBm5_miUUKNE08QGK0Kietp3S-ngPvikwPVCBLV_wDmBrF_OlSEMBtANW9eg79D3_y4kOZU9dG8bG_wphctPgRp5asZVt2Ib2zVP&s=1",
"externalUrl": "http://johnsoncitytn.org/",
"mobileUrl": "https://lm.facebook.com/l.php?u=http%3A%2F%2Fjohnsoncitytn.org%2F&h=AUDb75f2i4MOL0iRZzTtSvA_Kk_mq4NvS2OXjLo1sRG-nFzZhQmis6_xkA1ZlNJ8RE0V1KYVBa5W13d9MDWyp3ykghtSdiYlGw3ThgZJfgLO6lKNZFQ2_ckD3JusbfKCQjRbwqIo-J-MYpq6qFvqCsabwNxN7KBn&s=1",
"offset": 167,
"length": 17
}
],
"collaborators": [],
"actionLink": {
"type": "MMEMessengerActionLink",
"url": null
},
"previewTitle": null,
"previewDescription": null,
"previewSource": null,
"previewTarget": null,
"topComments": [
{
"author": "Tom McCormack",
"authorId": "pfbid02vuXJVG7EBSwLbibmGki4uAxXWEoUksYp7HCXxDs…",
"text": "There are other parts of Johnson City that need to be protected from flooding. Are you looking at protecting those areas also?",
"createdAt": "2026-08-12T23:16:13.000Z"
}
],
"feedbackId": "ZmVlZGJhY2s6MTM2OTU0NjgwODYxODA1OA==",
"pageName": "Johnson City, TN - City Government",
"pageId": "100066882550448",
"user": {
"id": "100066882550448",
"name": "Johnson City, TN - City Government",
"profileUrl": "https://www.facebook.com/100066882550448",
"profilePic": "https://scontent.fmad8-1.fna.fbcdn.net/v/t39.30808-1/46072…"
},
"pageProfilePicture": "https://scontent.fmad8-1.fna.fbcdn.net/v/t39.30808-1/46072…",
"scrapedAt": "2026-08-19T08:38:43.421Z"
}

Field names follow the vocabulary the established Facebook post scrapers use — time/timestamp, likes/comments/shares, media, textReferences — so you can point existing code at this actor without rewriting how you read the results. The fields it adds on top (postType, hashtags, imageCount, and the images/videos split alongside the combined media) keep their own names.

postType is one of video, photo, link or text. A post that shares a URL is a link post and carries the preview card in previewTitle, previewSource and previewDescription, with the real destination in link — unwrapped from the l.facebook.com redirect Facebook rewrites outbound links into.

textReferences lists what Facebook marks up inside the text — hashtags, mentions, events and external links — each with its type, ID, URL and character offset, so you can find them in text without re-parsing it. Reactions come both as topReactions and as one column per type (reactionLikeCount, reactionLoveCount, reactionHahaCount, reactionWowCount, reactionSadCount, reactionAngryCount, reactionCareCount) for loading straight into a spreadsheet or a table. Photo posts carry images with dimensions and the Facebook photo page; video posts carry videos with separate SD and HD URLs, a thumbnail, duration and a captions track when one exists.

The dataset ships four views: Posts (what it says and how it did), Engagement (reactions, comments, shares and views for ranking or trend work), Media (posts with photos or video and their URLs), and Posts with full text.

Set includeRaw to attach Facebook's untouched post object under raw when you need a detail that has no named field.

Things worth knowing about the numbers

  • topComments is not the comment thread. Facebook ships a couple of comments alongside the feed and this actor passes those through, so a post with commentCount: 14 will still usually carry zero or one entry here. Use commentCount for the real total.
  • Every post has a posted time. time and timestamp are always present — a post without one fails the run rather than arriving with a null, so you can sort and bucket on it without guarding. A share also carries the original's own time in timeCreated, and a video carries its upload time in videos[].publishedAt; both can be earlier than the post itself.
  • A shared post reports zero engagement. When isShare is true, the reactions, comments and shares belong to the original story, not to the share, and Facebook returns the sharer's own counters as empty. sharedPost names the original and links to it — read the engagement there.
  • Video URLs expire. sdUrl and hdUrl are signed and time-limited, good for hours rather than days. Download what you need soon after the run; the permalink in url keeps working.

About title

A Facebook post has no title of its own — it is just a body of text. title here is Facebook's own SEO headline for the post, which is what search engines and link previews show. For the occasional post Facebook generates no headline for, title falls back to the post's opening line — and for a photo posted with no caption at all, to Facebook's own description of the picture, so the field is never blank. Either way it is capped at 120 characters, so its length does not depend on which source it came from. text is always the complete, untruncated body.

When a page cannot be read

A URL that cannot be read does not fail the run and does not disappear — it comes back as its own row, so one bad page never costs you the pages that worked:

{
"facebookUrl": "https://www.facebook.com/somepage",
"url": "https://www.facebook.com/somepage",
"error": "not_available",
"errorDescription": "… is not publicly visible — Facebook reports \"This content isn't available\". The page may be private, restricted, or removed; only public pages can be read without a login.",
"scrapedAt": "2026-08-19T16:02:11.004Z"
}

error is one of not_available (private, restricted or deleted), not_found, or read_failed (Facebook refused the read, worth retrying). Rows carrying an error are not charged as posts.

How to use

From Apify Console

  1. Open the Actor and click Try for free / Start.
  2. Paste one or more public page URLs into Facebook page URLs — vanity (facebook.com/CoJCTN), numeric (facebook.com/profile.php?id=…) and m.facebook.com links all work.
  3. Leave What to read on Latest posts, or switch it to Page details, photos and videos to read the About block and media library instead.
  4. Raise Number of posts to 8 for the full batch Facebook serves in one request, or set Only posts newer than to poll incrementally.
  5. Click Start, then open the Dataset tab. Four ready-made views ship with it — Posts, Engagement, Media and Posts with full text — and you can export as JSON, CSV or Excel.

From the API

curl -s "https://api.apify.com/v2/acts/simple.actors~facebook-page-posts/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"startUrls": [{"url": "https://www.facebook.com/CoJCTN"}]}'

Or with the JavaScript client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('simple.actors/facebook-page-posts').call({
startUrls: [{ url: 'https://www.facebook.com/CoJCTN' }],
maxPosts: 8,
onlyPostsNewerThan: '24 hours',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

To follow a page over time, schedule the Actor and diff on postId.

Use cases

  • Monitor a Facebook page for new posts — run on a schedule with onlyPostsNewerThan set shorter than the gap between runs, and get exactly what is new.
  • Track emergency and service announcements — city governments, utilities, schools and clubs post outage notices, closures and alerts to Facebook first, often before anywhere else.
  • Competitor and brand monitoring — pull reactions, comments, shares and view counts across rival pages to see which posts actually performed.
  • Lead lists and CRM enrichmentmode: "details" returns a page's category, email, phone, website and address straight from the About block.
  • Archive a page's output — keep a dated, linkable, searchable record of what an organisation published, with its media.
  • Build a text dataset — full, untruncated post bodies for search, tagging, sentiment or topic analysis.

Usage notes

$1 per 1,000 posts, plus $0.003 per run — with Apify platform usage included. A typical run reads a page's latest 5 posts for $0.008, and a run that legitimately finds nothing costs only the run fee. Raise maxPosts to 8 for the full batch Facebook serves, at $0.011.

Page details is priced separately, from 9 September 2026: $0.004 per page for the About block, plus $0.006 when photos and videos are read too — so $4 per 1,000 pages for contact details alone, or $10 per 1,000 for everything. The media charge only applies when a media tab was actually read, and a page that could not be read costs nothing either way.

Rows carrying an error are not charged as posts.

What an empty result means

An empty dataset means the page genuinely has no public posts — not that something went wrong.

That holds because a read that fails leaves a row rather than finishing quietly: a page that does not exist, a private or restricted page, a challenge or consent interstitial served instead of the page, a rate limit, or retries running out each come back as a row carrying error and errorDescription. So an empty dataset is a real answer, and the error rows — not the run status — are where you look for failures.

The run itself succeeds even when every page in it failed. The one input that still fails a run is an onlyPostsNewerThan value that cannot be parsed as a window or a date, which is rejected before any page is fetched.

Settings

  • maxPosts — how many of the latest posts to return, newest first. Defaults to 5; raise it to 8 for the full batch Facebook serves in one request. 8 is the ceiling and anything above it is rejected rather than silently under-delivered. A smaller number also reaches less far back, so if onlyPostsNewerThan starts refusing a window it used to accept, raise this.
  • proxy — defaults to Apify residential proxy, and needs to. Facebook serves the page itself to anyone, but answers the posts request from a datacenter IP with a rate-limit error rather than posts, so a datacenter run fails instead of returning a short result. A run moves under half a megabyte, which puts the residential bandwidth at roughly a third of a cent.
  • onlyPostsNewerThan — keep only posts after a window like 20 hours, last 3 days or 90 minutes, or an ISO date such as 2026-08-01. Minutes, hours, days, weeks and months all work, with or without a leading "last". Ideal for incremental polling: ask for a window shorter than the gap between your runs and you get exactly what is new, or an error row on that page telling you the gap grew too large to answer honestly.
  • includeRaw — attach Facebook's untouched post object under raw. Useful for a field with no named equivalent; it makes items much larger.
  • Runs are capped at 256 MB.

Limits

  • Public pages only. A private page, or a personal profile that is not public, returns nothing — there is no session to log in with.
  • The latest 8 posts at most, not the archive. One request to Facebook returns about eight posts and asking again returns the same ones with a fresh cursor, so eight is a ceiling rather than a default — maxPosts above 8 is rejected rather than quietly under-delivered. Run the actor on a schedule to follow a page over time instead of trying to reach further back in one run.
  • The newest post is always included. Every run loads the page fresh, and Facebook serves it no-store, so nothing is cached between runs. Results are sorted by date, so a pinned older post cannot displace a recent one.
  • Date filtering only within the window a run reads. onlyPostsNewerThan keeps posts published after a window like 20 hours or an ISO date. Because a run reads only the latest maxPosts posts, a cutoff reaching further back than those posts do returns an error row for that page rather than a partial answer — so an empty result always means "nothing new since then", never "we did not look that far". There is no filter for older posts: reaching back past the latest batch is not something one request can do.
  • topComments is a sample, not the thread, and video URLs expire within hours. See the notes above.

FAQ

Is scraping Facebook legal? This Actor reads only pages that are publicly visible to any logged-out visitor, without logging in. Meta's Terms of Service restrict automated collection from its properties, so check the platform's ToS and your own obligations before using it, and run it at a volume and rate you are comfortable defending.

Do I need a Facebook login, cookies or an API key? No. There is no account, session or token involved — it reads the post data Facebook server-renders for logged-out visitors.

Does it support pagination or scraping a page's full history? No, and that is a hard limit rather than a missing feature. One request to Facebook returns about eight posts, and asking again returns the same eight with a fresh cursor, so maxPosts is capped at 8. To follow a page over time, run the Actor on a schedule with onlyPostsNewerThan instead of trying to reach further back in a single run.

Can it scrape private pages or personal profiles? No. A private page, or a personal profile that is not public, returns an error row of not_available — there is no session to log in with.

Why does it need a residential proxy? Facebook serves the page HTML to anyone, but answers the /api/graphql/ posts request from a datacenter IP with a rate-limit error instead of posts. A datacenter run therefore returns no posts. A run moves under half a megabyte, so the residential bandwidth stays small.

Why does a post with 14 comments only return one in topComments? Facebook ships a couple of comments alongside the feed and this Actor passes those through — it is a sample, not the thread. Use comments for the real total.

How do I get a page's email, phone and address? Set mode to details. The About block is not present in the post feed at all, so no post carries a category, email, website, phone or follower count.

Note

This actor is not affiliated with, endorsed by, or sponsored by Facebook or Meta. It reads publicly visible pages without logging in. Meta's Terms of Service restrict automated collection from its properties, so review them and your own obligations before using it, and run it at a volume and rate you are comfortable defending.