# Facebook Comments Scraper - Threaded Replies, No Login (`a.actors/facebook-comments-scraper`) Actor

Scrape full comment threads from Facebook posts without cookies or an account. Comment text, author, timestamp, likes, and nested replies to depth 2, with the parent id on every row so the thread rebuilds exactly. Works on page posts and public group posts alike.

- **URL**: https://apify.com/a.actors/facebook-comments-scraper.md
- **Developed by:** [ApifyActors](https://apify.com/a.actors) (community)
- **Categories:** Social media, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 comments

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Facebook Comments Scraper — whole threads, replies included, no login

Pull every comment on a Facebook post, with its replies, without an account and without
cookies. Page posts, video posts and public group posts all work the same way.

### What it does that the others don't

- **Complete threads, not the visible ones.** By default it reads the thread in full,
  oldest first, rather than Facebook's ranked view — which is what a visitor sees and is
  deliberately incomplete. If you want the ranked order you can ask for it.
- **Replies to depth 2, threaded properly.** Every row carries `parent_comment_id` and
  `depth`, so you can rebuild the tree exactly rather than guessing from indentation. A
  comment's `reply_count` covers its whole sub-thread, which is why depth 2 is what makes
  the numbers add up.
- **Group posts.** Public group threads read the same as page threads.
- **Failed posts cost nothing.** A deleted post, a comments-disabled post or a link with no
  usable id comes back as an error row, unbilled.

### Input

| Field | What it does |
|---|---|
| `startUrls` | Post permalinks or bare numeric post IDs, one per entry |
| `includeReplies` | Expand reply threads to depth 2 (on by default) |
| `maxCommentsPerPost` | `0` for the whole thread, or a cap to control spend — see the warning below |
| `commentsRanked` | Off (default) = complete thread, oldest first. On = Facebook's ranked, partial order |
| `countryCode` | Country to browse from |

```json
{
  "startUrls": ["https://www.facebook.com/nasa/posts/1234567890123456", "284219939974539"],
  "includeReplies": true,
  "maxCommentsPerPost": 0
}
```

> ⚠️ **If you want replies, leave `maxCommentsPerPost` at 0.** The cap counts every row,
> replies included, and it is applied after the reply layer is merged — with top-level
> comments first. So on a thread with 500 comments, a cap of 50 returns 50 top-level
> comments and **no replies at all**, which looks exactly like a thread that has none.

#### About post URLs

Facebook has two permalink shapes. One carries a numeric post id
(`/posts/<id>`, `/permalink/<id>`, `?story_fbid=<id>`, `/videos/<id>`, `/photo?fbid=<id>`) —
paste those directly. The other is the opaque `/posts/pfbid…` form you get from the share
button, which contains no id at all; no scraper can resolve it without already knowing the
post. Paste the **numeric post ID** instead — the companion Facebook Posts Scraper returns it
as `post_id` on every row, so chaining the two actors just works.

### Output

One row per comment or reply:

```json
{
  "post_id": "284219939974539",
  "post_url": "https://www.facebook.com/nasa/posts/1234567890123456",
  "source_name": "NASA",
  "comment_id": "1234567890",
  "comment_url": "https://www.facebook.com/nasa/posts/1234567890123456?comment_id=1234567890",
  "parent_comment_id": "",
  "depth": 0,
  "text": "Incredible image — what's the exposure time on this?",
  "created_at": "2026-09-22T09:14:03Z",
  "created_time": 1790068443,
  "likes": 24,
  "likes_label": "24",
  "reply_count": 3,
  "author_name": "Jane Doe",
  "author_id": "100001234567890",
  "author_anonymous": false,
  "source_dialect": "en",
  "attachment_type": "",
  "attachment_url": "",
  "scraped_date": "2026-09-23T00:28:13Z"
}
```

`depth: 0` is a top-level comment; replies carry `depth: 1` or `2` and point at their parent.

### Pricing

**$1.00 per 1,000 comments**, replies included at the same rate, plus Apify's standard
$0.00005 actor start. Nothing is charged for posts that fail.

The official Apify comments scraper charges $2.00 per 1,000. Cheaper actors exist; they
return top-level comments from a ranked, partial view, and several of them bill replies
separately at a higher rate than the comments themselves.

### Notes and limits

- **Public content only.** No account means no private groups and no friends-only posts.
- **Some replies are not exposed to a logged-out reader.** Where that happens the run log
  says so per comment, and the reply layer reports whether it finished complete or partial —
  it never reports missing replies as "this comment has none".
- **Long threads take requests.** Comments arrive about ten per request, so a 300-comment
  thread is roughly 30 round trips and the actor paces itself through them. Budget time for
  very large threads, and use `maxCommentsPerPost` if you would rather cap the spend — but
  read the warning above first.
- **Rate limiting is expected and handled.** The actor re-establishes its session and resumes
  from where it stopped rather than starting the thread over. This applies to the reply layer
  too, so a throttled reply query is retried rather than recorded as an empty thread.

### FAQ

**Do I need cookies or a Facebook account?** No.

**Will it get my account banned?** It never logs in, so your account is never involved.

**Can I get comments on a Reel?** If the URL carries a numeric id, yes.

**How do I get post URLs in bulk?** Run the companion Facebook Posts Scraper and feed its
`post_id` column straight into `startUrls`.

**Is this legal?** Scraping publicly available data is generally lawful in most
jurisdictions, but comments are personal data. You are responsible for your own compliance,
particularly under the GDPR, and for Facebook's terms.

# Actor input Schema

## `startUrls` (type: `array`):

One entry per post. Accepts permalinks that carry a numeric id (/posts/<id>, /permalink/<id>, ?story\_fbid=<id>, /videos/<id>, /photo?fbid=<id>) and bare numeric post IDs. Two link shapes do NOT work and are reported as an error rather than returning an empty result: opaque /posts/pfbid… links, which carry no id at all, and /reel/<id>/ links, where that id is the reel's and not the post's. For both, paste the numeric post ID instead - it is what the Facebook Posts Scraper returns in its post\_id field.

## `includeReplies` (type: `boolean`):

Expand reply threads to depth 2. A comment's reply count covers its whole sub-thread, so depth 2 is what makes the totals add up. Replies are charged at the same rate as comments, and they count towards "Max comments per post" - so on a busy thread, set that cap to 0 or the replies are the rows that get cut.

## `maxCommentsPerPost` (type: `integer`):

Total rows per post, replies included. 0 = the whole thread. ⚠️ The cap is applied after replies are merged and top-level comments come first, so a cap lower than the number of top-level comments returns no replies at all.

## `commentsRanked` (type: `boolean`):

Off (recommended) returns the complete thread, oldest first. On returns Facebook's own ranked ordering, which is what a visitor sees but does NOT return every comment.

## `countryCode` (type: `string`):

ISO country code for the exit location, e.g. US, GB, DE.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/nasa/posts/1234567890123456"
  ],
  "includeReplies": true,
  "maxCommentsPerPost": 0,
  "commentsRanked": false,
  "countryCode": "US"
}
```

# Actor output Schema

## `comments` (type: `string`):

One item per comment: text, author, timestamp, likes, reply count, and the parent id and depth needed to rebuild the thread. Posts that failed appear as error rows and are not charged.

## `summary` (type: `string`):

Counts of posts read, comments collected and posts that failed.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.facebook.com/nasa/posts/1234567890123456"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("a.actors/facebook-comments-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "startUrls": ["https://www.facebook.com/nasa/posts/1234567890123456"] }

# Run the Actor and wait for it to finish
run = client.actor("a.actors/facebook-comments-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.facebook.com/nasa/posts/1234567890123456"
  ]
}' |
apify call a.actors/facebook-comments-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,a.actors/facebook-comments-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/vOatlNLw3P6mqRa04/builds/mdhmm6eKgcBUozcmZ/openapi.json
