Facebook Posts API: public page posts as JSON, without the Graph API
Meta's Graph API returns posts only for pages you manage, after app review. This endpoint returns the publicly visible posts of a page or profile as JSON with post type, hashtags, links, media URLs, reaction counts and dates. It continues beyond the first few posts and accepts date filters. Call it through the Apify API with a token, or schedule runs and use webhooks.
What the Graph API gives you today
The /{page-id}/posts and /feed edges still exist, but reading them needs a Page access token for a page you administer, and reading other pages needs the Page Public Content Access feature, which is granted after app review and mostly to established apps. Comment counts and reactions come with more permissions still. For competitor pages, media pages or research across many pages, the Graph API is not an option in practice.
The endpoint
One HTTP call. The synchronous form waits for the run and returns the records in the same response (fine for up to a few minutes of work); the asynchronous form returns a run id at once and you read the dataset when it has finished, with webhooks if you want a callback.
Synchronous
curl -X POST "https://api.apify.com/v2/acts/alfalfa~facebook-posts-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN&format=json" \
-H 'Content-Type: application/json' \
-d '{"startUrls": [{"url": "https://www.facebook.com/humansofnewyork"}], "resultsLimit": 50, "onlyPostsNewerThan": "30 days"}'
Asynchronous
# start the run (returns immediately with the run id and dataset id)
curl -X POST "https://api.apify.com/v2/acts/alfalfa~facebook-posts-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' -d '{"startUrls": [{"url": "https://www.facebook.com/humansofnewyork"}], "resultsLimit": 50, "onlyPostsNewerThan": "30 days"}'
# read the results when the run has finished (JSON, CSV or XLSX; paginate with offset and limit)
curl "https://api.apify.com/v2/datasets/<defaultDatasetId>/items?token=$APIFY_TOKEN&format=json&offset=0&limit=1000"
Python
from apify_client import ApifyClient # pip install apify-client
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("alfalfa/facebook-posts-scraper").call(run_input={
"startUrls": [
{
"url": "https://www.facebook.com/humansofnewyork"
}
],
"resultsLimit": 50,
"onlyPostsNewerThan": "30 days"
})
items = list(client.dataset(run.default_dataset_id).iterate_items())
print(len(items), items[0])
JavaScript
import { ApifyClient } from 'apify-client'; // npm install apify-client
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('alfalfa/facebook-posts-scraper').call({"startUrls": [{"url": "https://www.facebook.com/humansofnewyork"}], "resultsLimit": 50, "onlyPostsNewerThan": "30 days"});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
Request fields
| Field | Type | Meaning |
|---|---|---|
startUrls | array | Page or profile URLs, as {"url": ...} objects or plain strings. |
resultsLimit | integer | Maximum posts per page. |
onlyPostsNewerThan | string | YYYY-MM-DD, ISO date-time, or relative such as 30 days; stops paging when older posts appear. |
onlyPostsOlderThan | string | Upper bound, same formats. |
captionText | boolean | Add the video transcript to video posts ($2 per 1,000 video posts looked up). |
maxConcurrency | integer | Pages processed in parallel, default 5. |
Response fields
One JSON object per record. The official Apify Actor's fields stay available with the same names and types; the fields added below give you simpler summaries.
| Field | Meaning |
|---|---|
postId, url, facebookUrl, pageName | Post ID and URL, the page it belongs to. |
time, timestamp | When it was posted, ISO 8601 and Unix time. |
text, link, previewTitle, previewDescription | The post text and the link preview when there is one. |
likes, comments, shares, reactionLikeCount ... reactionAngryCount | Engagement, with the reaction split. |
media, isVideo, viewsCount, captionText | Photos and videos with URLs, video views, optional transcript. |
topComments, sharedPost, user, collaborators, paidPartnership | Top comments, the original of a shared post, the author, co-authors, paid-partnership label. |
postType, isShare, hashtags, links | Post format, Facebook-share flag, hashtags and external destinations. |
images, videos, imageCount, videoCount | Direct media URLs and counts of the photo/video entries returned for each post. |
storyId, pageId, pageProfilePicture, scrapedAt, topReactions | Opaque story ID, page details, collection time and reaction summary. |
The direct images and videos URLs are also in media; Facebook CDN links can expire. media[].url points to a Facebook page for the photo or video. Keep url as the post permalink while the post remains public.
Limits, pagination, scheduling
- The synchronous endpoint cuts off after about five minutes; for large jobs use the asynchronous one and read the dataset in pages (
offsetandlimit, up to 1,000 per page, orformat=csvfor a file). - Runs can be scheduled on the platform (cron), triggered by webhook, and called from n8n, Make, Zapier or an AI agent through MCP.
- Set a maximum budget per run in the Actor settings; the run stops at the limit and never overshoots.
- Public data only: what a visitor without a Facebook account can see. Groups, Marketplace and private profiles are out of scope.
Pricing
$2 per 1,000 posts, no start fee, date filters free. Platform usage and residential proxies are included; you pay only for records written to the dataset. The Apify free plan includes $5 of usage a month, enough to try it properly.
Questions people ask
Does the Facebook Graph API still let me read posts of any page?
Only pages you administer, with a Page access token. Other pages need the Page Public Content Access feature after app review, which most developers do not get. This endpoint reads the public page instead and needs no Facebook account at all.
Is it a real API?
It is an Apify Actor called through the Apify REST API: POST the input, get JSON back, or start a run and read the dataset later. Tokens, pagination, webhooks and schedules work like any other API.
Can I get only new posts every day?
Yes. Set onlyPostsNewerThan to yesterday and schedule the run; the response contains only the posts published since then.
What does it cost?
$2 per 1,000 posts with platform usage included and no start fee; the official Apify posts scraper charges $5 plus a start fee and a fee per filter.