# Facebook Posts Scraper - $0.60 per 1,000 Posts (`dami_studio/facebook-posts-scraper-v1`) Actor

Scrape public Facebook Page posts without a login or cookies: text, date, permalink, photos and videos, reactions with the per-emoji breakdown, comment and share counts, and page identity. The cheapest Facebook posts scraper on the market at $0.60 per 1,000 posts. Date filters included free.

- **URL**: https://apify.com/dami\_studio/facebook-posts-scraper-v1.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 post scrapeds

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/platform/actors/running/actors-in-store#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 Posts Scraper

Give it a public Facebook Page URL, get that Page's recent posts back as rows: the post text, when
it was posted, the permalink, the photos and videos attached to it, and the reaction, comment and
share counts — plus who posted it.

No Facebook account. No cookies to paste. No login, no session token, no captcha to solve, and no
proxy of your own. Paste a URL, press start.

**This is the cheapest Facebook posts scraper on the market: $0.60 per 1,000 posts.** Flat on
every plan, one billable event, and date filtering costs nothing extra.

### Price

**$0.60 per 1,000 posts**, plus $0.0000125 per GB of run memory to start a run (a hundredth of a
cent on the default 1 GB).

You are charged **once per post row that actually comes back**. Nothing else bills:

- the sample row you get from an empty run is free
- diagnostic rows (page is private, page has no public posts, Facebook refused) are free
- a run that finds nothing costs only the start fee
- date filtering is free — no per-post surcharge

| Posts | Cost |
|---|---|
| 20 | $0.012 |
| 100 | $0.060 |
| 1,000 | $0.60 |
| 10,000 | $6.00 |

#### One event, flat on every plan

$0.60 per 1,000 is the rate on the free plan and on every paid plan alike — no volume tiers to
climb, no minimum spend, no plan gates. There is exactly one billable result event, so nothing in
the row is metered separately: the full reaction breakdown, the media list, the Page identity block
and both date filters are all part of the same post row.

What that means in practice:

| Job | Posts | Cost |
|---|---|---|
| One Page, latest 20 posts | 20 | $0.012 |
| 10 competitor Pages, daily, 20 posts each | 200/day | $0.12/day, about $3.60/month |
| 100 Pages, 100 posts each, one-off backfill | 10,000 | $6.00 |
| Date-filtered window on 50 Pages | only what matches | filtered-out posts are never charged |

Add the $0.0000125-per-GB start fee once per run and that is the whole bill.

### Input

| Field | What it does |
|---|---|
| `startUrls` | Public Facebook Page URLs. `https://www.facebook.com/NASA`, `facebook.com/pg/cocacola/posts/`, `profile.php?id=…` and bare handles all work. |
| `resultsLimit` | How many recent posts per Page. Default 20, maximum 500. |
| `onlyPostsNewerThan` | Optional. `2026-06-01`, a full ISO timestamp, or `3 days` / `2 months` / `1 year`. |
| `onlyPostsOlderThan` | Optional. Same formats. Combine the two to pull a specific window. |
| `proxyConfiguration` | Optional. Leave it alone unless you want the run to go through your own proxy servers. |

```json
{
  "startUrls": [
    { "url": "https://www.facebook.com/NASA" },
    { "url": "https://www.facebook.com/natgeo" }
  ],
  "resultsLimit": 50,
  "onlyPostsNewerThan": "3 months"
}
```

Run it with no input at all and you get one clearly labelled sample row, uncharged, so you can see
the shape before spending anything.

### Output

A real row from a real run on `https://www.facebook.com/NASA`:

```json
{
  "facebookUrl": "https://www.facebook.com/NASA",
  "pageName": "NASA - National Aeronautics and Space Administration",
  "pageId": "100044561550831",
  "pageUrl": "https://www.facebook.com/NASA",
  "pageIsVerified": true,
  "pageLikes": 28694568,
  "pageProfilePicture": "https://scontent.fagc3-2.fna.fbcdn.net/v/t39.30808-1/243095782_416661036495945_…png",
  "postId": "1602283071267063",
  "url": "https://www.facebook.com/NASA/posts/pfbid0zJ6mq7LDUr5ZxqeqrrPDCWnv87PJXe1qg9YoKpQNyULieuLXMdJjXJ2DEUAcTwfyl",
  "text": "Our photographers were on hand in Maine and Spain to capture the Aug. 12 solar eclipse. Check out a few of their photos here and see the rest on Flickr: https://www.flickr.com/photos/nasahqphoto/",
  "textLength": 195,
  "time": "2026-08-13T18:17:19.000Z",
  "timestamp": 1786645039,
  "likes": 23950,
  "reactionsCount": 23950,
  "topReactions": [
    { "type": "Like",  "count": 16618 },
    { "type": "Love",  "count": 6669 },
    { "type": "Wow",   "count": 328 },
    { "type": "Care",  "count": 303 },
    { "type": "Haha",  "count": 20 },
    { "type": "Sad",   "count": 9 },
    { "type": "Angry", "count": 3 }
  ],
  "comments": 321,
  "shares": 2616,
  "media": [
    {
      "type": "photo",
      "id": "1602282937933743",
      "url": "https://www.facebook.com/photo.php?fbid=1602282937933743&set=a.416661013162614&type=3",
      "thumbnailUrl": "https://scontent-ord5-1.xx.fbcdn.net/v/t39.99422-6/774600070_2460024401176674_…png"
    }
  ],
  "mediaCount": 4,
  "postType": "photo",
  "isReel": false,
  "isSponsored": false,
  "linkUrl": null,
  "linkTitle": null,
  "requestedUrl": "https://www.facebook.com/NASA",
  "scrapedAt": "2026-08-15T01:12:44.108Z"
}
```

