Facebook Comments Scraper - $0.25 per 1,000 avatar

Facebook Comments Scraper - $0.25 per 1,000

Pricing

from $0.25 / 1,000 comment scrapeds

Go to Apify Store
Facebook Comments Scraper - $0.25 per 1,000

Facebook Comments Scraper - $0.25 per 1,000

Turn public Facebook post and reel URLs into comment rows: author, text, timestamp, like count, reply count, and the nested replies if you want them. No login needed from you, and no browser. $0.25 per 1,000 comments, the same rate on every plan.

Pricing

from $0.25 / 1,000 comment scrapeds

Rating

0.0

(0)

Developer

Dami's Studio

Dami's Studio

Maintained by Community

Actor stats

0

Bookmarked

4

Total users

2

Monthly active users

2 days ago

Last modified

Share

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 the replies themselves if you want them.

Nothing to set up. You don't supply a login, a cookie or a token, and no browser is started.

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).

That $0.25 is the rate on every plan, free or paid. No volume tiers, no minimum spend, no add-on fees. What you read here is what you pay on day one.

CommentsTotal cost
100$0.026
1,000$0.251
10,000$2.501
100,000$25.001

What is actually charged

  • One comment-scraped event 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. They 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, so there's no price quietly attached to apify-default-dataset-item.

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"
}
FieldWhat it does
startUrlsPublic Facebook post, video, reel, photo or group-post permalinks. Up to 100 per run.
resultsLimitMaximum comments per post URL. Default 50, hard ceiling 5,000.
includeNestedCommentsAlso pull the replies under each comment. Each reply is its own row and counts towards the limit.
viewOptionRANKED_UNFILTERED (all comments, the default), RANKED_THREADED (most relevant), RECENT_ACTIVITY (newest first).
onlyCommentsNewerThanOptional cut-off. 2026-01-31, a full ISO timestamp, or relative like 3 days / 2 months. Dropped comments aren't charged.
sessionCookiesOptional, and most runs never touch it. See About logins below.
proxyUrlsLeave empty. Fill it in only if you want traffic to leave through proxy servers you already pay for.

Run it with empty input and you get one 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 rather than scraped. A Page isn't a post and has no single comment thread.

Output

One row per comment. Real row from a National Geographic post:

{
"ok": true,
"charged": true,
"recordType": "comment",
"inputUrl": "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/pfbid02r7oYKajerb...?comment_id=27860828630277533",
"id": "Y29tbWVudDoxNjM0OTI3NDg0NjcxMzQyXzI3ODYwODI4NjMwMjc3NTMz",
"commentId": "27860828630277533",
"feedbackId": "ZmVlZGJhY2s6MTYzNDkyNzQ4NDY3MTM0Ml8yNzg2MDgyODYzMDI3NzUzMw==",
"date": "2026-08-15T06:07:03.000Z",
"timestamp": 1786774023,
"text": "Perfect opportunity to clean our waters!!! Bring this history to the musuem for our kids.",
"likesCount": 106,
"likesCountText": "106",
"commentsCount": 4,
"threadingDepth": 0,
"replyToCommentId": null,
"profileName": "Maggie Dalkowska",
"profileId": "pfbid0gZTGg4botETTKbXHiLzt72U5Wig8Gg3mJgF18vBVbiKRWof4nKXS26GVDFy9siuyl",
"profileUrl": "https://www.facebook.com/maggie.dalkowska",
"profilePicture": "https://scontent-fra3-2.xx.fbcdn.net/v/t39.30808-1/465583957_...jpg",
"author": { "id": "pfbid0gZTGg4botETTKbXHiLzt72U5Wig...", "name": "Maggie Dalkowska", "url": "https://www.facebook.com/maggie.dalkowska" },
"attachments": [],
"scrapedAt": "2026-08-20T19:52:23.770Z"
}

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

  • likesCount is a number. Facebook only publishes a display string for reaction counts ("28", "1.2K", "3.4M"), so it's parsed back into an integer here. likesCountText keeps the original string. Above about a thousand reactions Facebook rounds the display string, so the number is rounded too: 1.2K becomes 1200, not the exact count. Nobody can give you the exact count from the public page, because the public page doesn't contain it.
  • commentsCount is the reply count on that comment, not the post's comment count.
  • profileId is sometimes a numeric id and sometimes an opaque pfbid… string, depending on the account. The pfbid form rotates over time, so commentId is the safer key.
  • attachments is an array of {type, url} for photo, video and sticker comments, empty for the usual text-only ones.
  • profileUrl is null for commenters whose profile Facebook renders without a public link.
  • postDescription is the post's text, not the comment's. It's 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, since 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's asked for. That's the floor for this target, not a limitation of this Actor.

