# Facebook Scraper: Pages, Posts, Reels, Comments & Marketplace (`abdelrahman12_3/facebook-scraper`) Actor

Public Facebook data without login: pages with contact details, posts, reels with captions, comments, Marketplace listings, groups and events. Low flat prices, free filters, monitoring mode and a coverage report for every input.

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

## Pricing

from $0.80 / 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 Scraper — Pages, Posts, Reels, Comments & Marketplace

Scrape public Facebook data **without logging in**:

- pages, with exact follower counts and contact details;
- posts, reels (with their captions), videos and photos;
- comments and replies;
- Marketplace listings;
- public groups and events.

Put anything in one box: page names, page links, post links, reel links, Marketplace search links or listing links. The scraper works out what each one is.

What makes it different:

- **You only pay for real results.** You are never charged for empty results, duplicates, items your filters removed, or items a monitor already gave you. Filters and sorting are free.
- **Every input gets a coverage report.** It says how many items you asked for, how many you got, and exactly why it stopped.
- **Reels come with their captions, plays and views.** Single reel links work too.
- **Date filters work on Facebook's side.** A page's posts from 2019 arrive without scrolling through everything newer, so old posts cost the same as new ones.

> This Actor is not affiliated with, endorsed by, or sponsored by Facebook or Meta. It only reads data that Facebook shows publicly to logged-out visitors.

### What you can scrape

| Input | Example | What you get |
|---|---|---|
| Page | `NASA`, `@NASA`, `https://www.facebook.com/NASA`, `profile.php?id=…`, `/p/Name-100…/` and `/pages/…/123…/` links | **Page details:** exact followers, category, bio, email, phone, websites, address, verified badge and pictures. **Page posts:** newest first, including reels, videos and photo posts. **Page transparency (extra):** creation date, ad status and confirmed owner. |
| Post | `https://www.facebook.com/NASA/posts/pfbid…`, `permalink.php?story_fbid=…`, `/share/p/…` | The post with all counts |
| Reel or video | `https://www.facebook.com/reel/930452829785006`, `/watch/?v=…`, `/NASA/videos/…`, `/share/r/…`, `/share/v/…`, `fb.watch/…` | Caption, reactions by type, comments, plays, views, duration and the video file link |
| Photo | `https://www.facebook.com/photo/?fbid=…` | Caption, counts and image |
| Comments | Turn on **Comments** | Comments on every delivered post: most relevant, newest first or all, optionally with replies |
| Marketplace search | `https://www.facebook.com/marketplace/nyc/search?query=bike&maxPrice=300`, or `marketplace: iphone 13 in London` | Listings with price, location, date and photo. Every filter in a pasted link is kept. |
| Marketplace listing | `https://www.facebook.com/marketplace/item/…` | The listing with description, condition and attributes (charged as a listing plus listing details) |
| Public group | `https://www.facebook.com/groups/…` | Group name and size, plus its posts (new, top or recent activity) |
| Event | `https://www.facebook.com/events/…` | Name, time, place, description, host and response count |

**Pasting inputs:**

- Paste them however you have them: one per line, or several on one line separated by commas.
- `@`, quotes and tracking parameters (`?ref=…`, `mibextid=…`) are fine.
- Mobile links (`m.facebook.com`) and share links (`/share/p/…`, `/share/r/…`, `fb.watch/…`) work too: the scraper opens them once and scrapes what they point to. A share link Facebook only opens for logged-in users is reported as `NOT_AVAILABLE_LOGGED_OUT` without charge.
- A `marketplace:` line is never split at commas, so `marketplace: iphone 13, 128gb in London` is one search.
- Duplicates such as `NASA`, `@nasa` and the page link are scraped once.