That post is a 4-photo album, so `mediaCount` is 4 and `media` holds four entries — only the first
is shown above to keep the example readable.

`topReactions` is the per-emoji breakdown Facebook shows when you hover the reaction bar, so you can
separate 6,669 Loves from 3 Angrys instead of only seeing one lump total.

#### Field coverage

Measured across a real 20-post run on NASA, not estimated:

| Field | Present |
|---|---|
| `postId`, `url`, `time`, `timestamp`, `likes`, `comments`, `shares`, `topReactions` | 20/20 |
| `pageName`, `pageId`, `pageUrl`, `pageIsVerified`, `pageLikes`, `pageProfilePicture` | 20/20 |
| `text` | 19/20 |
| `media` | 15/20 |

The one row without `text` is a photo post with no caption, and the five without `media` are
text-only posts. Missing values are `null` or an empty array — never a guess.

### Limits — read this before you build on it

- **Public Pages only.** Personal profiles, private groups and anything behind a login are out of
  scope. The whole design is logged-out; there is no cookie to paste and no account to create.
- **Comments themselves are not returned**, only the comment *count*. Pulling comment threads needs
  a separate request per post, which would change the price.
- **View counts are not returned.** Facebook does not put a view count on logged-out timeline
  units, so there is no honest number to report and none is invented.
- **`pageFollowers` is often `null`.** Facebook's public page description gives either a like count
  or a follower count depending on the Page. Whichever it gives is what you get.
- **`linkUrl` / `linkTitle` are only filled for link-share posts.** On photo, video and plain text
  posts they are `null`.
- **Some Pages return nothing and it is not a bug.** A minority of Pages serve a logged-out visitor
  no timeline at all — `facebook.com/cocacola` is a real, reproducible example: the Page itself
  resolves (name, id, likes all come back) but Facebook refuses its feed to every logged-out
  address. Age-restricted, country-restricted and brand-new Pages behave the same way. You get a
  free diagnostic row explaining it, and you are charged nothing for that page.
- **Depth.** 500 posts per Page is the ceiling here. Very deep runs need the run timeout raised
  above the 300 s default, because posts arrive 20 at a time.
- **Reaction counts are Facebook's own rounded totals** for the moment the post was read. They move.

### FAQ

**Do I need a Facebook account, cookies or a login?**
No. Nothing to log in to, nothing to paste, nothing to keep alive.

**Do I need to bring my own proxy?**
No. Exit addresses are included in the per-post price.

**Can I scrape a personal profile?**
No — Pages only. A personal profile's timeline is not public to logged-out visitors.

**Can I get the comments on each post?**
Not here. You get the comment count. Comment text needs a per-post fetch and belongs in a separate
Actor with its own price.

**How do I only get posts from the last month?**
Set `onlyPostsNewerThan` to `1 month`. It costs nothing extra, and posts that fall outside the
window are fetched and discarded without ever being charged to you.

**What does 1,000 posts cost?**
$0.60, plus the fraction-of-a-cent start fee.

**What happens if the Page has no public posts?**
You get one free diagnostic row explaining why, and you are charged nothing for it.

**Can I export to CSV or Excel?**
Yes. Apify exports the dataset as JSON, CSV, Excel or XML, and the same data is available over the
REST API.

**Is this affiliated with Facebook or Meta?**
No. It reads publicly visible Page content. Check Facebook's terms and your own local rules before
using the output commercially.

### How it works

Facebook's Page HTML and Facebook's GraphQL endpoint are two different gates, and that distinction
is the whole reason this can be cheap.

The rendered Page HTML answers from ordinary datacenter addresses, and it carries the numeric Page
id, the Page's public identity, and the exact persisted GraphQL query Facebook's own web client
uses for the timeline — query id and variables both. So the query is lifted verbatim rather than
guessed at, and reading it costs nothing.

`POST /api/graphql/` is a different story: it answers `Rate limit exceeded` from every datacenter
address tried, fresh session or not, warmed up or not. That is a network-class refusal, not a real
rate limit. Only residential-grade addresses get through, and only while carrying a `lsd` token and
`datr` cookie minted on that same address.

So the run splits its traffic: Page resolution goes over cheap addresses, and only the feed calls
go over metered ones. Then the pagination trick — Facebook's own "load more" refetch returns
**3 posts per call** no matter what you ask for, but the timeline fragment also accepts
`beforeTime`, and the cheap first-page query honours it. Stepping `beforeTime` back to just before
the oldest post seen returns the next **20**. That is roughly six times less bandwidth per post,
and it is what makes $0.60 per 1,000 a price this Actor can actually sustain.

No browser is launched at any point.

# Actor input Schema

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

Public Facebook Page URLs, one per line. Public Pages only — personal profiles and private groups need a login and are not supported.

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

How many recent posts to return for each Page URL. Charged per post actually returned.

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

Optional. Skip posts older than this date. Accepts 2026-01-31, a full ISO timestamp, or a relative value like "3 days", "2 months", "1 year".

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

Optional. Skip posts newer than this date. Same absolute or relative format as the field above.

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

Optional. Leave empty and the Actor picks its own exits — the price already covers them. Supply your own proxy URLs here only if you want the run to go through your addresses.

## Actor input object example

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

# Actor output Schema

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

Posts scraped from the requested Facebook Pages. Each row carries facebookUrl, pageName, pageId, pageUrl, pageIsVerified, pageProfilePicture, pageLikes, pageFollowers, postId, url, text, textLength, time, timestamp, likes, reactionsCount, topReactions, comments, shares, media, mediaCount, postType, isReel, isSponsored, linkUrl, linkTitle, requestedUrl and scrapedAt.

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

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

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

```

## MCP server setup

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

```

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/fve5NuHQH8Y69QssV/builds/uEAB5VOi6l0euBy0O/openapi.json
