# Facebook Page Posts Scraper (`s-r/facebook-pages`) Actor

Read the posts on any public Facebook page: text, posting date, exact and displayed reaction and share counts, attachment type and whether the post is sponsored. Paste page handles or links. No login.

- **URL**: https://apify.com/s-r/facebook-pages.md
- **Developed by:** [SR](https://apify.com/s-r) (community)
- **Categories:** Social media, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Facebook Page Posts Scraper

Read the posts on any public Facebook page as structured data: the text, when it
was posted, how many reactions and shares it got, what kind of attachment it
carries, and whether it was a paid placement.

Paste page handles or links. No login, no API key, no browser.

### What you get per post

- `text` — the post as written
- `posted_at` — a UTC timestamp
- `reaction_count` and `reaction_count_display`
- `share_count` and `share_count_display`
- `media_type` — Photo, Video, or whatever Facebook calls the attachment
- `link` — where the post points, when it points somewhere
- `is_sponsored` — true for paid placements
- `page`, `page_name`, `page_id`, `url`, `post_id`

### Two numbers for every count, on purpose

Facebook publishes each count twice: `4715` and `"4.7K"`. They are not
alternatives and neither is derived from the other.

The rounded one is what a visitor sees on the page, so it is the honest answer
to "what does this post say it got". The exact one is the only one you can add
up, average, or sort reliably: parse `"4.7K"` into `4700` and you have silently
replaced a real figure with a guess, and lost the real one.

So both are returned, named for what they are. `reaction_count` for arithmetic,
`reaction_count_display` for quoting.

### There is no comment column, and that is deliberate

Reactions and shares are in the page's data. **Comment counts are not** — they
are rendered separately and never appear as a number.

Rather than ship a `comment_count` column full of zeroes, there is no such
column. A post with 190 comments reported as `0` is worse than one that never
claims to know, because a zero survives into a spreadsheet, gets averaged, and
nobody questions it. The run summary states the absence once, with
`commentCountsAvailable: false`.

### Which links work

A page handle (`nasa`), a page link (`https://www.facebook.com/nasa`), or a
numeric profile link all work.

**Groups, events, Watch and Marketplace links are refused by name.** They are
different surfaces with different payloads, and attempting them would spend a
page budget to produce an unhelpful error. A rejected line is reported with the
line in it, so a run of fifty tells you exactly which one was wrong.

### How much you get

A page returns roughly **20 posts** in one request, newest first. **Maximum
posts** is a ceiling across all the pages in the run, so three pages at 45 posts
gives you the newest 20, 20 and 5 rather than a third of each.

`postsPerPage` in the summary breaks it down, which is the quick way to spot a
page that returned less than it should have.

Three pages and 45 posts took **twelve seconds** in testing, and every page
tested came back on the first attempt.

### About the country you search from

Facebook localises what a page shows. The **Search from country** input pins
where the request appears to come from and is recorded with every run, so two
runs a week apart are comparable rather than accidentally different.

### What people use this for

**Competitor monitoring.** Run a set of pages on a schedule and keep the rows.
Because `posted_at` and both counts are on every row, posting frequency and
engagement per post fall straight out of the table.

**Engagement benchmarking.** Reactions against shares, by attachment type,
across many pages, is the comparison that tells you what actually travels on
Facebook rather than what gets a passive like.

**Brand and PR tracking.** New posts mentioning a topic, with the page they came
from and how far they spread.

**Content research.** `media_type` next to the counts answers whether photos or
videos work better for a given page, on that page's own audience rather than in
the abstract.

**Ad detection.** `is_sponsored` separates what a page paid to show from what it
posted, which changes how you read every other number on the row.

### What this does not do

**No groups, no comments, no profiles.** This reads pages. Facebook groups are a
different surface with a different payload; personal profiles are not public in
the way pages are; and comments are not published as text or as a count here.
Each of those is a separate problem rather than a switch on this one.

**No search.** There is no keyword search over Facebook posts without an
account. You name the pages you care about, and that is the honest shape of what
is reachable.

**Reactions are not broken down by type.** Facebook publishes the total and, in
this payload, not the split between like, love, angry and the rest. The total is
what you get.

### Notes

Only public pages can be read. A page that is restricted, age-gated or removed
is reported by name rather than returned as an empty result you would have to
interpret.

Counts move constantly on popular posts, so a run is a snapshot. That is why the
posting date is on every row.

Posts come from the page's own feed, so this returns what the page published,
not what it was tagged in and not its comments.

# Actor input Schema

## `pages` (type: `array`):

Page handles or links, one per line, for example nasa or https://www.facebook.com/nasa. A numeric profile link works too. Groups, events, Watch and Marketplace links are not pages and are reported as such rather than attempted.

## `max_posts` (type: `integer`):

Stop after this many posts across all pages. A page carries about 20 in one request, so this is also the cost ceiling.

## `max_pages` (type: `integer`):

Stop after this many pages, counted after duplicates are removed.

## `attempts` (type: `integer`):

How often to retry a page that comes back without posts. Four is comfortable; every measured page succeeded on the first try.

## `region` (type: `string`):

Two-letter country code the request should appear to come from. Facebook localises what a page shows, so this is recorded rather than left to chance.

## Actor input object example

```json
{
  "pages": [
    "bbcnews"
  ],
  "max_posts": 50,
  "max_pages": 20,
  "attempts": 4,
  "region": "gb"
}
```

# Actor output Schema

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

One row per post.

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

Pages read, posts per page, how many carried text and reactions, and total reactions.

## `errors` (type: `string`):

Pages that could not be read.

# 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 = {
    "pages": [
        "nasa",
        "https://www.facebook.com/natgeo"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("s-r/facebook-pages").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 = { "pages": [
        "nasa",
        "https://www.facebook.com/natgeo",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("s-r/facebook-pages").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 '{
  "pages": [
    "nasa",
    "https://www.facebook.com/natgeo"
  ]
}' |
apify call s-r/facebook-pages --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,s-r/facebook-pages"
        }
    }
}
```

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/w1pkeC6vVPXKauOck/builds/uetq9bOxQVkPjBnyU/openapi.json
