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

Scrape posts from public Facebook Pages and profiles without login, by date window, with a per-URL coverage report. $3 per 1,000 posts.

- **URL**: https://apify.com/lordfloofington/facebook-posts-scraper.md
- **Developed by:** [Daniel G](https://apify.com/lordfloofington) (community)
- **Categories:** Social media
- **Stats:** 13 total users, 9 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $3.00 / 1,000 posts

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

Scrape posts from public Facebook Pages and profiles, plus single post, reel and video URLs, without login. Set a date window and receive a per-URL coverage report describing whether each feed was read completely. Each returned post costs $0.003 ($3 per 1,000); from October 21, 2026, each Page or profile checked also costs $0.003. A $0.00005 start fee applies per run.

### What it returns

Each dataset item is one post. Optional fields may be omitted.

| Name | Type | Meaning |
| --- | --- | --- |
| `facebookUrl` | string | Supplied Page, profile, post or video URL. |
| `inputUrl` | string | Original input URL. |
| `postId` | string | Facebook post ID. |
| `url` | string | Post permalink. |
| `topLevelUrl` | string | URL built from author and post IDs. |
| `time` | string | ISO 8601 publication time. |
| `timestamp` | number | Publication time, Unix seconds. |
| `text` | string or null, optional | Post text; videos can have empty or null text. |
| `user` | object | Author's `id`, `name`, `profileUrl` and `profilePic`; values can be null. |
| `pageName` | string | Name from the URL or resolved author name. |
| `facebookId` | string or null | Resolved Facebook ID. |
| `likes` | number or null | Reaction count. |
| `comments` | number or null | Comment count. |
| `shares` | number or null | Share count. |
| `topReactionsCount` | number or null | Reaction types represented. |
| `viewsCount` | number or null, optional | Video views when exposed. |
| `media` | array, optional | Photos or videos with available thumbnails and media fields. |
| `sharedPost` | object or null, optional | Shared post when exposed. |
| `link` | string, optional | Attached URL. |
| `isVideo` | boolean, optional | Whether the row contains video. |

Example row (shortened text and placeholder details):

```json
{"facebookUrl":"https://www.facebook.com/PAGE","inputUrl":"https://www.facebook.com/PAGE","postId":"POST_ID","pageName":"PAGE","url":"https://www.facebook.com/reel/POST_ID/","time":"2026-10-06T13:22:32.000Z","timestamp":1791292952,"user":{"id":"AUTHOR_ID","name":"Author","profileUrl":"https://www.facebook.com/AUTHOR_ID","profilePic":"https://example.com/profile.jpg"},"likes":62,"comments":1,"shares":3,"topReactionsCount":4,"topLevelUrl":"https://www.facebook.com/AUTHOR_ID/posts/POST_ID","facebookId":"AUTHOR_ID","collaborators":[],"paidPartnership":false,"topComments":[],"text":"Example post text…","media":[{"thumbnail":"https://example.com/thumbnail.jpg","__typename":"Video","url":"https://www.facebook.com/reel/POST_ID/","id":"VIDEO_ID"}],"isVideo":true}
```

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `startUrls` | Required | Array of objects containing a Facebook `url`. |
| `resultsLimit` | `20` | Maximum posts per URL; integer from 1–10,000. |
| `onlyPostsNewerThan` | No lower bound | Inclusive publication-time lower bound. |
| `onlyPostsOlderThan` | No upper bound | Exclusive publication-time upper bound. |
| `captionText` | `false` in the input editor | Compatibility boolean; ignored. |

The half-open window is `[onlyPostsNewerThan, onlyPostsOlderThan)`. Both bounds accept ISO dates, ISO datetimes or relative values such as `7 days`, `12 hours`, `2 weeks` and `1 month`, measured back from run initialization. Dates mean midnight UTC; datetimes without a timezone use UTC. Omit either bound; the lower bound must precede the upper bound. The window also filters single posts and videos.

Last seven days of one Page:

```json
{"startUrls":[{"url":"https://www.facebook.com/NASA/"}],"onlyPostsNewerThan":"7 days"}
```

One UTC day from several Pages:

```json
{"startUrls":[{"url":"https://www.facebook.com/NASA/"},{"url":"https://www.facebook.com/EuropeanSpaceAgency/"}],"onlyPostsNewerThan":"2026-10-01","onlyPostsOlderThan":"2026-10-02"}
```

### Supported URLs

Example paths use `https://www.facebook.com`; replace placeholder IDs/codes.

| URL form | Example | Notes |
| --- | --- | --- |
| Page or profile | `/NASA/`, `/profile.php?id=123`, `/people/Name/123` | Reads the timeline. Numeric paths work. |
| Post | `/NASA/posts/123`, `/NASA/posts/pfbidCODE`, `/permalink.php?story_fbid=123&id=456`, `/photo/?fbid=123` | Returns only the requested post; a reel's `/posts/` URL also works. |
| Reel | `/reel/123` | Likes, comments and shares; `viewsCount` is null. |
| Watch | `/watch/?v=123` | Likes, comments and `viewsCount`; `shares` is null. `/watch?v=123` also works. |
| Video | `/NASA/videos/123` | Same counts as watch; title-slug URLs such as `/NASA/videos/title/123` work. |
| Group post | `/groups/GROUP/posts/123`, `/groups/GROUP/permalink/123` | Public groups only. |
| Share | `/share/r/CODE`, `/share/v/CODE`, `/share/p/CODE` | Follows redirects to the final URL. |

Missing count fields are null, never estimated. Absent single-video media fields are also null. An absent post or video returns no row or post charge; its report uses `stopReason: "not_found"` and `status: "unavailable"`.

### How coverage is checked

Each accessible Page or profile feed is read at least twice using different IP addresses, with a third read when the first two differ. In-window results are merged by post ID. The first read stops after six consecutive posts older than the window start; later reads stop after three. Pinned posts and duplicates do not advance that streak.

`complete` and `empty` are reported only after two reads agree, or after a third read when the first two differ. All reads are merged by post ID, so a post found by any read is returned.

| Status | Meaning |
| --- | --- |
| `complete` | Posts returned; verification finished and traversal reached the date boundary or feed end. |
| `empty` | Same traversal conditions, with no posts in the window. |
| `partial` | Coverage limited by the post limit, charge cap, free plan limit or failed verification; possibly no rows. |
| `unavailable` | Target missing or hidden from logged-out visitors. |
| `failed` | Coverage could not be established and no posts were returned. |

Single-post URLs use one read. An available post outside the window returns no row and reports `empty`.

`stopReason` identifies the boundary (`passed_window_start`, `end_of_feed`), limit (`max_posts`, `charge_cap`, `free_plan_limit`), failure (`timeout`, `rate_limited`, `error`) or visibility issue (`not_found`, `private`, `login_wall`, `age_gated`, `restricted`, `not_visible_or_missing`).

The default key-value store's `OUTPUT` record contains the normalized window and reports in `targets`, with a `COVERAGE` log line per URL.

| Report field | Meaning |
| --- | --- |
| `walks` | Number of reads attempted. |
| `walksDisagreed` | Whether the first two reads differed. |
| `postsAddedByVerification` | Additional posts found before applying the results limit. |
| `outOfOrderPosts` | Older posts followed by an in-window or newer post during the first read. |
| `boundaryPost` | First post in the first read's final older streak. |
| `profileCharged` | Whether the profile event was charged. |

Reports also include `postsEmitted`, `newestPostAt`, `oldestPostAt`, `skipped`, `feedPagesRead`, `requestsFailed`, `retries` and `errors`. Compatibility fields `firstPageFetches` and `firstPageDisagreed` mirror `walks` and `walksDisagreed`.

Read `/records/OUTPUT` in the default store or through `/v2/actor-runs/{runId}/key-value-store/records/OUTPUT`.

### Pricing

- `post`: $0.003 per returned post ($3 per 1,000).
- `profile`: $0.003 per Page or profile checked, starting October 21, 2026, for `complete`, `empty` or `partial` results. Unavailable and failed URLs are free; single posts and videos pay only the `post` event.
- `apify-actor-start`: $0.00005 per run. There is no minimum post count.

From October 21, one profile checked for one day with four posts costs $0.015 in events, plus the start fee. Reaching your charge limit stops emission and crawling with `charge_cap`; coverage is `partial`.

### Limits

Runs by users on Apify's free plan are limited to 3 URLs and 20 results per run.

Only public content visible without login is available. Hidden or restricted Pages and age-gated posts cannot be returned. Comments and group feeds are not collected; `topComments` is always empty. Reels that appear on a timeline are returned like other posts.

The Actor keeps a small cache of Page/profile IDs in the runner's account (`facebook-posts-scraper-cache`), and deleting it only makes the next run slower.

A reel page can report a creation time a few minutes different from the feed's. Refresh counts by matching `postId`. `captionText` is accepted but does nothing.

### Use via API

Send JSON to the synchronous dataset-items endpoint:

```sh
curl -X POST 'https://api.apify.com/v2/acts/LordFloofington~facebook-posts-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{"startUrls":[{"url":"https://www.facebook.com/NASA/"}],"onlyPostsNewerThan":"7 days","resultsLimit":20}'
```

### Compliance

Users are responsible for complying with Facebook's terms and applicable law when using this Actor.

# Changelog

This Actor's version history is a separate document: https://apify.com/lordfloofington/facebook-posts-scraper/changelog.md

# Actor input Schema

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

Page, profile, or single post URLs to scrape. Runs by users on Apify's free plan are limited to 3 URLs and 20 results per run.

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

Inclusive lower date bound: ISO date or datetime, or a relative time such as 7 days.

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

Exclusive upper date bound: ISO date or datetime, or a relative time such as 2 weeks.

## `resultsLimit` (type: `integer`):

Maximum posts per URL; defaults to 20 when omitted.

## `captionText` (type: `boolean`):

Accepted for compatibility; this Actor does not fetch video transcripts.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/NASA/"
    }
  ],
  "resultsLimit": 20
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset of post rows.

## `coverage` (type: `string`):

Per-target coverage report in the OUTPUT key-value record.

# 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/NASA/"
        }
    ],
    "resultsLimit": 20,
    "captionText": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("lordfloofington/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/NASA/" }],
    "resultsLimit": 20,
    "captionText": False,
}

# Run the Actor and wait for it to finish
run = client.actor("lordfloofington/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/NASA/"
    }
  ],
  "resultsLimit": 20,
  "captionText": false
}' |
apify call lordfloofington/facebook-posts-scraper --silent --output-dataset

```

## MCP server setup

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