# Facebook Video Scraper (`fertech/facebook-video-scraper`) Actor

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

## Pricing

from $2.99 / 1,000 results

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 Video & Reel Scraper

Give it Facebook video, reel or story URLs. Get back structured data with
**exact play and view counts** — where the page shows one rounded figure and
calls it "views".

No login, no cookies, no session tokens.

**One flat rate, whatever Apify plan you are on. No plan ladder — see the
pricing section of this listing for the current rate.**

### Why this one

**Exact numbers, not rounded ones.** Facebook's page rounds what it shows
and hides the rest. On one real post:

| | Facebook's page shows | This Actor returns |
|---|---|---|
| Plays | "486K views" | **486,574** |
| Views | nothing at all | **306,796** |
| Reactions | "5K reactions" | **5,019** |

**Plays and views are not the same number.** Plays count replays. The figure
Facebook labels "views" on the page is really the play count — the actual
view count, 306,796 against 486,574 plays on that post, is not shown
anywhere. Both come back, under their own names.

### What you get

Feed it a list of Facebook video, reel or story URLs, get one structured
record per video.

- **Engagement**: exact plays, views, reactions and comments
- **Reactions**: the type breakdown, not just the total
- **Video**: caption, hashtags, mentions, dimensions, publish time
- **Owner**: page or profile name, id, URL, picture
- **Media**: video and thumbnail URLs

### Input

```json
{
  "postURLs": [
    "https://www.facebook.com/reel/1234567890123456/",
    "https://www.facebook.com/examplepage/videos/behind-the-scenes/1234567890123456/",
    "https://fb.watch/ExampleAB1/",
    "https://www.facebook.com/share/r/ExampleCD2/",
    "https://m.facebook.com/story.php?story_fbid=pfbid0...&id=100000000000002"
  ],
  "maxAttemptsPerUrl": 4
}
```

Every URL shape Facebook hands out works — the app's share sheet, the
desktop address bar and the mobile site all produce different formats, and
all of them are accepted. Tracking parameters (`mibextid`, `extid`, `ref`)
are ignored, so the same post submitted in two forms is fetched, delivered
and charged once.

### Output

One record per URL. Field names, types and shapes are exactly what the Actor
emits; values are illustrative:

```json
{
  "inputUrl": "https://www.facebook.com/reel/1234567890123456/",
  "id": "1234567890123456",
  "url": "https://www.facebook.com/watch/?v=1234567890123456",
  "type": "Video",
  "caption": "Behind the scenes from last weekend #liveshow #backstage #fyp",
  "hashtags": ["liveshow", "backstage", "fyp"],
  "mentions": [],
  "reactionsCount": 5019,
  "topReactions": ["Like", "Love", "Haha", "Care", "Angry"],
  "commentsCount": 25,
  "playCount": 486574,
  "viewCount": 306796,
  "shareCount": null,
  "timestamp": "2026-09-08T09:01:34.000Z",
  "ownerName": "Example Page",
  "ownerId": "100000000000001",
  "ownerUrl": "https://www.facebook.com/people/Example-Page/100000000000001/",
  "ownerProfilePicUrl": "https://scontent.xx.fbcdn.net/...jpg",
  "width": 720,
  "height": 1280,
  "videoUrl": "https://video.xx.fbcdn.net/...mp4",
  "thumbnailUrl": "https://scontent.xx.fbcdn.net/...jpg"
}
```

`videoUrl` and `thumbnailUrl` are signed and **time-limited** — download the
media rather than storing the link.

**A number that is missing is `null`, never `0`.** A zero here always means a
real zero. `shareCount` is always null: Facebook publishes no share count on
this surface.

### Errors

A URL that cannot be scraped still produces a record, carrying `error` and
`errorDescription`:

| `error` | Meaning | Retried |
|---|---|---|
| `invalid-url` | Not a Facebook video, reel or story URL | no |
| `unresolved-link` | A share link could not be resolved to a video — its redirect failed, or it led somewhere that is not a video | no |
| `not-found` | Removed, private, or never existed | no |
| `blocked` | Facebook served no data for this video on any attempt | yes, and the exit IP is rotated once it has failed enough times |
| `unsupported-page` | A page loaded but named no video | no |
| `network` | Connection, TLS, timeout or proxy failure | yes |