About logins

Worth spelling out, because "no login" gets used loosely.

Runs start logged out, and for a public post that is usually all it takes. When Facebook hands a logged-out reader nothing back, the run falls back to an account session that ships with the Actor so you don't have to supply one. There is a session involved. It just isn't yours and you never see it.

That session is shared across everyone using the Actor, and Facebook meters per account. If you're running heavily and start seeing BLOCKED diagnostics, sessionCookies lets you paste your own c_user=…; xs=… pair and get a rate limit nobody else is using. Treat those values like a password. Anyone holding them can act as that account, and Facebook may sign the session out or restrict the account for automated use.

What this does not do

An honest list, because you'll hit these otherwise.

  • Public posts only. Anything Facebook shows only to a specific audience, meaning private groups, friends-only posts, or a Page restricted by age or country, returns an uncharged diagnostic row saying so.
  • No reactions breakdown. You get the total reaction count, not how many were love, haha or angry. The public page doesn't carry it.
  • No commenter email or phone. Not published, not inferable, not returned.
  • One level of replies. includeNestedComments pulls the replies under a top-level comment. Replies to replies are collapsed by Facebook into the same level, so that's 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 get 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 reads per address. The Actor rotates through a large pool of datacenter addresses and retries, but a very large run may still see a few posts come back as BLOCKED diagnostics. They aren't charged. Re-running usually clears them.

Diagnostics

Nothing here fails silently, and a failed run never bills you for the failure. Every problem becomes a row with "charged": false and a code:

codeMeaning
BAD_INPUTThat URL isn't a Facebook post URL. The row says why.
NOT_FOUNDThe post is private, deleted, or the URL points at something with no comment thread.
NO_COMMENTSThe post exists and is readable but has no comments, or none newer than your date filter.
BLOCKEDEvery exit address tried was throttled on this post. Re-run.
RATE_LIMITEDFacebook asked for a slower pace. Re-run.
DEAD_BATCHFifteen URLs in a row returned nothing, so the run stopped early rather than working through a list of dead links. The rest weren't touched and weren't charged.
PROXY_INPUT_ADJUSTEDYou asked for a proxy setting this Actor doesn't use; it ran on its own rotating addresses instead.
CHARGE_ERRORA billing event couldn't be recorded. Told to you rather than hidden.

The run still ends SUCCEEDED in all of these cases, because a failed run 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? Not from you. Paste URLs, get comments. There's an optional sessionCookies field if you'd rather the run used your own account's rate limit than the shared one.

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 is charged as a comment because each 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 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's no tier that makes it cheaper or more expensive.

Why do I get exactly ten comments per request in the logs? Because that's 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 doesn't offer residential proxy: on a target this data-heavy the bandwidth would cost more than the data is worth, and you'd 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 works as a deduplication key across runs.

What happens if I run it with no input? One sample row, 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 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 exports as JSON, CSV, Excel and XML from the dataset endpoint.

This Actor reads public Facebook content: posts and comments that anyone can open. It doesn't bypass a privacy setting and it doesn't touch private groups or restricted posts. Comment text and profile names are personal data in most jurisdictions, so you are the controller of whatever you collect, and GDPR, the CCPA and Facebook's own terms are yours to satisfy. If someone asks you to delete their comment out of your dataset, that request lands on you, not on us.