**Marketplace locations** can be written the way you would say them: `London`, `new york`, `القاهرة`, `Dubaii` (typos are fixed and noted in the run), `Portland, OR`, or coordinates such as `51.5072, -0.1276`. Smaller places such as `Mansoura` are looked up on Facebook itself. A place Facebook does not know, or a name that points to a different place than the one in our list (for example `London, Ontario`), stops with a message asking for coordinates instead of searching the wrong city. The coverage report shows the coordinates that were searched.

### What Facebook shows logged-out visitors

Facebook limits what anyone can see without an account. Here are those limits up front:

- **Not available:**
  - **Ad Library:** Facebook blocks automated logged-out access with a bot challenge, and this scraper does not get around it.
  - **Facebook's own search:** posts, pages, people and hashtags. Multi-word text is treated as a Marketplace search instead.
  - **Private groups, friend lists, follower lists, stories and who reacted.**
- **Marketplace sellers are hidden.** Facebook does not show the seller to logged-out visitors. Listings come without the seller.
- **A page's Reels tab is hidden.** Reels still come through the page's posts, which include its reels, and through reel links.
- **Unavailable content looks the same whatever the cause.** Deleted content and content limited by age or country show the same "This content isn't available" page, so both are reported as `NOT_FOUND`.
- **Follower counts:** exact numbers come from the page itself. When Facebook only gives a rounded number (for example "39M"), that is what you get.

When an input stops because of one of these limits, its coverage row says `FACEBOOK_LOGGED_OUT_CAP` or `NOT_AVAILABLE_LOGGED_OUT`, so you know nothing went wrong.

### Monitoring mode

1. Set **Monitor name** (for example `competitors`).
2. Save the input as a task and give it a schedule (hourly, daily…).
3. Each run returns only the posts, listings and events this monitor has never delivered before.

**For pages and groups,** a monitor stops reading once a whole page of posts is posts it delivered in an earlier run, so frequent checks stay cheap. A pinned or out-of-order old post does not stop it early.

**Page records are bought once per monitor.** The first run delivers the page record (and transparency, if on). Later runs skip the page record and its download entirely and only look for new posts.

**For Marketplace,** a monitor always sorts by newest first. A listing it already delivered comes back once more if its price changes. The record is marked `change: "price_drop"` or `"price_rise"`, with the old price in `previousPrice`.

Each input checked costs one *monitor check* event. That covers the check even when nothing new appeared. New items are charged as usual.

### Output

Each record type gets its own table, so every table stays clean: **Posts** (the main dataset), **Pages**, **Comments**, **Marketplace listings**, **Groups**, **Events** and **Coverage report**. You can open each one from the run's Output tab or through the API. Records in a table always have the same fields; a field that is unknown comes back as `null` instead of disappearing.

#### Post

```json
{
  "recordType": "post",
  "id": "1645077636987606",
  "url": "https://www.facebook.com/reel/930452829785006/",
  "type": "reel",
  "text": "Welcome aboard! 🙌 After nearly eight hours in flight, Crew-13 arrived at the International Space Station…",
  "createdAt": "2026-10-02T02:09:39.000Z",
  "author": { "id": "100044561550831", "name": "NASA - National Aeronautics and Space Administration", "url": "https://www.facebook.com/NASA" },
  "reactionsCount": 11835,
  "reactions": { "like": 9535, "love": 1813, "care": 191, "haha": 243, "wow": 43, "sad": 2, "angry": 8 },
  "commentsCount": 727,
  "sharesCount": 1046,
  "viewsCount": 156766,
  "playsCount": 415636,
  "videoDurationSec": 65.3,
  "imageUrl": "https://scontent…",
  "videoUrl": "https://video…mp4",
  "attachments": [{ "type": "video", "url": "https://www.facebook.com/reel/930452829785006/", "width": 3840, "height": 2160 }],
  "metrics": { "engagementRate": 0.00049, "ageHours": 37.1, "viewsPerHour": 4226.5 },
  "source": { "kind": "page", "value": "NASA" },
  "input": "https://www.facebook.com/NASA"
}
```

