# Facebook Videos Scraper By Creator Profile (`simpleapi/facebook-videos-scraper`) Actor

Facebook Videos Scraper extracts data from Facebook video posts, including titles, descriptions, view counts, reactions, comments, shares, and upload timestamps. Ideal for content analysis, trend monitoring, research, and automating structured Facebook video data collection

- **URL**: https://apify.com/simpleapi/facebook-videos-scraper.md
- **Developed by:** [SimpleAPI](https://apify.com/simpleapi) (community)
- **Categories:** Lead generation, Videos, Social media
- **Stats:** 27 total users, 4 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Reels Scraper — Creator Videos, DASH Formats and Reactions

Facebook Reels Scraper By Creator Profile takes a creator or Page URL — no reel links required — and returns every reel it can enumerate from that Page as structured JSON: `videoId`, `title`, `ownerId`, `reelUrl`, `publishedAt`, `duration_string`, `reactionsCount`, `commentsCount`, `sharesCountRounded`, and the video's DASH and progressive MP4 stream URLs. Every reel page it opens also already embeds 5 more reels from the same creator, which the Actor harvests at no extra request. It is built for social teams tracking a creator's reel output, agencies benchmarking client or competitor video activity, and researchers and AI pipelines that need typed Facebook video data without a login. Point it at one Page and start pulling structured rows within the same run.

### What is Facebook Reels Scraper By Creator Profile?

It is an Apify Actor that turns a Facebook creator or Page URL into a catalog of that Page's public reels — video metadata, DASH/MP4 formats, and engagement counts, one JSON row per reel. It also accepts a single reel URL or a bare numeric video ID directly, for when you already know what you want. No Facebook account, login, or cookie is required or supported anywhere in the input schema — every request is anonymous.

- Discovery by creator/Page URL, reels-tab URL, single reel URL, or bare video ID — mixed freely in one input list
- Every reel page yields 5 same-creator sibling reels already embedded in the page, harvested without an extra request
- Reaction, comment and share counts read against each video's own decoded story token, not a neighbouring one
- DASH video/audio representations plus progressive SD/HD MP4 links, switchable by request user agent
- Filters for minimum reactions, reels taken per Page, and a hard cap on total rows returned
- Standard Apify dataset export — JSON, CSV or Excel, or read live through the Apify API

### What data can I extract with Facebook Reels Scraper?

Every reel is one dataset row. Rows discovered as sibling reels without a full fetch carry a lighter subset of the same shape; the table below lists every key the Actor can write to a row.

| Field | Example Value | Use Case |
| ----- | ----- | ----- |
| `videoId` | `"2429400350831482"` | Stable identifier, also used to de-duplicate across siblings and re-runs |
| `id` | `"2429400350831482"` | Same value as `videoId`, kept for yt-dlp-shaped tooling that expects `id` |
| `display_id` | `"2429400350831482"` | Same value again, yt-dlp compatibility field |
| `webpage_url_basename` | `"2429400350831482"` | Same value again, yt-dlp compatibility field |
| `ownerId` | `"20531316728"` | Creator/Page numeric ID, the Actor's own field name |
| `uploader_id` | `"20531316728"` | Same creator ID under the yt-dlp-compatible field name |
| `uploader` | `"National Geographic"` | Creator display name |
| `reelUrl` | `"https://www.facebook.com/reel/2429400350831482"` | Canonical reel link |
| `webpage_url` | `"https://www.facebook.com/20531316728/videos/2429400350831482/"` | Full video permalink under the creator's numeric ID |
| `original_url` | `"https://www.facebook.com/natgeo"` | The exact URL or ID you supplied that led to this row |
| `webpage_url_domain` | `"facebook.com"` | Fixed domain field, yt-dlp compatibility |
| `extractor` / `extractor_key` | `"facebook"` / `"Facebook"` | yt-dlp compatibility fields |
| `sourceType` | `"profile_catalog"` | How the row was found: `input_url`, `profile_catalog`, or `sibling_reel` |
| `discoveredFrom` | `"https://www.facebook.com/natgeo"` | The input URL or reel that produced this row |
| `detailLevel` | `"full"` | `full`, `lightweight` (unexpanded sibling), or `id_only` (catalog listing without detail fetch) |
| `validity` | `"ok"` | Machine-readable outcome — see the validity note below |
| `status` | `"Success"` | Human-readable outcome: `Success`, `Empty`, or `Failed` |
| `charged` | `true` | Whether this row consumed a `row_result` billing event |
| `title` | `"One Sheet Coq Au Vin"` | Reel caption title, with Facebook's own view/reaction-count prefix stripped |
| `fulltitle` | `"One Sheet Coq Au Vin"` | Same as `title`, yt-dlp compatibility field |
| `description` | `"Weeknight dinner, one pan, no fuss."` | Full caption text where Facebook exposes it |
| `thumbnail` | `"https://scontent.xx.fbcdn.net/.../thumb.jpg"` | Preferred thumbnail image URL |
| `thumbnails` | `[{"url": "...", "id": "0"}]` | yt-dlp-shaped thumbnail list, one entry wrapping `thumbnail` |
| `firstFrameThumbnail` | `"https://scontent.xx.fbcdn.net/.../frame0.jpg"` | First-frame thumbnail, when Facebook returns one |
| `duration` | `34.5` | Video duration in seconds |
| `duration_string` | `"0:34"` | Human-readable `h:mm:ss` / `m:ss` duration |
| `timestamp` | `1753420800` | Unix publish time |
| `publishedAt` | `"2026-07-25T00:00:00+00:00"` | ISO 8601 publish time |
| `ageDays` | `1.0` | Days between scrape time and publish time |
| `upload_date` | `"20260725"` | `YYYYMMDD` publish date, yt-dlp compatibility field |
| `release_year` | `null` | Always null — not extractable from this surface |
| `epoch` | `1753507200` | Unix time the row was extracted |
| `reactionsCount` | `72` | Reactions read against this exact video's story token |
| `commentsCount` | `14` | Comment count, same anchoring |
| `sharesCountRounded` | `"12"` | Facebook's own rounded share count string |
| `storyId` | `"S:_I20531316728:VK:2429400350831482"` | Decoded story token the engagement counts were anchored to |
| `feedbackId` | `"1234567890123456"` | Facebook's internal feedback/comment-thread ID for the video |
| `engagementAnchor` | `"story_token"` | `story_token` when reactions resolved against the anchor, `unresolved` when they did not |
| `siblingReelIds` | `["2431...", "2432...", "2433...", "2434...", "2435..."]` | Video IDs of the sibling reels found on this page |
| `siblingReelCount` | `5` | Count of sibling reels found on this page |
| `captionLocales` | `["en_US"]` | Available caption/subtitle locales, when Facebook lists any |
| `automatic_captions` / `subtitles` / `requested_subtitles` | `{}` / `{}` / `null` | Always empty — no caption tracks are extractable from this surface |
| `formats` | `[ { "format_id": "0", "ext": "m4a", ... }, ... ]` | Every DASH audio/video representation plus progressive SD/HD links — see the Output sample below |
| `requested_formats` | `[ {video format}, {audio format} ]` | The one video + one audio format the Actor picked as best, present only when DASH was resolved |
| `format` | `"249 - 720x1280 (DASH video)+251 - audio only (DASH audio)"` | Combined format label, present only when `requested_formats` is present |
| `format_id` | `"249+251"` | Combined format ID, same condition |
| `ext` | `"mp4"` | Container extension, same condition |
| `protocol` | `"https+https"` | Delivery protocol, same condition |
| `language` | `null` | Always null, same condition |
| `format_note` | `"DASH video+DASH audio"` | Same condition |
| `filesize_approx` | `142857` | Estimated combined byte size from bitrate × duration, same condition |
| `tbr` / `vbr` / `abr` | `1024.0` / `896.0` / `128.0` | Combined, video and audio bitrate in kbps, same condition |
| `width` / `height` | `720` / `1280` | Best video representation's pixel dimensions, same condition |
| `resolution` | `"720x1280"` | `WxH` string, same condition |
| `fps` | `30.0` | Frame rate read from the DASH manifest, same condition |
| `dynamic_range` | `"SDR"` | Same condition |
| `vcodec` / `acodec` | `"avc1.64001f"` / `"mp4a.40.2"` | Codec strings, same condition |
| `asr` | `44100` | Audio sample rate, same condition |
| `audio_channels` | `2` | Same condition |
| `aspect_ratio` | `0.56` | Width/height ratio, same condition |
| `stretched_ratio` | `null` | Always null, same condition |
| `_has_drm` | `null` | Always null — not evaluated |
| `playlist` / `playlist_index` | `null` / `null` | Always null — a single reel is never treated as a playlist |
| `pageBytes` | `1867432` | Raw HTML response size in bytes, on rows that fetched a full page |
| `scrapedAt` | `"2026-07-26T09:00:00+00:00"` | ISO 8601 timestamp the row was pushed |
| `error` | `"ClientError: 403"` | Present only on `status: "Failed"` rows |
| `url` | `"https://www.facebook.com/badpage"` | Present only on `Failed`/unrecognised-input rows, in place of `webpage_url` |

> **Fields are omitted, not nulled, when they don't apply.** The 22 flattened format-summary fields (`requested_formats` through `aspect_ratio` above) exist on a row **only** when the Actor resolved a usable video+audio pair — on a row with no media, those keys are absent from the JSON entirely, not present as `null`. The same is true of `detailLevel`: two row shapes — a Page fetch that failed outright, and a Page that returned zero reels — omit the `detailLevel` key rather than writing it as null. And `url` only ever appears on `Failed` or unrecognised-input rows; every successful row carries the location under `webpage_url` / `original_url` / `reelUrl` instead, with no `url` key at all.

#### Engagement and sibling-reel fields

`reactionsCount`, `commentsCount` and `sharesCountRounded` are the decision-driving numbers — Facebook renders these blocks 6 times per reel page (once for the requested reel, once for each of its 5 siblings) in server-randomised order, so the Actor binds each block to the nearest preceding decoded `storyId` token rather than reading the first match. `engagementAnchor` tells you whether that anchor resolved (`story_token`) or not (`unresolved`). `siblingReelIds` and `siblingReelCount` carry the same-creator reels harvested from that page for free — filter or expand on these to build a fuller picture of a creator's catalog without additional requests.

#### Discovery, status and quality fields

`sourceType`, `discoveredFrom` and `detailLevel` tell you how a row was found and how much of it was actually fetched — useful when mixing `input_url`, `profile_catalog` and `sibling_reel` rows in one dataset. `validity` and `status` are the machine- and human-readable outcome codes (`ok`, `unavailable_or_not_a_reel`, `no_media_returned`, `fetch_failed`, `no_reels_on_page`, `unrecognised_input`, `no_engagement_anchor`, among others), and `charged` tells you whether the row consumed a billing event — filter on `charged: true` (equivalently `validity: "ok"` for full-detail rows) to keep only billed rows.

### How does Facebook Reels Scraper differ from the official Facebook API?

Facebook's Graph API has no endpoint that lists a Page's reels for anonymous or app-level callers, and nothing resembling the sibling-reel graph this Actor reads off a single reel page — so there is no official API comparison to make here. The realistic alternative is building the extraction yourself.

#### Why not build this yourself?

A DIY version has to solve three problems this Actor already solves. First, Facebook renders the same engagement counters (`unified_reactors`, `total_comment_count`, `share_count_reduced`) up to 6 times per reel page — once per sibling — in randomised order, so a naive first-match read silently returns the wrong video's numbers; the fix is decoding each `UzpfS...` story token and binding every counter block to its nearest preceding token. Second, discovering a Page's reels means enumerating the `/<page>/reels/` surface and reconciling two independent ID sources that don't always agree. Third, video delivery means parsing a DASH XML manifest for adaptive representations and falling back to Facebook's progressive `browser_native_sd_url` / `browser_native_hd_url` fields when DASH isn't present — plus handling the Chrome-vs-crawler user-agent difference that changes which of those exist at all. None of this is exposed through any public Facebook API; it all has to be read out of the page payload directly.

Build it yourself if you need to control every byte of the request pipeline and already have proxy and anti-bot infrastructure in place. Use Facebook Reels Scraper if you want a creator's reel catalog as typed rows without maintaining that pipeline.

### How to use data extracted from Facebook Reels Scraper

#### 📱 Social and content teams

Point `startUrls` at a creator or Page, set `maxReelsPerProfile` to the depth you need, and pull `title`, `publishedAt`, `duration_string`, `reactionsCount`, `commentsCount` and `sharesCountRounded` straight into a content calendar or performance sheet — sorted by `ageDays` for recency or `reactionsCount` for what's actually landing with the audience.

#### 🏢 Agencies benchmarking creators

Running the same Page list on a schedule and diffing `reactionsCount` / `commentsCount` / `sharesCountRounded` per `videoId` across runs turns this into a repeatable competitor or client benchmark, without re-collecting reel URLs by hand each time — `discoveredFrom` keeps every row traceable back to the Page that produced it.

#### 🔬 Market and network research

`siblingReelIds` and `siblingReelCount` expose which reels Facebook itself groups with a given video, on the same creator — useful for mapping a creator's most-connected content or spot-checking catalog completeness, since `expandSiblingReels` surfaces this network at no extra request cost.

#### 🤖 AI agents and automated pipelines

Because every row is typed JSON keyed the same way on every run, a pipeline can index `title`, `description` and `reactionsCount` into a vector store or feed an agent tool that answers "what is this creator's best-performing reel this month" against live rows rather than a stale export.

### ⬇️ Input

Required per schema: none.

| Parameter | Required | Type | Description | Example Value |
| ----- | ----- | ----- | ----- | ----- |
| `startUrls` | No | array | Creator/Page URL, reels-tab URL, single reel URL, or bare video ID — mix freely. Page URLs are enumerated via the public reels surface; reel URLs are scraped directly. | `["https://www.facebook.com/natgeo", "https://www.facebook.com/reel/2429400350831482"]` |
| `urls` | No | array | Legacy/back-compat input list. Scraped **in addition to** `startUrls`, accepting the same URL shapes. Leave empty if you're using `startUrls`. | `[]` |
| `maxReelsPerProfile` | No | integer | Cap on reel IDs taken from each creator/Page URL. Default `10`, minimum `0`, maximum `500`. `0` means no cap. | `10` |
| `fetchProfileReelDetails` | No | boolean | On (default): each discovered reel is fetched for formats, duration, thumbnail and engagement (1 request per reel). Off: rows carry only the reel ID and URL, with no extra requests. | `true` |
| `expandSiblingReels` | No | boolean | On (default): harvests the 5 same-creator sibling reels every reel page already embeds, at no extra request. | `true` |
| `fetchFullSiblingRows` | No | boolean | Off (default): sibling rows carry ID, creator ID, URL and engagement counts only, free. On: each sibling is fetched too for formats/duration/thumbnail — 1 extra request per sibling. | `false` |
| `maxItems` | No | integer | Hard stop across the whole run — input reels, discovered reels and siblings combined, counted after video-ID de-duplication. Default `0` (unlimited), minimum `0`, maximum `100000`. | `0` |
| `minReactions` | No | integer | Drops rows below this reaction total. Rows whose reaction count could not be resolved are dropped whenever this is above `0`. Default `0`, minimum `0`, maximum `100000000`. | `0` |
| `requestUserAgent` | No | string | `"chrome"` (default) returns DASH representations plus progressive MP4; `"facebookexternalhit"` returns progressive MP4 only, no DASH manifest. | `"chrome"` |
| `concurrency` | No | integer | How many Facebook pages are fetched in parallel. Default `5`, minimum `1`, maximum `10`. | `5` |
| `proxyConfiguration` | No | object | Apify Proxy (residential or datacenter) or your own proxy URLs. Left disabled by default for a direct connection; the Actor retries on a residential IP if a request looks blocked. | `{"useApifyProxy": false}` |

#### Example input

```json
{
  "startUrls": [
    "https://www.facebook.com/natgeo",
    "https://www.facebook.com/reel/2429400350831482"
  ],
  "maxReelsPerProfile": 10,
  "fetchProfileReelDetails": true,
  "expandSiblingReels": true,
  "fetchFullSiblingRows": false,
  "maxItems": 100,
  "minReactions": 0,
  "requestUserAgent": "chrome",
  "concurrency": 5,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

**⚠️ Common pitfall:** `urls` is not a replacement for `startUrls` — it is an additive legacy list. Putting the same Page in both fields doesn't duplicate work (targets are de-duplicated by exact string match before the run starts), but leaving old integration input in `urls` while also filling `startUrls` is fine and expected; you don't need to migrate it.

### ⬆️ Output

Typed, normalized JSON, one row per reel (or per lightweight sibling), with a consistent field set for a given `detailLevel`. Export as JSON, CSV or Excel from the Apify Console, or read the dataset through the Apify API.

#### Example output — full-detail reel row

```json
{
  "videoId": "2429400350831482",
  "id": "2429400350831482",
  "display_id": "2429400350831482",
  "webpage_url_basename": "2429400350831482",
  "ownerId": "20531316728",
  "uploader_id": "20531316728",
  "uploader": "National Geographic",
  "reelUrl": "https://www.facebook.com/reel/2429400350831482",
  "webpage_url": "https://www.facebook.com/20531316728/videos/2429400350831482/",
  "original_url": "https://www.facebook.com/natgeo",
  "webpage_url_domain": "facebook.com",
  "extractor": "facebook",
  "extractor_key": "Facebook",
  "sourceType": "profile_catalog",
  "discoveredFrom": "https://www.facebook.com/natgeo",
  "detailLevel": "full",
  "validity": "ok",
  "status": "Success",
  "charged": true,
  "title": "One Sheet Coq Au Vin",
  "fulltitle": "One Sheet Coq Au Vin",
  "description": "Weeknight dinner, one pan, no fuss.",
  "thumbnail": "https://scontent.xx.fbcdn.net/v/one-sheet-coq-au-vin/thumb.jpg",
  "thumbnails": [{ "url": "https://scontent.xx.fbcdn.net/v/one-sheet-coq-au-vin/thumb.jpg", "id": "0" }],
  "firstFrameThumbnail": "https://scontent.xx.fbcdn.net/v/one-sheet-coq-au-vin/frame0.jpg",
  "duration": 34.5,
  "duration_string": "0:34",
  "timestamp": 1753420800,
  "publishedAt": "2026-07-25T00:00:00+00:00",
  "ageDays": 1.0,
  "upload_date": "20260725",
  "release_year": null,
  "epoch": 1753507200,
  "reactionsCount": 72,
  "commentsCount": 14,
  "sharesCountRounded": "12",
  "storyId": "S:_I20531316728:VK:2429400350831482",
  "feedbackId": "1234567890123456",
  "engagementAnchor": "story_token",
  "siblingReelIds": ["2431011122334455", "2432022233445566", "2433033344556677", "2434044455667788", "2435055566778899"],
  "siblingReelCount": 5,
  "captionLocales": ["en_US"],
  "automatic_captions": {},
  "subtitles": {},
  "requested_subtitles": null,
  "formats": [
    {
      "format_id": "0",
      "manifest_url": "https://video.xx.fbcdn.net/v/manifest.mpd",
      "ext": "m4a",
      "width": null,
      "height": null,
      "tbr": 128.0,
      "asr": 44100,
      "fps": null,
      "format_note": "DASH audio",
      "container": "m4a_dash",
      "vcodec": "none",
      "acodec": "mp4a.40.2",
      "resolution": "audio only",
      "format": "0 - audio only (DASH audio)"
    },
    {
      "format_id": "sd",
      "quality": -3,
      "url": "https://video.xx.fbcdn.net/v/sd.mp4?bitrate=512000",
      "ext": "mp4",
      "resolution": null,
      "format": "sd - unknown"
    },
    {
      "format_id": "249",
      "manifest_url": "https://video.xx.fbcdn.net/v/manifest.mpd",
      "ext": "mp4",
      "width": 720,
      "height": 1280,
      "tbr": 1024.0,
      "fps": 30.0,
      "format_note": "DASH video",
      "container": "mp4_dash",
      "vcodec": "avc1.64001f",
      "acodec": "none",
      "resolution": "720x1280",
      "aspect_ratio": 0.56,
      "format": "249 - 720x1280 (DASH video)"
    }
  ],
  "requested_formats": ["<the DASH video object above>", "<the DASH audio object above>"],
  "format": "249 - 720x1280 (DASH video)+0 - audio only (DASH audio)",
  "format_id": "249+0",
  "ext": "mp4",
  "protocol": "https+https",
  "language": null,
  "format_note": "DASH video+DASH audio",
  "filesize_approx": 142857,
  "tbr": 1152.0,
  "vbr": 1024.0,
  "abr": 128.0,
  "width": 720,
  "height": 1280,
  "resolution": "720x1280",
  "fps": 30.0,
  "dynamic_range": "SDR",
  "vcodec": "avc1.64001f",
  "acodec": "mp4a.40.2",
  "asr": 44100,
  "audio_channels": 2,
  "aspect_ratio": 0.56,
  "stretched_ratio": null,
  "_has_drm": null,
  "playlist": null,
  "playlist_index": null,
  "pageBytes": 1867432,
  "scrapedAt": "2026-07-26T09:00:00+00:00"
}
```

`formats` carries three shapes side by side: **DASH audio** representations (`format_note: "DASH audio"`, `container: "m4a_dash"`, `vcodec: "none"`), **DASH video** representations (`format_note: "DASH video"`, `container: "mp4_dash"`, `acodec: "none"`), and **progressive** `sd`/`hd` MP4 links (no `container`/`manifest_url`, a direct playable `url`, `dynamic_range: "SDR"`). `requestUserAgent: "facebookexternalhit"` returns only the progressive entries — no DASH manifest, so no `formats` entries carry `container: "mp4_dash"` or `"m4a_dash"`, and the flattened `requested_formats`/`format`/`resolution`/etc. fields fall back accordingly or are omitted if nothing resolves at all.

### How do you filter and target specific reels?

`startUrls` accepts four shapes and you can mix them in one list: a creator/Page URL (`https://www.facebook.com/natgeo`), a reels-tab URL (`https://www.facebook.com/nba/reels/`), a single reel URL (`https://www.facebook.com/reel/2429400350831482`), or a bare video ID (`2429400350831482`). Page URLs go through catalog discovery; reel URLs and IDs are scraped directly — use a Page URL when you want a creator's output over time, and a reel URL/ID when you already know the exact video.

`maxReelsPerProfile` controls discovery depth per Page — set it to `0` for no cap, or leave it at the default `10` for a quick sample. `expandSiblingReels` (on by default) adds the 5 same-creator reels embedded on every reel page at no extra request, and `fetchFullSiblingRows` decides whether those siblings stay lightweight (ID, creator ID, URL, engagement) or get upgraded to full detail at 1 extra request each. `minReactions` is a quality threshold read against each video's own decoded story token — not a neighbouring one — so it never filters on the wrong video's number, and any row whose reaction count can't be resolved is dropped once this is above `0`. `maxItems` is the volume control: a single global cap across input reels, discovered reels and siblings combined, applied after de-duplication by `videoId`, so duplicates never eat into your quota.

Three real examples:

```json
{ "startUrls": ["https://www.facebook.com/BuzzFeedTasty"], "maxReelsPerProfile": 25, "expandSiblingReels": false, "maxItems": 25 }
```

A capped, sibling-free catalog pull — exactly the Page's own reels, nothing borrowed from neighbours.

```json
{ "startUrls": ["https://www.facebook.com/reel/2429400350831482"], "expandSiblingReels": true, "fetchFullSiblingRows": true, "minReactions": 50 }
```

One reel expanded into its full sibling network, all fully fetched, with low-engagement rows dropped.

```json
{ "startUrls": ["https://www.facebook.com/LADbible", "https://www.facebook.com/nba"], "fetchProfileReelDetails": false, "maxItems": 200 }
```

Cheap ID-only catalog listing across two Pages, capped at 200 total rows, with no per-reel detail fetch.

### ▶️ Want to try other Facebook scrapers?

| Scraper Name | What it extracts |
| ----- | ----- |
| Facebook Video Search Scraper - Description & Hashtags | Public Facebook Watch videos by keyword, with caption text and structured hashtag entities |
| Facebook Hashtag Search Scraper — Top Posts & Engagement Ranking | Hashtag posts ranked by engagement score and rate |
| Facebook Search Scraper By Hashtag | Pages posting under a hashtag, with matching post text and full Page profile data |
| Facebook Pages Scraper With Ad Library Activity | Public Page data plus Ad Library activity and individual ad creatives |
| Facebook Page Content Strategy Scraper | Per-post content type, hashtags, mentions and a run-level content-mix summary |
| Facebook Groups Search Scraper — Member Size & Activity Metrics | Facebook Groups ranked by member size and activity |
| Instagram Single Reels Scraper | A single Instagram Reel's caption, engagement counts, video/thumbnail URLs and music metadata |
| TikTok Video Scraper - Hashtag & Sound Reach | TikTok videos enriched with hashtag reach and sound-usage stats |

### How to extract Facebook video data programmatically

Facebook Reels Scraper is a standard Apify Actor run — one API call, one auth header, structured JSON back in the dataset.

#### Python example

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("<YOUR_USERNAME>/facebook-videos-scraper-by-creator-profile").call(run_input={
    "startUrls": ["https://www.facebook.com/natgeo"],
    "maxReelsPerProfile": 20,
    "minReactions": 25,
    "maxItems": 100,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("charged"):
        print(row["videoId"], row["reactionsCount"], row.get("resolution"))
```

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request.

#### Export to spreadsheets or CRM

Export the dataset as CSV directly from the Apify Console and import it, mapping `title` and `uploader` to name columns, `reactionsCount`/`commentsCount`/`sharesCountRounded` to engagement columns, and `webpage_url`/`reelUrl` to a link column.

### Is it legal to scrape Facebook reels?

Yes — Facebook Reels Scraper reads only public reel and Page content, the same video, caption and engagement counts already visible to any anonymous visitor on the reel page. No login, cookie or authenticated session is used or accepted anywhere in the input.

This output is business/content data — public video posts and their publicly displayed engagement counts, not private profile information — so the framework that applies is Facebook's Terms of Service and database-rights rules around systematic reuse of a compiled catalogue, not personal-data regimes like GDPR/CCPA. Consult legal counsel for commercial applications involving bulk storage of this data.

### ❓ Frequently asked questions

#### What if a Page publishes no reels?

You get a real zero, not an invented row or a silent failure. The catalog-discovery step reports the actual reel-ID count it found on the Page — including `0` — and writes a single uncharged row with `validity: "no_reels_on_page"` and `status: "Empty"` rather than fabricating results.

#### Can I get sibling reels along with the main reel I requested?

Yes. `expandSiblingReels` is on by default and harvests up to 5 same-creator sibling reels already embedded on the reel page you fetched, at no extra request. Set `fetchFullSiblingRows` to `true` if you also want each sibling's formats, duration and thumbnail — that costs 1 extra request per sibling.

#### How accurate are the reaction, comment and share counts?

The Actor returns the counts exactly as Facebook renders them on the reel page at request time, anchored to that specific video's decoded story token rather than a neighbouring one in the same page. Accuracy depends on how current Facebook's own counters are at the moment of the request; re-run the target reel later to see updated counts.

#### How many reels can I get per run?

`maxItems` is the hard stop — a single cap across input reels, discovered reels and sibling reels combined, applied after de-duplication by `videoId`. Default is `0` (unlimited), maximum is `100000`. Separately, `maxReelsPerProfile` (default `10`, maximum `500`, `0` for no cap) limits how many reel IDs are taken from each individual Page before those per-reel caps and filters apply.

#### What does `minReactions` actually filter on?

It drops any row whose reaction count is below the threshold, but the filter reads against that exact video's own decoded story token rather than the first reaction count it finds on the page — Facebook repeats the same counter block up to 6 times per reel page (once per sibling), in randomised order, so a naive read would sometimes filter on the wrong video's number. Rows whose reaction count can't be resolved at all are dropped once this filter is above `0`.

#### Does Facebook Reels Scraper work with Claude, ChatGPT and AI agent frameworks?

Yes. It is callable as a standard HTTP-triggered Apify Actor run through the Apify API, so LangChain, CrewAI, n8n or a hand-written tool definition can invoke it and receive typed JSON with no parsing step.

#### How does Facebook Reels Scraper compare to other Facebook scrapers?

Checked on the Apify Store on 2026-07-26: `curious_coder/facebook-profile-scraper` and `igolaizola/facebook-profile-scraper` both target full profile/page data — bio, contact info, ratings, or posts pulled with your own session cookies — rather than a reel catalog specifically; igolaizola's Actor documents requiring exported Facebook cookies for its scrape. `cleansyntax/facebook-profile-posts-scraper` documents a profile-posts API with pagination and retry logic, returning post objects with media links but not DASH manifests or the sibling-reel graph. None of the three document harvesting sibling reels or anchoring engagement counts to a specific video's story token, which is what this Actor is built around.

#### Can I use Facebook Reels Scraper without a Facebook account or login?

Yes. No account, cookie, session or API key for Facebook is required or accepted anywhere in the input schema. The only credential you need is your Apify token.

### Conclusion

Facebook Reels Scraper By Creator Profile turns a single Page URL into a structured catalog of that creator's public reels — video metadata, DASH/MP4 formats, and reaction/comment/share counts anchored to the right video every time — plus the sibling reels each page already carries, for free. It is built for social teams, agencies, researchers and AI pipelines that need that data as typed rows rather than a scraping project of their own. Open Facebook Reels Scraper on Apify and start a run with a Page URL to see it working.

# Actor input Schema

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

🧭 Mix freely: a creator/page URL (https://www.facebook.com/natgeo), a reels tab (https://www.facebook.com/nba/reels/), a single reel (https://www.facebook.com/reel/2429400350831482), or a bare video ID (2429400350831482). Page URLs are enumerated via the public reels surface; reel URLs are scraped directly. Example: one page URL + maxReelsPerProfile=10 → up to 10 reels.

## `urls` (type: `array`):

♻️ Backward-compatible input for existing runs and integrations. Anything placed here is scraped in addition to 🎯 above and accepts the same URL shapes. Leave empty if you are using 🎯.

## `maxReelsPerProfile` (type: `integer`):

🔢 Cap on how many reel IDs are taken from each creator/page URL. Measured public yield is about 10 reel IDs per page fetch on active pages (natgeo, BuzzFeedTasty, nba, LADbible) and 0 on pages that publish no reels. Set 0 for no cap. Default is 10.

## `fetchProfileReelDetails` (type: `boolean`):

✅ On: each discovered reel ID is fetched so the row carries formats, duration, thumbnail and engagement (1 request per reel). ❌ Off: rows contain the reel ID and URL only, with no extra requests — useful for cheap catalog listing. Default is on.

## `expandSiblingReels` (type: `boolean`):

♻️ Every reel page already embeds 5 more reels from the same creator, which the original scraper downloaded and threw away. Measured: exactly 5 siblings on every page tested, same creator only — this does not crawl a whole account. Example: 1 reel URL → about 6 rows for 1 request. Default is on.

## `fetchFullSiblingRows` (type: `boolean`):

📥 Off (default): sibling rows are free and carry ID, creator ID, URL and engagement counts. On: each sibling is fetched too so it gets formats, duration and thumbnail — costs 1 extra request per sibling (about 5 per input reel).

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

🛑 Hard stop across the whole run, counting input reels, siblings and discovered reels together. Rows are deduplicated by video ID first. Example: 2 page URLs + maxItems=25 → the run stops at 25 rows. Set 0 for unlimited. Default is 0.

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

📈 Drop videos below this reaction total. Reactions are read against the video's own decoded story token, so the number belongs to that video and not to a neighbouring one. Rows whose reaction count could not be resolved are dropped when this is above 0. Set 0 to keep everything. Default is 0.

## `requestUserAgent` (type: `string`):

🧪 Chrome returns richer pages (DASH representations plus progressive MP4). The crawler user agent returns progressive MP4 only and no DASH manifest. Change this only if you are debugging. Default is Chrome.

## `concurrency` (type: `integer`):

🚀 How many Facebook pages are fetched at the same time. Lower this if you start seeing blocks. Example: concurrency=5 with a 10-reel page → 2 waves. Default is 5.

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

🛡️ Optional. Use Apify Proxy (residential or datacenter) or your own proxy URLs. If a request looks blocked the actor retries on a residential IP before the first row for that target is written. Leave disabled for a direct connection.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.facebook.com/natgeo",
    "https://www.facebook.com/reel/2429400350831482"
  ],
  "maxReelsPerProfile": 10,
  "fetchProfileReelDetails": true,
  "expandSiblingReels": true,
  "fetchFullSiblingRows": false,
  "maxItems": 0,
  "minReactions": 0,
  "requestUserAgent": "chrome",
  "concurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped items in the Actor's default dataset.

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

A single JSON record written once the run finishes: totals requested/pushed/charged/filtered, per-profile reel-catalog yield, per-reel sibling yield, and a per-target status map.

# 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": [
        "https://www.facebook.com/natgeo",
        "https://www.facebook.com/reel/2429400350831482"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("simpleapi/facebook-videos-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 = {
    "startUrls": [
        "https://www.facebook.com/natgeo",
        "https://www.facebook.com/reel/2429400350831482",
    ],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("simpleapi/facebook-videos-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 '{
  "startUrls": [
    "https://www.facebook.com/natgeo",
    "https://www.facebook.com/reel/2429400350831482"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call simpleapi/facebook-videos-scraper --silent --output-dataset

```

## MCP server setup

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