Facebook Comments Scraper
Scrape comments from any public Facebook post or reel: author, text, date, likes, replies. No login or cookies.
How it works
- 1Open it on Apify
Hit Run on Apify — it opens the tool in the cloud, no install.
- 2Set the inputs
Adjust
startUrls,resultsLimit,includeNestedComments(sensible defaults are pre-filled). - 3Click Run
The tool runs on Apify’s cloud and collects the data for you.
- 4Export the results
Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.
Inputs
| Field | What it does | Type |
|---|---|---|
startUrls | Public Facebook post, video, reel, photo or group-post permalinks. You get one row per comment for each of them. | array |
resultsLimit | Stop after this many comments for each post URL. Keep it low while you are testing - you pay per comment. | integer |
includeNestedComments | Also fetch the replies underneath each comment. Every reply is returned as its own row and counts towards the limit. | boolean |
viewOption | How Facebook should order the comments before they are read. | string |
onlyCommentsNewerThan | Optional cut-off. Absolute (2026-01-31 or a full ISO timestamp) or relative (3 days, 2 months). Comments older than this are dropped and never charged. There is no add-on fee for using it. | string |
proxyUrls | Leave this empty. By default the run rotates a large pool of datacenter addresses that cost you nothing per gigabyte. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, in the form http://user:pass@host:port. | array |
sessionCookies | Leave this empty unless you need it. Runs are logged out by default and that is enough for public Facebook content. Facebook shows some things only to a signed-in account, and it limits how fast any one account may read; supplying your own cookie uses your account and your own rate limit, shared with nobody. In Chrome: open facebook.com while signed in, press F12, then Application > Cookies > https://www.facebook.com, and paste the values as "c_user=<value>; xs=<value>". One line per account. Treat these like a password: anyone with them can act as that account, and Facebook may sign the session out or restrict the account for automated use. | array |
What you get
A structured dataset — each result includes fields like:
profileNametextdatelikesCountcommentsCountthreadingDepthcommentUrlfacebookUrlExport every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.
Related tools in Social Media Scrapers
Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.
Facebook Groups Scraper
Scrape public Facebook group posts without logging in: author, text, time, permalink, media and engagement counts.
Facebook Posts Scraper
Scrape posts from any public Facebook Page: text, time, permalink, media, reactions, comments and shares. No login, no cookies. $0.60 per 1,000 posts.
Facebook Reels Scraper
Scrape public Facebook Page Reels - up to 200 per Page: views, reactions, comments, shares, captions, hashtags, duration, MP4 and thumbnail links. No login.
Instagram Post Scraper
Scrape Instagram posts and reels by link or shortcode for $0.18 per 1,000, flat at every volume.
Instagram Post Scraper
Scrape Instagram posts and reels with no login: caption, like and comment counts, publish date, every carousel image and video URL, owner and hashtags.
Instagram Profile Scraper
Bulk Instagram profile scraper. Usernames or profile URLs in, one row out: bio, exact follower, following and post counts, verified flag, category, e-mail.
Where this tool sits
- Categories
- Social Media Scrapers
Facebook Comments Scraper
Paste public Facebook post URLs, get the comments back as rows: who wrote it, what they wrote, when, how many likes it got, how many replies it has, and — if you want them — the replies themselves.
No Facebook account. No cookies. No login. No browser. No proxy of your own.
Price
$0.25 per 1,000 comments, plus a $0.001 start fee per run (one tenth of a cent, billed per gigabyte of run memory, so exactly $0.001 on the default 1 GB).
This is the cheapest Facebook comments scraper on the market, and $0.25 is the rate on every plan — free or paid. There are no volume tiers, no minimum spend and no add-on fees. Where a headline rate is a tiered one, it is usually the rate you get only after committing to a large subscription; this price has no tiers to hide behind, so what you read here is what you pay on day one.
| Comments | Total cost |
|---|---|
| 100 | $0.026 |
| 1,000 | $0.251 |
| 10,000 | $2.501 |
| 100,000 | $25.001 |
What is actually charged
- One
comment-scrapedevent per comment row written to the dataset. Nothing else is metered
per row.
- Replies count as comments — each reply is its own row, so each one is one charge.
- Free: the sample row an empty run returns, every diagnostic row (blocked post, private post,
post with comments turned off, bad URL), and every comment dropped by the onlyCommentsNewerThan filter. Those rows all carry "charged": false.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row. The primary
event is a named one — there is no price quietly attached to apify-default-dataset-item, which is the trick that makes some scrapers bill you for their own error messages.
Input
{
"startUrls": [
{ "url": "https://www.facebook.com/natgeo/posts/pfbid02rAK2eMyfVYDZbESKoNVuwWjcFr5BgRBfjp27nhgQToSiQD9NF3ypNdXpQuwSp7qnl" },
{ "url": "https://www.facebook.com/NASAJSC/posts/1011509817757722" }
],
"resultsLimit": 50,
"includeNestedComments": true,
"viewOption": "RANKED_UNFILTERED"
}
| Field | What it does |
|---|---|
startUrls | Public Facebook post, video, reel, photo or group-post permalinks. Up to 100 per run. |
resultsLimit | Maximum comments per post URL. Default 50, hard ceiling 5,000. |
includeNestedComments | Also pull the replies under each comment. Each reply is its own row and counts towards the limit. |
viewOption | RANKED_UNFILTERED (all comments, the default), RANKED_THREADED (most relevant), RECENT_ACTIVITY (newest first). |
onlyCommentsNewerThan | Optional cut-off — 2026-01-31, a full ISO timestamp, or relative like 3 days / 2 months. Dropped comments are not charged. |
proxyUrls | Leave empty. Only fill this in if you want traffic to leave through proxy servers you already pay for. |
Run it with empty input and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.
URL shapes that work
https://www.facebook.com/<page>/posts/<pfbid...>
https://www.facebook.com/<page>/posts/<numeric id>
https://www.facebook.com/<page>/videos/<numeric id>
https://www.facebook.com/reel/<numeric id>
https://www.facebook.com/groups/<group id>/posts/<numeric id>
https://www.facebook.com/permalink.php?story_fbid=<id>&id=<page id>
https://www.facebook.com/photo.php?fbid=<id>
A bare Page URL like https://www.facebook.com/natgeo is rejected with an explanation instead of being scraped — a Page is not a post and has no single comment thread.
Output
One row per comment. A real row from a National Geographic post:
{
"ok": true,
"charged": true,
"recordType": "comment",
"inputUrl": "https://www.facebook.com/natgeo/posts/pfbid02rAK2eMyfVYDZbESKoNVuwWjcFr5BgRBfjp27nhgQToSiQD9NF3ypNdXpQuwSp7qnl",
"facebookUrl": "https://www.facebook.com/natgeo/posts/pfbid02rAK2eMyfVYDZbESKoNVuwWjcFr5BgRBfjp27nhgQToSiQD9NF3ypNdXpQuwSp7qnl",
"postId": "1634927484671342",
"postTitle": "National Geographic",
"postDescription": "A punishing heat wave has left much of Europe in drought, causing rivers to run at record-low levels.",
"pageName": "National Geographic",
"commentUrl": "https://www.facebook.com/natgeo/posts/pfbid02rAK2eMyfVYDZbESKoNVuwWjcFr5BgRBfjp27nhgQToSiQD9NF3ypNdXpQuwSp7qnl?comment_id=27860828630277533",
"id": "Y29tbWVudDoxNjM0OTI3NDg0NjcxMzQyXzI3ODYwODI4NjMwMjc3NTMz",
"commentId": "27860828630277533",
"feedbackId": "ZmVlZGJhY2s6MTYzNDkyNzQ4NDY3MTM0Ml8yNzg2MDgyODYzMDI3NzUzMw==",
"date": "2026-08-15T06:07:03.000Z",
"timestamp": 1786860423,
"text": "Perfect opportunity to clean our waters!!! Bring this historical junk up.",
"likesCount": 28,
"likesCountText": "28",
"commentsCount": 1,
"threadingDepth": 0,
"replyToCommentId": null,
"profileName": "Maggie Dalkowska",
"profileId": "100000529787288",
"profileUrl": "https://www.facebook.com/maggie.dalkowska",
"profilePicture": "https://scontent.xx.fbcdn.net/v/...",
"author": { "id": "100000529787288", "name": "Maggie Dalkowska", "url": "https://www.facebook.com/maggie.dalkowska" },
"attachments": [],
"scrapedAt": "2026-08-15T13:29:41.902Z"
}
A reply row looks the same but with "threadingDepth": 1, a replyToCommentId pointing at its parent, and a commentUrl carrying both comment_id and reply_comment_id.
Field notes
likesCountis a number. Facebook only publishes a display string for reaction counts
("28", "1.2K", "3.4M"), so it is parsed back into an integer here. likesCountText keeps the original string if you want to show exactly what Facebook shows. Above about a thousand reactions the display string is rounded by Facebook, so the number is too — 1.2K becomes 1200, not the exact count. Nobody can give you the exact count from the public page, because the public page does not contain it.
commentsCountis the reply count on that comment, not the post's comment count.attachmentsis an array of{type, url}for photo, video and sticker comments, empty for the
usual text-only ones.
profileUrlis null for the small number of commenters whose profile Facebook renders without a
public link.
postDescriptionis the post's text, not the comment's. It is read from the post's own
content section, which matters more than it sounds: taking the longest text block on the page instead returns whichever reader wrote the longest comment.
Speed
Measured on the platform, at the default 1 GB:
- One post, 60 comments: 13 seconds, from a cold container.
- Two posts, 8 comments each: 7 seconds — up to three posts are worked on at once.
Facebook hands out comments in pages of ten and silently ignores any request for a larger page, so a hundred comments is ten round trips no matter how it is asked for. That is the floor for this target, not a limitation of this Actor.
What this does not do
An honest list, because you will hit these otherwise.
- Public posts only. No login is ever performed, so anything that needs an account — private
groups, friends-only posts, a Page that has restricted its audience by age or country — returns an uncharged diagnostic row saying so. This is a design decision, not a bug: an Actor holding someone's session cookie is one password change away from breaking and one policy change away from getting an account disabled.
- No reactions breakdown. You get the total reaction count, not how many were love, haha or
angry. The logged-out page does not carry it.
- No commenter email or phone. Not published, not inferable, not returned.
- One level of replies.
includeNestedCommentspulls the replies under a top-level comment.
Replies to replies are collapsed by Facebook into the same level, so that is as deep as the public thread goes.
- Reply counts can exceed the rows you get. A comment may report six replies and return four
if some were made by accounts that later deactivated or were removed.
- Very large threads. The ceiling is 5,000 comments per post URL. Threads deeper than that are
paginated by Facebook in a way that starts repeating rather than continuing, and repeated rows are dropped rather than charged.
- A post with comments turned off returns one uncharged diagnostic row, not an error.
- Rate limiting. Facebook meters logged-out reads per address. The Actor rotates through a large
pool of datacenter addresses and retries, but a very large single run may still see a few posts come back as BLOCKED diagnostics. They are not charged. Re-running usually clears them.
Diagnostics
Nothing about this Actor fails silently, and a failed run never bills you for the failure. Every problem becomes a row with "charged": false and a code:
code | Meaning |
|---|---|
BAD_INPUT | That URL is not a Facebook post URL. The row says why. |
NOT_FOUND | The post is private, deleted, or the URL points at something with no comment thread. |
NO_COMMENTS | The post exists and is readable but has no comments, or none newer than your date filter. |
BLOCKED | Every exit address tried was throttled on this post. Re-run. |
RATE_LIMITED | Facebook asked for a slower pace. Re-run. |
DEAD_BATCH | Fifteen URLs in a row returned nothing, so the run stopped early rather than working through a list of dead links. The rest were not touched and not charged. |
PROXY_INPUT_ADJUSTED | You asked for a proxy setting this Actor does not use; it ran on its own rotating addresses instead. |
CHARGE_ERROR | A billing event could not be recorded. Told to you rather than hidden. |
The run itself still ends SUCCEEDED in all of these cases, because a run that failed would bill you the start fee for the privilege of being told you pasted the wrong link.
FAQ
Do I need a Facebook account, cookies or an access token? No. Nothing is logged in and nothing is stored between runs. You paste URLs, you get comments.
Does it work on reels and videos? Yes. Reels, videos, photos and group posts all have the same comment thread underneath them and are read the same way.
How do I get the replies under a comment? Set includeNestedComments to true. Each reply arrives as its own row with threadingDepth: 1 and replyToCommentId set, and each one is charged as a comment because each one is a row.
How do I only get recent comments? Set onlyCommentsNewerThan to 7 days, or to a date like 2026-06-01. Anything older is dropped before it reaches the dataset, and dropped comments are never charged. Combine it with viewOption: "RECENT_ACTIVITY" and the run also stops paging as soon as it goes past your cut-off, which is faster and cheaper than filtering afterwards.
How much does 10,000 comments cost? $2.50 plus the $0.001 start fee. There is no tier that makes it cheaper and no tier that makes it more expensive.
Why do I get exactly ten comments per request in the logs? Because that is what Facebook returns. Asking for 25, 50 or 100 returns ten. The Actor pages until it reaches your resultsLimit.
Can I use my own proxies? Yes — put them in proxyUrls as http://user:pass@host:port. Leave it empty and the run uses a rotating pool of datacenter addresses, which costs you nothing extra. This Actor deliberately does not offer residential proxy: on a target this data-heavy the bandwidth would cost more than the data is worth, and you would be paying for it.
Is the comment ID stable? commentId is Facebook's own numeric comment id and is stable. id is the base64 node id, also stable. Either one works as a deduplication key across runs.
What happens if I run it with no input? You get one sample row, clearly marked "_sample": true and "charged": false, showing every field. Only the start fee applies.
Can it scrape all comments from a whole Page? Not directly — this Actor takes post URLs. Collect the post URLs first, then feed them in here, up to 100 per run.
Integration
Standard Apify Actor. Start it from the API, the JS or Python client, a schedule, or an MCP client, and read the default dataset:
curl -X POST "https://api.apify.com/v2/acts/dami_studio~facebook-comments-scraper/runs?token=<YOUR_TOKEN>" \
-H 'content-type: application/json' \
-d '{"startUrls":[{"url":"https://www.facebook.com/natgeo/posts/pfbid02rAK2eMyfVYDZbESKoNVuwWjcFr5BgRBfjp27nhgQToSiQD9NF3ypNdXpQuwSp7qnl"}],"resultsLimit":50}'
Output is available as JSON, CSV, Excel and XML from the dataset endpoint.
Legal
This Actor reads only what Facebook serves to a logged-out visitor — the same content anyone can see without an account. It does not log in, does not bypass a privacy setting, and does not touch private groups or restricted posts. Comment text and profile names are personal data in most jurisdictions: you are the controller of whatever you collect, and the GDPR, the CCPA and Facebook's own terms are your responsibility to satisfy. Scrape public opinion, not people.