# Facebook Posts Scraper - Public Page Posts, No Login (`lanternlab318/facebook-posts`) Actor

- **URL**: https://apify.com/lanternlab318/facebook-posts.md
- **Developed by:** [Lantern Lab](https://apify.com/lanternlab318) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 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 - Public Page Posts, No Login

Give it Facebook Page URLs or names and get each Page's public posts back: text, publish time, reaction, comment and share counts, photo and video links, and the Page's name and IDs. No Facebook login, no cookies, no account of your own. **$1.99 per 1,000 posts plus $0.005 per run.** See [Pricing](#pricing).

### What makes it different

- **Your limit is a hard limit.** `maxPosts` is never exceeded, and you are never charged for more posts than you asked for.
- **No duplicates.** Posts are deduplicated across the whole run by Facebook's own post ID; a Page given twice is read once.
- **Blocked, missing and failed is free - and labelled.** Every input gets a free `input_status` row. A Page Facebook hides behind its login page, one that does not exist, or one cut short by a limit is marked `partial: true` (and `blocked: true` when Facebook refused us) with a reason - never reported as "zero posts".
- **The date filter works.** `newerThan` keeps only posts published since that date; older posts are skipped and not charged.
- **Real timestamps.** `publishedAt` is Facebook's own publish time; `collectedAt` is when this run read the post.

### Input

```json
{
    "startUrls": [{ "url": "https://www.facebook.com/nike" }],
    "pages": ["bbcnews"],
    "maxPosts": 20,
    "newerThan": "30 days"
}
```

| Field | What it does |
| --- | --- |
| `startUrls` | Page URLs: `https://www.facebook.com/nike`, `https://www.facebook.com/profile.php?id=<number>`. |
| `pages` | Page names instead of URLs: `nike`, `@bbcnews`, or a numeric Page ID. |
| `maxPosts` | Posts per Page, newest first. Default 20, up to 1,000. |
| `newerThan` | `2026-10-01`, an ISO time, or `7 days` / `2 weeks` / `3 months`. |
| `maxUpstreamRequests` | Optional hard ceiling on requests. By default the Actor works one out from your input, so a run is never unbounded. |
| `proxyConfiguration` | Proxy for each Page's first request (Apify datacenter by default). Timeline requests always use Apify residential proxy, the only IPs Facebook answers them on. |

### Output

Every row has a `recordType`. Dataset views: Posts, Input status.

**post**

```json
{
    "recordType": "post",
    "postId": "1096426988518676",
    "url": "https://www.facebook.com/nike/posts/pfbid02crEqAFcDiSVrDXAevm1sMzJwiCv2ajpCaWWotsSk7btboXoFe1kReAzJqBFrNvB3l",
    "text": "Nice finishes first. @janniksin takes home his second Grand Slam title of the year. ...",
    "publishedAt": "2024-09-08T20:38:04.000Z",
    "publishedAtEpoch": 1725827884,
    "reactionCount": 9480,
    "commentCount": 4094,
    "shareCount": 1569,
    "viewCount": null,
    "media": [
        { "type": "photo", "id": "1096426958518679", "imageUrl": "https://scontent-....fbcdn.net/...", "videoUrl": null }
    ],
    "imageUrls": ["https://scontent-....fbcdn.net/..."],
    "videoUrls": [],
    "sharedPostId": null,
    "pageName": "Nike",
    "pageProfileId": "100044541544829",
    "pageId": "15087023444",
    "pageUrl": "https://www.facebook.com/nike",
    "inputValue": "https://www.facebook.com/nike",
    "rank": 7,
    "partial": false,
    "partialReason": null,
    "collectedAt": "2026-10-31T14:40:12.511Z"
}
```

- `reactionCount` is all reactions together (Like, Love, Haha, ...), as Facebook shows it. `commentCount` and `shareCount` are Facebook's own totals; comment text is not collected.
- `media` lists each photo and video with its Facebook media ID: `imageUrl` is the photo (or the video's thumbnail), `videoUrl` the playable video file where Facebook gives one to logged-out visitors. Links only - nothing is downloaded. Facebook's media links expire after some days.
- `viewCount` is filled only where Facebook shows it logged-out, otherwise `null`. No field is ever estimated: what Facebook does not show is `null`.
- `pageProfileId` is the ID Facebook's timeline is keyed on; `pageId` is the classic numeric Page ID where Facebook gives one.

**input_status** (free) - one per input: `pageName`, `pageId`, `postsReturned`, `rowsCharged`, `duplicatesSkipped`, `olderSkipped`, `partial`, `blocked`, `partialReason` and a plain-English `statusText`.

**RUN_SUMMARY** (free) - the last row, and the same object in the key-value store: totals, incomplete inputs by reason, requests sent, and why the run stopped if it stopped early.

#### Partial reasons

| `partialReason` | `blocked` | Meaning |
| --- | --- | --- |
| `login_wall` | yes | Facebook sent us to its login page: the Page does not exist under that name, is a personal profile, or is age- or region-restricted. |
| `blocked` | yes | Facebook rate-limited or refused the requests, also on fresh IPs. Not an empty result. |
| `not_available` | no | Facebook says "This content isn't available right now": the Page was deleted, never existed, or is shown only to some people. |
| `invalid_input` | no | Not a Facebook Page name or URL (for example a different website, a group or an event). Nothing was requested. |
| `upstream_error` / `unexpected_shape` | no | A request failed, or the answer was not in a shape the Actor knows. Not an empty result. |
| `upstream_request_cap` | no | The run reached `maxUpstreamRequests`. |
| `charge_limit_reached` | no | The maximum charge you set for the run was reached. |
| `not_collected` | no | The run stopped before reaching this input. |

### Limits, stated up front

- **Public Pages only, no login.** Personal profiles, groups, events, Marketplace, Stories and anything that needs a Facebook account are out of scope.
- **Comments are counted, not collected.** This Actor returns each post's comment count, not the comments themselves.
- **Order.** Posts come in the order Facebook's timeline lists them to a logged-out visitor: newest first, with now and then an older post in between. Pinned posts are left out by Facebook in this view.
- **End of the list.** When Facebook stops listing a Page's posts to logged-out visitors before `maxPosts`, the Page's `statusText` says so.
- **Speed.** Facebook gives 3 posts per request, so 100 posts take about 34 requests.

### Pricing

Pay per result: **$1.99 per 1,000 posts**, plus **$0.005 per run** (start fee). A result is one `post` row. `input_status` rows, the run summary, duplicates, posts older than `newerThan`, and anything a Page could not return are free, so a blocked, missing or empty Page costs nothing beyond the start fee. Example: 10 Pages x 50 posts = 500 posts = $0.995 + $0.005.

### How it works

Plain HTTP requests to the same public endpoints Facebook's own logged-out Page view uses: the Page itself (name, IDs and newest post), then the Page timeline's next pages, 3 posts at a time, through Apify residential proxy. A rate-limited request is retried on a new IP. No browser, no login, no cookies of your own.

# Actor input Schema

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

One public Facebook Page per line, for example https://www.facebook.com/nike or https://www.facebook.com/profile.php?id=100064620046507. Groups, events, Marketplace and personal profiles are not supported.

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

Optional: Pages by name instead of URL, one per line: "nike", "@bbcnews" or a numeric Page ID. Pages given twice, here or in the URLs, are read once.

## `maxPosts` (type: `integer`):

Upper bound on the post rows one Page may produce, in the order Facebook lists them (newest first). Never exceeded, and you are never charged for more.

## `newerThan` (type: `string`):

Leave empty for no date filter. A date (2026-10-01), a full time (2026-10-01T08:00:00Z) or a relative period ("7 days", "2 weeks", "3 months"). Older posts are skipped and not charged; a Page stops after two answers in a row that hold nothing newer.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the HTTP requests this run sends. At the ceiling collection stops: what was read is kept, unfinished Pages are written with partialReason "upstream_request_cap" and the run ends normally. Leave it empty and the Actor works a ceiling out from your Pages and limits, so a run is never unbounded.

## `proxyConfiguration` (type: `object`):

The proxy each Page's first request goes through (Apify Proxy, datacenter, by default). Facebook answers its timeline requests only on residential IPs, so those always go through Apify residential proxy.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.facebook.com/nike"
    }
  ],
  "maxPosts": 20,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# 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"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lanternlab318/facebook-posts").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" }] }

# Run the Actor and wait for it to finish
run = client.actor("lanternlab318/facebook-posts").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"
    }
  ]
}' |
apify call lanternlab318/facebook-posts --silent --output-dataset

```

## MCP server setup

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

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/7s3V9Bh28hx0WxaFl/builds/DBTPaJ0DuaAhBRCwN/openapi.json
