Facebook Comments API: every comment and reply of a post as JSON
The Graph API exposes comments only on posts of pages you manage. This endpoint returns the comments and replies of any public post, as a signed-out visitor sees them, with the thread structure preserved. One call, JSON back, no Facebook account.
What the Graph API gives you today
The /{post-id}/comments edge works for your own page's posts with a Page token carrying pages_read_user_content. For other pages' posts it needs the Page Public Content Access feature, granted after app review to few apps. Replies, reaction counts and commenter profiles are further permissions. For any post you do not own, the Graph API is closed 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-comments-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN&format=json" \
-H 'Content-Type: application/json' \
-d '{"startUrls": [{"url": "https://www.facebook.com/humansofnewyork/posts/pfbid0BbKbkisExKGSKuhee9a7i86RwRuMKFC8NSkKStB7CsM3uXJuAAfZLrkcJMXxhH4Yl"}], "resultsLimit": 200, "includeNestedComments": true}'
Asynchronous
# start the run (returns immediately with the run id and dataset id)
curl -X POST "https://api.apify.com/v2/acts/alfalfa~facebook-comments-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' -d '{"startUrls": [{"url": "https://www.facebook.com/humansofnewyork/posts/pfbid0BbKbkisExKGSKuhee9a7i86RwRuMKFC8NSkKStB7CsM3uXJuAAfZLrkcJMXxhH4Yl"}], "resultsLimit": 200, "includeNestedComments": true}'
# 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-comments-scraper").call(run_input={
"startUrls": [
{
"url": "https://www.facebook.com/humansofnewyork/posts/pfbid0BbKbkisExKGSKuhee9a7i86RwRuMKFC8NSkKStB7CsM3uXJuAAfZLrkcJMXxhH4Yl"
}
],
"resultsLimit": 200,
"includeNestedComments": true
})
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-comments-scraper').call({"startUrls": [{"url": "https://www.facebook.com/humansofnewyork/posts/pfbid0BbKbkisExKGSKuhee9a7i86RwRuMKFC8NSkKStB7CsM3uXJuAAfZLrkcJMXxhH4Yl"}], "resultsLimit": 200, "includeNestedComments": true});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);
Request fields
| Field | Type | Meaning |
|---|---|---|
startUrls | array | Post URLs in any form Facebook uses: /posts/, /photo/?fbid=, /videos/, /reel/, permalink.php. |
resultsLimit | integer | Maximum top-level comments per post; replies come on top of it. |
includeNestedComments | boolean | Return replies and replies to replies as their own records with parent links. |
viewOption | string | RANKED_UNFILTERED (all), RECENT_ACTIVITY (newest), RANKED_THREADED (most relevant). |
onlyCommentsNewerThan | string | Date or relative period; with the newest view the run stops early. |
Response fields
One JSON object per record. Field names are the ones the official Apify Actor uses, so code written for it works unchanged.
| Field | Meaning |
|---|---|
commentId, commentUrl, id, feedbackId | IDs and the direct link. |
date, text, attachments | When, what, and any photo, GIF or video attached. |
profileName, profileId, profileUrl, profilePicture, author | Who wrote it. |
likesCount, commentsCount | Reactions on the comment and the number of replies. |
threadingDepth, replyToCommentId, parentComment, parentReply | 0 for a comment, 1 and 2 for replies, with the parents, so the thread can be rebuilt. |
facebookUrl, facebookId, postTitle, postDescription | The post the comment belongs to. |
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
$0.50 per 1,000 comments or replies, no start fee, no surcharge for date filters. 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
Can I read comments on another page's post through the Graph API?
Not without the Page Public Content Access feature, which requires app review and is rarely granted. This endpoint reads the public post instead and needs no Facebook account.
Why is the count lower than the number shown on the post?
The number on the post includes replies, spam-filtered comments and comments by deactivated accounts, which no visitor can read. The response contains what a signed-out visitor can see.
Do I get the thread structure?
Yes. Replies are separate records with threadingDepth, replyToCommentId, parentComment and parentReply, so a conversation tree can be rebuilt without guessing.
What does it cost?
$0.50 per 1,000 comments or replies, platform usage included, no start fee. The official Apify comments scraper charges $2.50 plus $1 per run and a surcharge per comment when a date filter is on.