# Facebook Posts Scraper (`crawlerbros/facebook-posts-scraper`) Actor

Scrape public Facebook Page and Profile posts and Reels — text, reactions, comments, shares, photos, videos with captions, and hashtag/link entities. Also fetches a single post or Reel by its URL. No login, cookies, or proxy setup required.

- **URL**: https://apify.com/crawlerbros/facebook-posts-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Posts Scraper

Scrape **Facebook** Page and Profile posts — text, reactions, comments, shares, video and photo media, and structured hashtag/link entities — plus a dedicated cheap lookup for a single post or Reel by its permalink URL. No login, no cookies, no proxy setup — just paste a Facebook URL and run. Works out of the box on the free Apify plan.

### What this actor does

- **Two input modes in one run:** paste a Page/Profile URL to scrape its recent posts (including Reels), or a single post permalink to fetch just that one post — mix both in the same `startUrls` list
- **Single-post lookups are genuinely cheap:** a post permalink page is fully rendered by Facebook itself, so this actor fetches it directly with no browser — faster and lower-cost than a full Page/Profile scrape. (Standalone `/reel/...` links specifically are less reliable this way — see the FAQ.)
- **Real server-side date filtering:** `onlyPostsNewerThan`/`onlyPostsOlderThan` are applied by Facebook itself, not discarded after the fact — large date-scoped runs don't need to scroll through everything in between
- **Full reaction breakdown:** all 7 real Facebook reaction types (Like, Love, Care, Haha, Wow, Sad, Angry), not a partial subset
- **Real video captions:** for video/reel posts, fetch Facebook's own auto-generated `.srt` caption file as a transcript (optional toggle)
- **Structured text entities:** hashtags, mentions, and external links are parsed out of the post text automatically — external links are the clean destination URL, not Facebook's tracking redirect
- **Direct video download links:** HD and SD progressive MP4 URLs for video/reel posts, not just a thumbnail
- Empty fields are omitted, not returned as `null`

### Output

Every target produces one record per post found (a single-post/reel URL always produces exactly one; a Page/Profile URL produces up to `maxItems`).

#### Post fields