Facebook rate-limits repeated requests from one IP. The Actor detects that
and retries, dropping an exit IP once it has failed enough times, so this
rarely reaches your dataset — and **retries are not charged.** You submit
each URL once and pay once, whatever it takes.

### Pricing: every submitted URL is charged once

See the pricing section of this listing for the current rate.

**One URL in, one charge out.**

Posts get removed, accounts go private, and Facebook rate-limits repeated
requests. When a URL can't be scraped you get an error record instead of a
video record, so you always know what happened to it — and it costs the same
as a delivered one.

Retries and duplicates are not charged on top. You pay once per URL you
submit, however many attempts it takes.

### Requirements

**Residential proxies are recommended.** Facebook rate-limits repeated
requests from one IP within seconds, so an unproxied run returns mostly
empty pages.

The Actor uses Apify residential proxies by default, so there is nothing to
configure — no proxy picker in the input form, though an API caller can
still override it.

Residential proxies are **not included in the Apify Free plan.** On the Free
plan this Actor stops with a clear message rather than spending your credit
on requests that mostly cannot succeed.

### Limitations

- Video content only. Photo posts, page feeds, profiles and groups are not
  supported.
- Comment text is not returned; `commentsCount` is.
- `shareCount` is always null — Facebook publishes none.

### Reading the run summary

Each run writes a summary to the log and to the key-value store, under
`RUN_SUMMARY`:

```
requested 200 | delivered 187 (93.5%) | unpaid 0 | notfound 8 | blocked-responses 42 | failed 5 | callbackErrors 0
```

| Field | What it counts |
|---|---|
| `requested` | URLs you submitted |
| `delivered` | URLs that produced a video record |
| `notfound` | Removed, private or non-existent posts |
| `failed` | URLs that ended in any other error record |
| `blocked-responses` | *Responses* that were rate-limited — not URLs |
| `unpaid` | URLs processed after the charge budget ran out |
| `callbackErrors` | Rows that could not be written. Should always be 0 |

**These do not sum to `requested`**, and that is deliberate. Every URL ends
as exactly one of `delivered`, `notfound` or `failed`. `blocked-responses`
sits alongside them: one video rate-limited twice and then fetched adds 1 to
`delivered` and 2 to `blocked-responses`.

### Using the API

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("YOUR_USERNAME/facebook-video-scraper").call(run_input={
    "postURLs": ["https://www.facebook.com/reel/1234567890123456/"],
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["playCount"], item["ownerName"])
```

### Is scraping Facebook legal?

This Actor collects only publicly available data — information anyone can
see without logging in. It does not access private accounts or login-walled
content. You remain responsible for how you use the data, particularly
regarding personal information and the GDPR. If you plan to process personal
data, seek your own legal advice first.

### Issues and requests

Found a bug, or want photo posts, page feeds or comment text supported?
Open an issue on the Actor's Issues tab — feature demand genuinely drives
what gets built next.

# Changelog

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

# Actor input Schema

## `postURLs` (type: `array`):

Facebook video, reel or story URLs. All the shapes Facebook hands out work: /reel/<id>, /watch/?v=<id>, /videos/<id>, /<page>/videos/<slug>/<id>/ (what the "Copy link" button produces), share links (facebook.com/share/r/... and fb.watch/...) and m.facebook.com story.php links. Tracking parameters are ignored.

## `maxAttemptsPerUrl` (type: `integer`):

Facebook rate-limits repeated requests from one IP. An exit IP that keeps failing is dropped for a new one, so more attempts raise the success rate at the cost of bandwidth. Values outside 1-10 are clamped.

## Actor input object example

```json
{
  "maxAttemptsPerUrl": 4
}
```

# Actor output Schema

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

All scraped video records and any error records. An error record carries an error code of invalid-url, unresolved-link, not-found, unsupported-page, blocked or network, plus an errorDescription.

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

Delivery counts for the run, as written: requested, delivered, unpaid, notfound, blocked, failed, callbackErrors, plus a one-line summary.

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

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

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

```

## MCP server setup

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