`type` is one of `status`, `photo`, `album`, `video`, `reel`, `link`, `event` or `shared`.

Group posts carry a `group` object. Posts from posts, reel and photo links have `detailLevel: "full"`; posts from feeds have `"feed"`.

#### Page

```json
{
  "recordType": "page",
  "id": "100044561550831",
  "pageId": "54971236771",
  "vanity": "NASA",
  "name": "NASA - National Aeronautics and Space Administration",
  "category": "Government organisation",
  "followersCount": 28723016,
  "followingCount": 52,
  "talkingAboutCount": 100290,
  "isVerified": true,
  "email": "public-inquiries@hq.nasa.gov",
  "phone": null,
  "websites": ["https://www.nasa.gov/nasa-app/", "https://www.nasa.gov/"],
  "bio": "Explore the universe and discover our home planet.",
  "transparency": { "creationDate": "4 March 2009", "isRunningAds": false, "adStatus": "This Page isn't currently running ads.", "confirmedOwner": "NATIONAL AERONAUTICS AND SPACE ADMINISTRATION" }
}
```

#### Comment

Each comment has its text, date, reactions, reply count, depth (0 = comment, 1 = reply), parent and post link.

**Commenter names are hidden by default** for privacy: each person gets a stable anonymous `authorHash` instead. The same applies to the members who wrote group posts. Comments written by the page itself keep its name. Turn on *Include people's names* if you need them and have a lawful reason to process them.

#### Marketplace listing

Each listing has:

- its title, price as a number and as written (`$100`), and crossed-out original price;
- city and state, listing date, photo, category and delivery types;
- sold and pending flags.

With **Listing details**, it also gets the description, condition, attributes and category name. Each listing also records the search it came from.

#### Coverage report (free)

The **Coverage report** table has one row per input. The same rows are also saved as the `COVERAGE` record in the run's key-value store.

```json
{
  "input": "https://www.facebook.com/NASA",
  "detectedType": "page",
  "requested": 50,
  "delivered": { "posts": 50, "pages": 1, "comments": 0, "listings": 0, "groups": 0, "events": 0 },
  "skipped": { "filtered": 0, "duplicate": 0, "seenBefore": 0 },
  "stopCode": "LIMIT_REACHED",
  "message": "Delivered the number of items you asked for.",
  "charged": { "page": 1, "post": 50 },
  "requests": 19
}
```

The possible stop codes are:

- `LIMIT_REACHED`: delivered what you asked for
- `NO_MORE_DATA`: delivered everything that exists
- `FACEBOOK_LOGGED_OUT_CAP`: Facebook shows no more to logged-out visitors
- `NOT_AVAILABLE_LOGGED_OUT`: this needs a login, for example a private group
- `NOT_FOUND`: deleted, wrong link, or limited by age or country
- `LOGIN_REQUIRED`
- `BLOCKED`
- `LAYOUT_CHANGED`
- `SPENDING_LIMIT`
- `RUN_LIMIT`
- `INVALID_INPUT`: the message says how to fix it
- `COMPLETED`: finished, for example a page with only page details asked for

The `SUMMARY` record has the run totals, every event charged and any input the scraper adjusted for you.

### Pricing

Pay per event. No monthly rental, no minimum results per query, and no extra charge for filters, sorting or date windows.

| Event | Price per 1,000 |
|---|---|
| Post (posts, reels, videos, photos) | $0.80 |
| Group post | $1.20 |
| Comment or reply | $0.35 |
| Page | $2.80 |
| Page transparency (extra, per page; charged alone when Page details is off) | $0.80 |
| Marketplace listing | $0.25 |
| Listing details (extra, per listing) | $0.40 |
| Event | $3.00 |
| Monitor check (per input, monitoring mode only) | $1.00 |
| Saved media file | $2.00 |

**Examples:**

- 100 pages with details and their latest 50 posts each: about **$4.30**.
- An hourly Marketplace monitor for one search: about **$0.75 a month**, plus $0.25 per 1,000 new listings.