- `postId` — the post's numeric Facebook ID
- `permalinkUrl` — direct link to the post
- `postType` — `photo`, `video`, `reel`, `link`, or `text`
- `text` — the post's full message text
- `hashtags` — hashtags found in the post text
- `mentions` — tagged people/Pages found in the post text
- `externalLinks` — external links shared in the post, as the real destination URL (not Facebook's `l.facebook.com` tracking redirect)
- `creationTime` — Unix timestamp of when the post was published
- `author` — `{id, name, url, profileUrlIsFallback}`; `profileUrlIsFallback` is `true` when Facebook didn't expose a direct profile URL and this actor constructed one from the author's numeric ID instead
- `attachments` — media on the post:
  - Photos: `url`, `width`, `height`, `accessibilityCaption` (Facebook's own OCR/alt-text)
  - Videos/reels: `videoUrlHd`, `videoUrlSd` (direct downloadable MP4 links), `thumbnailUrl`, `durationMs`, `width`, `height`, `isReel`, `captionsAvailable` (list of `{locale, languageName, url}`), and — when `fetchVideoCaptions` is on — `videoTranscript` (the real `.srt` file content) and `videoTranscriptLocale`
- `reactionsTotal` — total reaction count
- `reactionBreakdown` — `[{type, count}, ...]` covering all reaction types Facebook returns for the post (not capped at a fixed set)
- `commentsCount` — total comment count (including replies)
- `sharesCount` — total share count
- `isAd` — `true` if Facebook flags the post as sponsored
- `topComments` — top-level comments (present when `includeTopComments` is on): `{text, author, createdTime, reactionsTotal, replyCount}`
- `allComments` — up to 100 top-level comments per post (present when `fetchAllComments` is on, Page/Profile batches only), same shape as `topComments`
- `inputUrl` — the original URL from your input
- `scrapedAt` — ISO 8601 timestamp of when this record was scraped

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | `[{"url": "https://www.facebook.com/nike"}]` | Facebook Page/Profile URLs and/or single post/reel permalink URLs |
| `maxItems` | integer | `20` | Posts to fetch per Page/Profile URL (ignored for single-post/reel URLs) |
| `onlyPostsNewerThan` | string | – | Only posts published after this date: `2026-01-01`, full ISO 8601, or relative (`1 day`, `2 weeks`, `3 months`) |
| `onlyPostsOlderThan` | string | – | Only posts published before this date, same formats |
| `includeTopComments` | boolean | `true` | Attach each post's top-level comments (no extra request) |
| `fetchVideoCaptions` | boolean | `false` | Download the real `.srt` caption file for video/reel posts as a transcript |
| `fetchAllComments` | boolean | `false` | Page/Profile batches only: fetch up to 100 top-level comments per post as `allComments` (one extra request per post) |

#### Example: recent posts from a Page

```json
{
  "startUrls": [{ "url": "https://www.facebook.com/nike" }],
  "maxItems": 30
}
```

#### Example: a single post by URL

```json
{
  "startUrls": [
    { "url": "https://www.facebook.com/bbcnews/posts/pfbid02wicKt1z7teFytZajyGT72VCz5tcuVQmCZC4HrZTHZZ8SEyxZBgfxMn37pnRey6Sql" }
  ]
}
```

#### Example: posts from the last 30 days, with video transcripts

```json
{
  "startUrls": [{ "url": "https://www.facebook.com/nike" }],
  "onlyPostsNewerThan": "30 days",
  "fetchVideoCaptions": true
}
```

#### Example: mixing a Page batch and a single reel lookup in one run

```json
{
  "startUrls": [
    { "url": "https://www.facebook.com/nike" },
    { "url": "https://www.facebook.com/reel/752115997659579/" }
  ]
}
```

### Use cases

- **Social media monitoring** — track a brand or competitor Page's recent posts and engagement
- **Content research** — pull a Page's or Profile's post history for analysis
- **Fact-checking and journalism** — fetch a specific viral post by URL with its exact text, reactions, and comments
- **Video content archiving** — download direct MP4 links and captions for video/reel posts
- **Sentiment and engagement analysis** — reaction-type breakdowns and comment data for a batch of posts
- **Link and hashtag tracking** — structured extraction of hashtags and external links shared in posts

### FAQ

**Why does a single post URL return results faster than a Page/Profile URL?**
A post permalink page is fully rendered by Facebook itself, so this actor fetches it directly with a lightweight HTTP request — no browser needed. A Page/Profile URL only shows one post without scrolling, so scraping more requires a real browser session to trigger Facebook's own pagination.

**A standalone `/reel/...` URL sometimes returns nothing — will that be fixed?**
This is a genuine, confirmed Facebook restriction, not a bug in this actor: Facebook applies significantly stricter anti-bot checks to a Reel reached by pasting its permalink directly than to the exact same Reel reached by scrolling through a Page's timeline. This actor automatically retries a blocked single-Reel lookup before giving up — but confirmed via live testing across multiple accounts and previously-untouched Reel IDs that Facebook can still block the standalone deep link even after retrying. **If you need Reels reliably, scrape them via their Page/Profile URL instead of a standalone `/reel/...` link** — Reels returned as part of a Page/Profile batch (via `startUrls`) are not affected by this restriction.

**Why only 7 reaction types, and why does a post sometimes show fewer?**
Facebook has exactly 7 reaction types (Like, Love, Care, Haha, Wow, Sad, Angry). This actor returns whichever of them a post actually received — a post with no Angry reactions simply won't have an Angry entry in `reactionBreakdown`, not a zero.

**Are video view/play counts included?**
No. Facebook does not reliably expose video view counts on the public post surface this actor reads, so this field is never included — we don't fabricate or guess a number.

**Is the video transcript AI-generated?**
No — `videoTranscript` (when `fetchVideoCaptions` is on) is the real `.srt` caption file Facebook itself generated for the video, not independent speech-to-text transcription.

**Why is a post author's profile URL sometimes constructed rather than a direct Facebook link?**
Facebook doesn't always expose a direct profile URL for a post's author. When that happens, this actor builds a valid `profile.php?id=...` URL from the author's numeric ID and sets `profileUrlIsFallback: true` so you know it was constructed rather than returned directly by Facebook.

**Does this work on private profiles or restricted posts?**
No — a private, restricted, or removed target simply produces no record for that URL, rather than a fabricated empty-looking result.

**Do I need cookies or to log in?**
No. This actor reads only Facebook's publicly accessible pages — no login, no cookies, ever.

**Is this actor affiliated with Facebook or Meta?**
No. This is an independent, third-party actor that reads Facebook's own publicly accessible pages; it is not affiliated with, endorsed by, or operated by Meta Platforms, Inc.

**How fresh is the data?**
Every run fetches live from Facebook at request time — there's no caching, so results reflect what Facebook shows at the moment the actor runs.

**Are the comment/reaction/share counts accurate for posts with thousands of comments?**
Yes. These are Facebook's own aggregate totals, not something derived by counting visible items, so they're exact regardless of scale — verified directly against a real, logged-in view of a post with 4,169 comments: `commentsCount`, `reactionsTotal`, `sharesCount`, and every value in `reactionBreakdown` matched exactly. (The `topComments`/`allComments` arrays themselves are intentionally capped previews, not the full comment thread — see the field list above.)

# Actor input Schema

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

Mix any of: a Page/Profile URL (e.g. https://www.facebook.com/nike) to scrape its recent posts, or a single post/reel permalink (e.g. https://www.facebook.com/nike/posts/123..., https://www.facebook.com/reel/456...) to fetch just that one post. Single-post URLs are cheaper and faster since they don't need a browser.

## `maxItems` (type: `integer`):

How many recent posts to fetch per Page/Profile URL. Ignored for single-post/reel URLs, which always return exactly one result.

## `onlyPostsNewerThan` (type: `string`):

Only fetch posts published after this date. Accepts an absolute date ("2026-01-01"), a full ISO 8601 timestamp, or a relative value ("1 day", "2 weeks", "3 months"). Applied as a real server-side filter for Page/Profile batches, not just a post-fetch discard.

## `onlyPostsOlderThan` (type: `string`):

Only fetch posts published before this date. Same accepted formats as "Only posts newer than".

## `includeTopComments` (type: `boolean`):

Attach each post's top-level comments (already included in the same fetch — no extra request). Up to 10 comments for single-post/reel URLs, or Facebook's inline preview count for Page/Profile batches.

## `fetchVideoCaptions` (type: `boolean`):

For video/reel posts, download the actual .srt caption file content as a transcript. This is Facebook's own auto-generated captions, not independent AI transcription. Adds one extra request per video post.

## `fetchAllComments` (type: `boolean`):

For Page/Profile batches only, fetch up to 100 top-level comments per post (beyond the small free preview in "Include top comments") as an allComments field. Adds one extra request per post — only enable for smaller batches.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/nike"
    }
  ],
  "maxItems": 20,
  "includeTopComments": true,
  "fetchVideoCaptions": false,
  "fetchAllComments": false
}
```

# Actor output Schema

## `posts` (type: `string`):

Dataset containing all scraped Facebook posts.

# 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": [
        {
            "url": "https://www.facebook.com/nike"
        }
    ],
    "maxItems": 20,
    "includeTopComments": true,
    "fetchVideoCaptions": false,
    "fetchAllComments": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/facebook-posts-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": [{ "url": "https://www.facebook.com/nike" }],
    "maxItems": 20,
    "includeTopComments": True,
    "fetchVideoCaptions": False,
    "fetchAllComments": False,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/facebook-posts-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": [
    {
      "url": "https://www.facebook.com/nike"
    }
  ],
  "maxItems": 20,
  "includeTopComments": true,
  "fetchVideoCaptions": false,
  "fetchAllComments": false
}' |
apify call crawlerbros/facebook-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/facebook-posts-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/LeIMt69YyknKbHXI4/builds/MIaWIDr1XwQzxpah4/openapi.json
