Facebook Page Posts Scraper avatar

Facebook Page Posts Scraper

Pricing

from $1.00 / 1,000 posts

Go to Apify Store
Facebook Page Posts Scraper

Facebook Page Posts Scraper

Scrape the latest posts from any public Facebook page: full text, date, reactions broken down by type, comments, shares, video views, photo and video URLs, hashtags, links and top comments. 52 fields per post. No login, no cookies, no browser. Unofficial.

Pricing

from $1.00 / 1,000 posts

Rating

0.0

(0)

Developer

Simple Actors

Simple Actors

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

Give it a public Facebook page URL and get back its latest posts — full text, publish date, reactions, comments, shares, photos and video, newest first. 52 fields per post.

$1 per 1,000 posts, plus $0.003 per run — with Apify platform usage included. A typical run reads a page's latest 8 posts for $0.011, and a run that legitimately finds nothing costs only the run fee.

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.

What you can do with it

  • Archive a page's posts — keep a dated, linkable record of what an organisation published, in a format you can search.
  • Watch a page for new posts — run it on a schedule and diff on postId to catch anything new.
  • Follow announcements — city governments, utilities, schools and clubs post outage notices, closures and alerts to Facebook first.
  • Build a text dataset — full post bodies for search, tagging or analysis.

How to use it

Paste one or more page URLs:

{
"startUrls": [{ "url": "https://www.facebook.com/CoJCTN" }]
}

That is the whole input. The actor loads the page and makes a single posts request, and returns however many posts that request gives back — usually about 8. There is no count to tune and nothing to page through.

maxPosts is optional and only ever trims: set 5 if five is all you want. Raising it fetches nothing extra, because there is no second request.

Vanity URLs (facebook.com/CoJCTN), numeric ones (facebook.com/profile.php?id=100066882550448) and m.facebook.com links all work — they get normalised for you.

What you get

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.

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 broken read fails the run instead of finishing quietly: a page that does not exist, a challenge or consent interstitial served instead of the page, a rate limit, or retries running out all raise an error. So you can trust an empty result, and treat a failed run as something to retry.

Call it 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"}]}'

Settings

  • maxPosts — how many of the latest posts to return, newest first. Defaults to 8, the number one request to Facebook normally supplies. It is a target rather than a cap: if a batch comes back short, the run asks for more instead of returning fewer. A page that simply has fewer posts returns everything it has.
  • 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 a failed run 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, 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 8 posts, a cutoff reaching further back than those posts do fails the run rather than returning 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.

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.