**Never charged:**

- empty results
- duplicates
- filtered posts or listings
- items a monitor already delivered
- errors
- group records
- coverage rows

Set *Maximum cost per run* when you start a run, and the scraper will stop cleanly when it reaches it.

### Tips

- **Use residential proxies (the default).** Facebook blocks many datacenter IPs.
- **To reach old posts,** set *Posts older than* (for example `2020-01-01`). The page timeline jumps straight to that date.
- **Date filters take a date or a relative value.** Use the date picker in the form. Through the API you can send `2026-09-01`, `7 days`, `7d`, `2 weeks ago`, `3 months` or `yesterday`. If a date cannot be read safely, for example `03/04/2026`, the run stops before any request and tells you how to write it. Nothing is charged in that case.
- **Filters on reactions, comments, shares or views drop items whose counts are unknown.** They never guess.
- **For Marketplace,** paste the search link from your browser to keep every filter you set there, or use the Marketplace options in the form.
- **Use *Flat output* and snake_case for spreadsheets and databases.** `recordType` keeps its name in every mode.
- **Use *Run tag* to label batches when you run the same task many times.**

### Legal

This scraper only collects data Facebook shows publicly to logged-out visitors. It never logs in, never uses accounts or cookies from real users, never solves challenges, and never bypasses privacy settings.

Some results, such as names in comments and group posts, can still be personal data under laws like the GDPR. That is why names are hidden by default. You are responsible for having a lawful basis to collect and use the data, and for collecting only what you need. If you are unsure, ask a lawyer.

# Changelog

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

# Actor input Schema

## `targets` (type: `array`):

One per line (or several separated by commas). Accepts page names (NASA or @NASA), page links, profile.php?id= links, post links (/posts/, permalink.php, story_fbid), share links (/share/…, fb.watch), reel, video and photo links, public group links, event links, Marketplace search links (filters in the link are kept) and Marketplace item links. For a Marketplace search without a link, write marketplace: iphone 13 in London. Duplicates are scraped once.

## `pageDetails` (type: `boolean`):

Name, exact follower count, category, bio, email, phone, websites, address, verified badge and pictures for each page.

## `pagePosts` (type: `boolean`):

The page's posts, newest first, including reels, videos and photo posts. Use the date filters to reach older posts directly.

## `pageTransparency` (type: `boolean`):

Creation date, whether the page is running ads now, and the confirmed owner. Charged as an extra event per page. With Page details off, only the transparency event is charged.

## `comments` (type: `boolean`):

Comments on every delivered post (page posts, single post and reel links, group posts). Charged per comment.

## `commentsSort` (type: `string`):

The same choices Facebook offers under a post.

## `maxCommentsPerPost` (type: `integer`):

Replies count towards this limit too.

## `includeReplies` (type: `boolean`):

Also collect replies to comments (one level deep).

## `marketplaceLocation` (type: `string`):

City for Marketplace searches written as text (for example London, New York, Cairo, Dubai) or coordinates (51.5072, -0.1276). Small typos are fine. Not needed when you paste a Marketplace link or write marketplace: bike in London.

## `marketplaceRadiusKm` (type: `integer`):

Distance around the location.

## `marketplaceMinPrice` (type: `integer`):

In the local currency.

## `marketplaceMaxPrice` (type: `integer`):

In the local currency.

## `marketplaceSort` (type: `string`):

Monitoring always uses Newest first.

## `marketplaceDaysSinceListed` (type: `integer`):

Facebook's own choices are 1, 7 and 30. Leave empty for any time.

## `marketplaceCondition` (type: `array`):

Leave empty for any condition.

## `listingDetails` (type: `boolean`):

Opens each listing found by a search for its description, condition and attributes. Charged as an extra event per listing. Listing links in What to scrape always get details.

## `skipSold` (type: `boolean`):

Listings marked sold or pending are left out (and never charged).

## `groupSort` (type: `string`):

For public group links.

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

Only posts from this date on. Pick a date, or switch to relative for values like 7 days or 3 months. Applied by Facebook itself for page posts, so older posts are never downloaded. Free.

## `olderThan` (type: `string`):

Only posts before this date. Page posts jump straight to this date, so this reaches years-old posts without scrolling through newer ones.

## `minReactions` (type: `integer`):

Posts with fewer reactions, or an unknown count, are left out.

## `minComments` (type: `integer`):

Posts with fewer comments, or an unknown count, are left out.

## `minShares` (type: `integer`):

Posts with fewer shares, or an unknown count, are left out.

## `minViews` (type: `integer`):

Applies to videos and reels.

## `postTypes` (type: `array`):

Leave empty for all types.

## `maxItemsPerTarget` (type: `integer`):

Posts or listings per input. Pages, comments, groups and events are not counted.

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

Stop the whole run after this many posts and listings.

## `monitorName` (type: `string`):

Turns on monitoring: the run returns only posts, listings and events this monitor has not delivered before, so you never pay twice. Page records are delivered once per monitor. Marketplace listings whose price changed come back once more, marked price_drop or price_rise. Use the same name on a scheduled task. Upper or lower case and extra spaces do not matter.

## `trackPriceChanges` (type: `boolean`):

In monitoring mode, deliver a known listing again when its price changes.

## `includeCommenterProfiles` (type: `boolean`):

Off by default for privacy: commenters and group members get a stable anonymous ID instead of their name and profile link. Page names are always shown.

## `fieldCase` (type: `string`):

camelCase or snake_case keys.

## `flatten` (type: `boolean`):

Turn nested objects into dotted keys (author.name). Handy for spreadsheets.

## `runTag` (type: `string`):

Free text copied onto every record, to label batches.

## `saveMedia` (type: `string`):

Copies images (and optionally videos) of delivered posts into this run's storage before Facebook's links expire. Charged per file.

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

Residential proxies are recommended; Facebook blocks many datacenter IPs.

## `maxConcurrency` (type: `integer`):

How many requests run at the same time. Higher is faster but more likely to be blocked.

## `maxRequestsPerMinute` (type: `integer`):

Upper limit on requests per minute across the whole run.

## Actor input object example

```json
{
  "targets": [
    "https://www.facebook.com/NASA"
  ],
  "pageDetails": true,
  "pagePosts": true,
  "pageTransparency": false,
  "comments": false,
  "commentsSort": "relevant",
  "maxCommentsPerPost": 50,
  "includeReplies": false,
  "marketplaceRadiusKm": 65,
  "marketplaceSort": "best",
  "marketplaceCondition": [],
  "listingDetails": false,
  "skipSold": true,
  "groupSort": "chronological",
  "postTypes": [],
  "maxItemsPerTarget": 10,
  "maxItems": 1000,
  "trackPriceChanges": true,
  "includeCommenterProfiles": false,
  "fieldCase": "camel",
  "flatten": false,
  "saveMedia": "none",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 6,
  "maxRequestsPerMinute": 120
}
```

# Actor output Schema

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

No description

## `pages` (type: `string`):

No description

## `comments` (type: `string`):

No description

## `listings` (type: `string`):

No description

## `groups` (type: `string`):

No description

## `events` (type: `string`):

No description

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

No description

## `summary` (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 = {
    "targets": [
        "https://www.facebook.com/NASA"
    ],
    "maxItemsPerTarget": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("abdelrahman12_3/facebook-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 = {
    "targets": ["https://www.facebook.com/NASA"],
    "maxItemsPerTarget": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("abdelrahman12_3/facebook-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 '{
  "targets": [
    "https://www.facebook.com/NASA"
  ],
  "maxItemsPerTarget": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call abdelrahman12_3/facebook-scraper --silent --output-dataset

```

## MCP server setup

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