Facebook Videos Scraper With Reactions & Comment Counts avatar

Facebook Videos Scraper With Reactions & Comment Counts

Pricing

from $1.55 / 1,000 results

Go to Apify Store
Facebook Videos Scraper With Reactions & Comment Counts

Facebook Videos Scraper With Reactions & Comment Counts

Facebook Videos Scraper extracts public video data including titles, descriptions, creators, views, reactions, comment counts, shares, timestamps, and video URLs. Build structured datasets for content research, engagement analysis, trend tracking, and competitor monitoring.

Pricing

from $1.55 / 1,000 results

Rating

0.0

(0)

Developer

Scrapio

Scrapio

Maintained by Community

Actor stats

0

Bookmarked

7

Total users

0

Monthly active users

19 hours ago

Last modified

Share

Facebook Reels Scraper — Extract Reactions, Comments and Share Counts

Facebook Videos Scraper With Reactions & Comment Counts turns a list of public Facebook Reel URLs into typed JSON rows — reaction total, comment count and rounded share count, each one bound to the Reel's own decoded parent story id, plus the full video record (duration, thumbnail, publish date, downloadable SD/HD and DASH format URLs). No login, no cookies, no browser. A Reel that Facebook can't actually serve data for is flagged status: "Unavailable" and never charged. This guide covers every input field, every output key, and how teams run it in a real pipeline.

What Does Facebook Videos Scraper With Reactions & Comment Counts Do?

It fetches each Facebook Reel URL you supply as a logged-out request, reads the reaction total, comment count and rounded share count that Facebook embeds in the page for that specific Reel, and writes one dataset row per URL. No Facebook account, login or cookie is required — every field returned is already visible to an anonymous visitor. The Actor also decodes and extracts the Reel's technical video record: title, uploader id, duration, thumbnail, publish timestamp, and the DASH/progressive format URLs Facebook serves for playback.

  • Anchored reaction, comment and share counts for a single requested Reel — not a mix of it and its neighbours
  • Dead, deleted or non-Reel URLs are detected and flagged rather than silently returning wrong or borrowed data
  • Optional evidence columns showing what a naive (first-match) scraper would have reported, so you can audit the anchoring
  • Threshold filters (minReactions, minComments) that skip low-engagement Reels before they're written or billed
  • Parallel fetching across up to 10 concurrent requests
  • Downloadable video/audio format URLs (DASH manifests, progressive SD/HD MP4) alongside the engagement data
  • A legacy urls input alias so JSON built for the original Facebook Videos Scraper runs here unchanged

Features & Capabilities

Facebook Videos Scraper With Reactions & Comment Counts is built around one problem competitors don't solve: a Facebook Reel page embeds several engagement blocks, not one, and Facebook does not guarantee which block belongs to which video.

Core features

  • Story-anchored engagement — every unified_reactors, total_comment_count and share_count_reduced block on the page is bound to the nearest preceding base64 story token (S:_I<ownerId>:VK:<videoId>), decoded and matched against the requested video id. Only the block that matches is emitted as reactionsCount, commentsCount and sharesCountRounded.
  • Reaction fallback — if no unified_reactors block anchors to the requested video, the Actor falls back to the page's likers count before giving up, recorded internally as _likers_anchored.
  • Dead-URL detection without a false "Success" — Facebook answers a dead Reel id, or a /videos/<id>/ or /watch/?v= URL, with an HTTP 200 page of roughly 450–500 KB that looks like a normal response but carries zero playable media keys. The Actor treats the presence of dash_manifest_xml_string, browser_native_sd_url or browser_native_hd_url as the only reliable signal of a real video document, and marks anything else Unavailable.
  • Anchor evidence for auditinganchorStoryId, anchorFeedbackId, engagementBlocksOnPage, naiveFirstMatchReactions and naiveFirstMatchWasWrong let you verify, per row, that the number you got is the anchored one and not a first-match guess.
  • Full video recordtitle, description, uploader_id, ownerId, thumbnail, duration, duration_string, publishedAt, ageDays, plus a formats array of DASH audio/video representations and progressive SD/HD download URLs with codec, resolution, fps and bitrate detail.
  • Engagement threshold filtersminReactions and minComments drop underperforming Reels before a row is written, so you never pay for results below your bar.
  • Uncharged accounting for every failure mode — dead URLs, fetch errors and filtered Reels are all excluded from billing; only status: "Success" rows use the row_result charged event.

How Facebook Videos Scraper With Reactions & Comment Counts compares to other Facebook scrapers

Checked on the Apify Store on 26 July 2026. Both comparison actors publish READMEs; features marked "not documented" simply weren't described there, not confirmed absent.

FeatureFacebook Videos Scraper With Reactions & Comment Countswhoareyouanas/facebook-group-scraperapify/facebook-comments-scraper
Target surfacePublic Facebook ReelsFacebook groups and Pages (posts)Any Facebook post, photo, Reel or video (comments only)
Reaction total anchored to one videoPartial — full 6-reaction breakdown, on posts, not Reels❌ not returned
Comment count✅ (individual comment records, not a total)
Share count✅ (rounded, as Facebook displays it)❌ not returned
Protection against a sibling video's numbers landing on your row✅ story-id anchoredNot documentedNot documented
Downloadable video/audio format URLs✅ SD/HD + DASHNot documentedNot documented
Login or cookies requiredNo — logged out by designOnly for private groupsNot documented
Dead/unavailable URL handling✅ flagged Unavailable, unchargedNot documentedNot documented
Pricing modelPay per event (row_result)Pay per eventPay per event

If your use case is feeding structured data to an LLM or a monitoring pipeline, the anchoring row is the decision-maker — a scraper that returns the first engagement block it finds on the page will silently attribute a sibling Reel's numbers to yours on a random share of runs, and there is no way to detect that from the output alone unless the tool exposes evidence fields the way this one does.

When another tool might suit you better

If you need Facebook group or Page posts — text, images, top comments with author names — rather than Reel engagement, whoareyouanas/facebook-group-scraper covers both groups and Pages from one input and returns a full six-reaction breakdown per post, which this Actor does not attempt (Reels only expose a single reaction total, not a per-type split). If you specifically need comment threads with replies and commenter profile links rather than a comment count, apify/facebook-comments-scraper is built for that and works across posts, photos, Reels and videos. This Actor's scope is narrower and deeper: Reel engagement totals, anchored correctly, plus the underlying video record.

Facebook Videos Scraper With Reactions & Comment Counts within the Scrapio data stack

Facebook Videos Scraper With Reactions & Comment Counts is currently Scrapio's only Facebook Actor, and it covers Reel engagement exclusively. If your workflow also needs Facebook group posts, Page posts, or comment threads with replies, those surfaces are not covered by this Actor and would need a separate tool — see the Related Facebook Scrapers & Tools section below for the closest publicly available options.

Why do developers and data teams scrape Facebook Reels?

  • 🏢 Social media and marketing teams monitor how their own or a competitor's Reels perform over time — pulling reactionsCount, commentsCount and sharesCountRounded for a batch of Reel URLs weekly and charting the trend per videoId, without touching Facebook's ad tools or a business account.
  • 📊 AI training data and RAG indexingtitle and description are the high-information text fields for embedding into a vector store or fine-tuning corpus; reactionsCount, commentsCount, sharesCountRounded and engagementAnchored are consistently typed across every row, so they drop into a training set without a normalization pass. RAG use: index Reel titles and descriptions so an agent can answer "which of our Reels got the most comments this month" against live data. Training use: the engagement counts make a clean structured-label column for models predicting content performance.
  • 📱 Competitive and market intelligence — track a competitor's Reel output by anchoring runs to their known video ids, watching reactionsCount and commentsCount move week over week to spot which content formats are gaining traction before it shows up in a market report.
  • 🔬 Research and academic use — public-data-only datasets on short-form video engagement, built by batching Reel URLs collected from a public sample and exporting the resulting rows for statistical analysis. No private or gated content is accessible.
  • 🎥 Product and SaaS development — creator-analytics dashboards, engagement-monitoring tools, or content-performance APIs built on top of reactionsCount, commentsCount, sharesCountRounded and the video metadata fields, refreshed on a schedule rather than re-implemented against Facebook's own markup.

🍚 Input Parameters

All parameters are optional at the schema level — supplying either videoUrls or the legacy urls field is what makes a run useful. Read directly from .actor/actor.json, in schema order.

ParameterRequiredTypeDescriptionExample Value
videoUrlsNoarrayOne Reel URL per line, e.g. https://www.facebook.com/reel/2429400350831482 — the only URL shape that carries real video data logged-out. A /<page>/videos/<id>/ or /watch/?v= link is accepted, but Facebook answers those with a recommendation feed holding nothing about the requested video: such rows come back status: "Unavailable" and are not charged.["https://www.facebook.com/reel/2429400350831482"]
minReactionsNointegerSkip (and do not charge for) any Reel whose anchored reaction total is below this number. 0 = keep everything. Minimum 0, maximum 100000000, default 0.1000
minCommentsNointegerSkip (and do not charge for) any Reel whose anchored comment count is below this number. 0 = keep everything. Minimum 0, maximum 100000000, default 0.50
includeAnchorEvidenceNobooleanAdds the audit trail for every engagement number: anchorStoryId, anchorFeedbackId, engagementBlocksOnPage, and naiveFirstMatchReactions — the value a first-match scraper would have reported from the same HTML. Turn off for a leaner dataset. Default true.true
includeUnavailableRowsNobooleanWhen a URL yields no playable video, still write an uncharged row with status: "Unavailable" and a reason, so you can see exactly which inputs failed. Set to false to drop them entirely. Either way they are never billed. Default true.true
maxConcurrencyNointegerHow many Reel pages to fetch at once. Values outside 1–10 are clamped and the coercion is logged. Minimum 1, maximum 10, default 3.5
urlsNoarrayBackward-compatible alias for videoUrls. If this is filled it takes precedence, so an input JSON written for the original Actor runs here unchanged. Leave empty otherwise.[]
proxyConfigurationNoobjectApify Proxy (residential or datacenter) or your own proxy URLs. Reel pages resolve without a proxy on most runs; the Actor switches to residential automatically if a fetch looks blocked. Proxy retries happen before any row is written, so a retry can never duplicate a row.{"useApifyProxy": false}

If both urls and videoUrls are filled, urls wins — this exists so input JSON written for the actor this one is based on keeps working unchanged, since Apify prefills schema defaults into every run and a naive "prefer the new field" check would never fall through to the legacy one.

Example input

{
"videoUrls": [
"https://www.facebook.com/reel/2429400350831482",
"https://www.facebook.com/reel/2430902544051423"
],
"minReactions": 100,
"minComments": 0,
"includeAnchorEvidence": true,
"includeUnavailableRows": true,
"maxConcurrency": 5,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}

Supported URL types and input formats

URL shapeResult
https://www.facebook.com/reel/<id>Full row — video metadata and anchored engagement
https://www.facebook.com/<page>/videos/<id>/Accepted, but Facebook serves a recommendation feed with no data on the requested id — row comes back status: "Unavailable", reason feed_fallback_no_video_data, not charged
https://www.facebook.com/watch/?v=<id>Same as above — accepted, status: "Unavailable", not charged

Only the /reel/<id> shape carries real video data on a logged-out request. This is a constraint of what Facebook itself serves to an anonymous visitor on the other two URL shapes, not a limitation the Actor imposes — and it's the reason includeUnavailableRows exists: so you can see which of your input URLs need to be re-collected as Reel links rather than page-video links.

{ "videoUrls": ["https://www.facebook.com/reel/2429400350831482"] }
{ "urls": ["https://www.facebook.com/reel/2430902544051423", "https://www.facebook.com/reel/1180563276897543"] }

📦 Output Format

Every row is typed, normalized JSON with a stable key set. Export as JSON, JSONL, CSV, XML, Excel or HTML from the Apify dataset, or read it through the Apify API. A run also writes a SUMMARY.json object to the Actor's key-value store with per-run totals — see below.

Output for a Reel

A status: "Success" row carries every key the Actor produces. With includeAnchorEvidence at its default of true, that's all of the following:

{
"title": "One Sheet Coq Au Vin | Tasty",
"description": "This one-pan French classic is easier than it looks.",
"uploader": null,
"uploader_id": "61559962654628",
"thumbnail": "https://scontent.fplu2-1.fna.fbcdn.net/v/t15.5256-10/thumb.jpg",
"duration": 47.36,
"id": "2429400350831482",
"formats": [
{
"format_id": "dash-audio-0",
"manifest_url": "https://video.fplu2-1.fna.fbcdn.net/dash/manifest.mpd",
"ext": "m4a",
"width": null,
"height": null,
"tbr": 128.0,
"asr": 44100,
"fps": null,
"language": null,
"format_note": "DASH audio",
"filesize": 758272,
"container": "m4a_dash",
"vcodec": "none",
"acodec": "mp4a.40.2",
"dynamic_range": null,
"url": "https://video.fplu2-1.fna.fbcdn.net/dash/audio-0.m4a",
"manifest_stream_number": 0,
"is_dash_periods": true,
"http_headers": {"User-Agent": "facebookexternalhit/1.1"},
"downloader_options": {"http_chunk_size": 262144000},
"protocol": "https",
"audio_ext": "m4a",
"video_ext": "none",
"audio_channels": 2,
"vbr": 0,
"abr": 128.0,
"resolution": "audio only",
"aspect_ratio": null,
"format": "dash-audio-0 - audio only (DASH audio)"
},
{
"format_id": "dash-video-0",
"manifest_url": "https://video.fplu2-1.fna.fbcdn.net/dash/manifest.mpd",
"ext": "mp4",
"width": 720,
"height": 1280,
"tbr": 1100.0,
"asr": null,
"fps": 30.0,
"language": null,
"format_note": "DASH video",
"filesize": 6513920,
"container": "mp4_dash",
"vcodec": "avc1.640028",
"acodec": "none",
"dynamic_range": "SDR",
"url": "https://video.fplu2-1.fna.fbcdn.net/dash/video-0.mp4",
"manifest_stream_number": 0,
"is_dash_periods": true,
"http_headers": {"User-Agent": "facebookexternalhit/1.1"},
"downloader_options": {"http_chunk_size": 262144000},
"protocol": "https",
"video_ext": "mp4",
"audio_ext": "none",
"abr": 0,
"vbr": 1100.0,
"resolution": "720x1280",
"aspect_ratio": 0.56,
"format": "dash-video-0 - 720x1280 (DASH video)"
}
],
"timestamp": 1748887451,
"automatic_captions": {},
"subtitles": {},
"webpage_url": "https://www.facebook.com/reel/2429400350831482",
"original_url": "https://www.facebook.com/reel/2429400350831482",
"webpage_url_basename": "2429400350831482",
"webpage_url_domain": "facebook.com",
"extractor": "facebook",
"extractor_key": "Facebook",
"playlist": null,
"playlist_index": null,
"thumbnails": [{"url": "https://scontent.fplu2-1.fna.fbcdn.net/v/t15.5256-10/thumb.jpg", "id": "0"}],
"display_id": "2429400350831482",
"fulltitle": "One Sheet Coq Au Vin | Tasty",
"duration_string": "0:47",
"upload_date": "20260602",
"release_year": null,
"requested_subtitles": null,
"_has_drm": null,
"epoch": 1753500862,
"requested_formats": [
{ "format_id": "dash-video-0", "...": "same shape as the video format entry above" },
{ "format_id": "dash-audio-0", "...": "same shape as the audio format entry above" }
],
"format": "dash-video-0 - 720x1280 (DASH video)+dash-audio-0 - audio only (DASH audio)",
"format_id": "dash-video-0+dash-audio-0",
"ext": "mp4",
"protocol": "https+https",
"language": null,
"format_note": "DASH video+DASH audio",
"filesize_approx": 7272192,
"tbr": 1228.0,
"width": 720,
"height": 1280,
"resolution": "720x1280",
"fps": 30.0,
"dynamic_range": "SDR",
"vcodec": "avc1.640028",
"vbr": 1100.0,
"stretched_ratio": null,
"aspect_ratio": 0.56,
"acodec": "mp4a.40.2",
"abr": 128.0,
"asr": 44100,
"audio_channels": 2,
"videoId": "2429400350831482",
"ownerId": "61559962654628",
"reactionsCount": 15234,
"commentsCount": 812,
"sharesCountRounded": "1.2K",
"engagementAnchored": true,
"publishedAt": "2026-06-02T18:04:11Z",
"ageDays": 53.6,
"status": "Success",
"isValid": true,
"unavailableReason": null,
"charged": true,
"scrapedAt": "2026-07-26T09:14:22.123Z",
"anchorStoryId": "S:_I61559962654628:VK:2429400350831482",
"anchorFeedbackId": "feedback:2429400350831482",
"engagementBlocksOnPage": 6,
"naiveFirstMatchReactions": 348,
"naiveFirstMatchWasWrong": true
}

Two fields are consistently null on the Reel surface, not a missed extraction: uploader (Facebook does not embed an owning_profile name on Reel pages, only the numeric uploader_id/ownerId) and release_year (Facebook never publishes one). requested_formats, format, format_id, ext, protocol, filesize_approx, tbr, width, height, resolution, fps, dynamic_range, vcodec, vbr, stretched_ratio, aspect_ratio, acodec, abr, asr and audio_channels are only populated when at least one playable format was found — on a Success row that is always the case.

Row shapes: Success, Unavailable and Failed

status takes one of three values, and the row shape genuinely differs between them — this matters if you're writing a schema-strict downstream consumer.

Success — every key shown above is populated (or explicitly null where Facebook doesn't publish the value). Only Success rows use the row_result charged event.

Unavailable (only written when includeUnavailableRows is true, its default) — the same key set as a Success row, but every key is forced to null except formats ([]), thumbnails ([]), automatic_captions ({}), subtitles ({}), extractor ("facebook"), extractor_key ("Facebook"), webpage_url_domain ("facebook.com"), and epoch. Notably videoId, reactionsCount and commentsCount are null here too, even when they were technically computable — this is deliberate: on a page-video or watch-link shell, any data present belongs to other creators' recommended videos, so carrying it over would recreate the exact cross-video mixup this Actor exists to prevent. anchorStoryId, anchorFeedbackId, engagementBlocksOnPage, naiveFirstMatchReactions and naiveFirstMatchWasWrong are absent entirely, regardless of includeAnchorEvidence. Added fields: original_url, status: "Unavailable", isValid: false, unavailableReason (one of feed_fallback_no_video_data, video_unavailable_or_not_a_reel, no_media_streams_found, no_playable_formats), charged: false, scrapedAt.

{
"status": "Unavailable",
"isValid": false,
"unavailableReason": "feed_fallback_no_video_data",
"charged": false,
"original_url": "https://www.facebook.com/watch/?v=999999999999999",
"scrapedAt": "2026-07-26T09:15:03.201Z",
"videoId": null,
"reactionsCount": null,
"commentsCount": null,
"formats": [],
"thumbnails": [],
"automatic_captions": {},
"subtitles": {},
"extractor": "facebook",
"extractor_key": "Facebook",
"webpage_url_domain": "facebook.com",
"epoch": 1753500903
}

Failed — written when the HTTP fetch itself raises an exception after 3 retries. This row is a different, minimal shape: only original_url, status: "Failed", isValid: false, unavailableReason: "fetch_failed", charged: false, error (the exception message), and scrapedAt are present — none of the video or engagement keys exist at all, not even as null.

{
"original_url": "https://www.facebook.com/reel/0000000000000000",
"status": "Failed",
"isValid": false,
"unavailableReason": "fetch_failed",
"charged": false,
"error": "ClientResponseError: 429, message='Too Many Requests'",
"scrapedAt": "2026-07-26T09:16:40.558Z"
}

A fourth case never reaches the dataset at all: a Reel that fetches and extracts successfully but falls below minReactions or minComments is filtered silently — no row is written and nothing is charged. It's only visible in the run log and in SUMMARY.json.

The SUMMARY.json key-value store record

Alongside the dataset, every run writes a SUMMARY.json object to its default key-value store: runId, total_requested, total_processed, success_count, charged_count, unavailable_not_charged, filtered_not_charged, failed_count, engagement_anchored_count, rows_where_naive_first_match_would_have_been_wrong, a stats map of {url: outcome} for every input URL, and a note string describing the anchoring behaviour. Use it to reconcile billed rows against the URL list without re-scanning the dataset.

Schema stability and export options

Field names stay stable across runs — Facebook's front-end markup changing does not change the Actor's output keys, because extraction reads embedded page data rather than rendered HTML elements. If Facebook removes a key from the page payload, the corresponding output field returns null rather than disappearing, so downstream code that expects the key never breaks on a missing-key error. Datasets export as JSON, JSONL, CSV, XML, Excel (XLSX) or HTML from the Apify Console, the Apify API, or apify_client/apify-client in your own code.

💡 Facebook Videos Scraper With Reactions & Comment Counts Strategy Guide

🎯 Strategy 1: Real-time enrichment pipeline

When a new Reel URL lands in a spreadsheet, CRM record or content-tracking sheet, trigger a run with that single URL in videoUrls, wait for the run to finish, and append reactionsCount, commentsCount, sharesCountRounded and engagementAnchored back onto the source record. Because charged tells you whether the row was billed, you can safely re-run on a record that previously came back Unavailable without double-paying if it still fails.

🎯 Strategy 2: Scheduled monitoring and alerting

Keep a fixed list of Reel URLs you track — your own or a competitor's — and run the Actor on a recurring Apify Schedule. Store reactionsCount and commentsCount per videoId from each run, diff them against the previous run's values, and alert when the delta on a single Reel crosses a threshold you define. ageDays lets you exclude Reels that are too old to be worth continued monitoring.

🎯 Strategy 3: Bulk dataset build

For a research or training-data corpus, paste a large batch of Reel URLs into videoUrls, set minReactions/minComments to pre-filter out low-signal Reels before they're written, and raise maxConcurrency toward its documented maximum of 10 to shorten wall-clock time. Values outside 1–10 are clamped by the Actor and the coercion is logged, so an out-of-range setting degrades to the nearest valid value instead of failing the run. Aggregate the resulting dataset to CSV or Excel for downstream analysis.

Strategy comparison at a glance

StrategyBest forRun patternOutput format
Real-time enrichmentAppending engagement to individual records as they arriveSingle-URL run, triggered on demandJSON row via API, written back to source system
Scheduled monitoringTracking engagement trend on a fixed watchlistRecurring Apify Schedule, fixed URL listDataset diffed run-over-run by videoId
Bulk dataset buildResearch corpora, training dataOne large batch run, maxConcurrency raised, thresholds setCSV or Excel export of the full dataset

Facebook Videos Scraper With Reactions & Comment Counts is Scrapio's only Facebook Actor today, so there is no sibling scraper in this account to link to for group, Page or comment coverage. The table below lists other Facebook-focused tools observed live on the Apify Store as of 26 July 2026, for workflows this Actor doesn't cover.

ScraperWhat it extracts
whoareyouanas/facebook-group-scraperPosts, comments and full six-reaction breakdowns from Facebook groups and Pages
lanky_quantifier/facebook-public-scraperPosts, comments and Page metadata from public Facebook Pages
apify/facebook-comments-scraperComment threads with replies and commenter profile links from posts, photos, Reels and videos

How to integrate Facebook Videos Scraper With Reactions & Comment Counts with your stack

Facebook Videos Scraper With Reactions & Comment Counts works with any language or tool that can make an HTTP request, because it runs on the Apify platform and is called through the Apify API or an Apify SDK.

Python

from apify_client import ApifyClient
import csv
client = ApifyClient("<YOUR_APIFY_TOKEN>")
reel_urls = [
"https://www.facebook.com/reel/2429400350831482",
"https://www.facebook.com/reel/2430902544051423",
]
run = client.actor("<YOUR_USERNAME>/facebook-videos-scraper-reactions-comment-counts").call(
run_input={
"videoUrls": reel_urls,
"minReactions": 0,
"maxConcurrency": 5,
}
)
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
with open("reel_engagement.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(
f, fieldnames=["videoId", "status", "reactionsCount", "commentsCount", "sharesCountRounded"]
)
writer.writeheader()
for row in rows:
writer.writerow({k: row.get(k) for k in writer.fieldnames})
print(f"Wrote {len(rows)} rows")

Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('<YOUR_USERNAME>/facebook-videos-scraper-reactions-comment-counts').call({
videoUrls: [
'https://www.facebook.com/reel/2429400350831482',
'https://www.facebook.com/reel/2430902544051423',
],
minReactions: 0,
maxConcurrency: 5,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) {
console.log(row.videoId, row.status, row.reactionsCount, row.commentsCount);
}

Async and scheduled pipelines

For large URL lists, start the run through the Apify API and poll client.run(runId).get() (or the equivalent Console/API call) until the run's status is SUCCEEDED, rather than blocking on .call(). For recurring collection — daily or weekly engagement checks on the same Reel list — set up an Apify Schedule against this Actor with a fixed input, and read the resulting dataset from each scheduled run through the API on your own cadence.

🎯 Who Needs Facebook Videos Scraper With Reactions & Comment Counts? (Use Cases & Industries)

  • 🏢 Social media managers pull reactionsCount, commentsCount and sharesCountRounded for a batch of their brand's own Reels each week to report engagement trend to stakeholders, without opening Facebook's native (and slower) analytics UI for each post individually.
  • 📊 Growth and content teams compare reactionsCount and commentsCount across a set of Reel formats or hooks they've tested, using publishedAt and ageDays to normalize for how long each Reel has had to accumulate engagement before comparing.
  • 📱 Competitive analysts track a named competitor's public Reel output over time, anchoring runs to specific Reel URLs and watching commentsCount growth as an early signal of which content resonates before it becomes a broader trend.
  • 🔬 Researchers studying short-form video virality build public-data-only datasets from a sampled list of Reel URLs, using reactionsCount, commentsCount, sharesCountRounded and ageDays as structured engagement variables for statistical analysis.

Scraping publicly accessible data is broadly permitted in the United States; in hiQ Labs, Inc. v. LinkedIn Corp., 938 F.3d 985 (9th Cir. 2019), the Ninth Circuit held that accessing publicly available web data does not violate the Computer Fraud and Abuse Act — a precedent widely cited for public-data scraping generally, not specific to Facebook. This Actor requests only Reel pages a logged-out, anonymous visitor can already see; it does not bypass a login wall, a paywall or any access control.

Using the data may still violate Facebook's Terms of Service, which is a contractual matter between you and Facebook rather than a criminal one, and is a separate question from whether the scraping itself is lawful. Most fields returned — engagement counts, video metadata, format URLs — are not personal data. Two fields, uploader_id and ownerId, are numeric identifiers tied to the Reel's owner and could, combined with other information, constitute personal data about an individual creator under GDPR or CCPA; the Actor does not resolve these ids to a name, email or any other contact detail.

Facebook Videos Scraper With Reactions & Comment Counts returns only publicly accessible data. What you do with that data is your responsibility — consult legal counsel for commercial applications involving personal data.

❓ Frequently asked questions

Does Facebook Videos Scraper With Reactions & Comment Counts work without a Facebook account?

Yes. Every request is made logged out, on purpose — requests carry a facebookexternalhit/1.1 User-Agent rather than a browser identity, and no cookie, session or account is used anywhere in the Actor. A logged-in session risks being served a JavaScript shell with the embedded engagement data stripped out, so logged-out is the more reliable path, not a fallback.

How does it handle Facebook's anti-scraping measures, and can I run it at scale without getting blocked?

Reel pages resolve without a proxy on most runs. If a fetch looks blocked — the Actor checks the error message for markers like 403, 429, access denied or checkpoint — it automatically switches to an Apify Residential proxy for the retry. Each URL gets up to 3 fetch attempts with an increasing backoff delay before it's marked Failed, and all retries for a URL complete before any row for that URL is written, so a retry can never produce a duplicate row. maxConcurrency caps parallel fetches at 10; no uptime or block-rate figure is published for runs beyond that.

How fresh is the data Facebook Videos Scraper With Reactions & Comment Counts returns?

Every run performs a live fetch of each Reel URL at request time — nothing is cached between runs. scrapedAt on every row records exactly when that fetch happened.

What happens if I submit a URL that isn't a Reel, or a Reel that's unavailable?

A /<page>/videos/<id>/ or /watch/?v= URL is accepted as input but Facebook serves a recommendation-feed shell for it logged out, with no data about the specific video requested — the row comes back status: "Unavailable", unavailableReason: "feed_fallback_no_video_data", and is not charged. A dead or deleted Reel id, or a URL with no playable media stream at all, comes back Unavailable with unavailableReason set to video_unavailable_or_not_a_reel, no_media_streams_found or no_playable_formats. Set includeUnavailableRows to false if you'd rather these rows were dropped entirely instead of written.

Why might the reaction and comment counts differ from a plain HTML scraper?

Because a Facebook Reel page embeds roughly six engagement blocks — the requested Reel plus sibling Reels from the same creator shown in the up-next tray — in an order Facebook randomizes on every fetch. A scraper that takes the first engagement match it finds in the HTML will report a different video's numbers on most runs. This Actor decodes the base64 story token preceding each block and only emits the block anchored to the requested video id. With includeAnchorEvidence on, naiveFirstMatchReactions shows what a first-match scraper would have reported, and naiveFirstMatchWasWrong flags when that number was wrong.

Am I charged for Unavailable, Filtered or Failed rows?

No. Only status: "Success" rows use the row_result charged event. Unavailable rows (dead URLs or non-Reel links) and Failed rows (fetch errors after retries) are written to the dataset with charged: false if includeUnavailableRows is true. Reels that pass extraction but fall below minReactions or minComments are filtered before any row is written at all — they appear only in the run log and in SUMMARY.json, never in the dataset, and are never billed.

Which fields work best for AI training data and RAG indexing?

For RAG indexing, title and description carry the most descriptive free text per Reel and chunk cleanly into a vector store. For training data, reactionsCount, commentsCount, sharesCountRounded, engagementAnchored and ageDays are the most structurally consistent fields across rows — every value is a typed primitive with a defined null state, so no normalization pass is needed before indexing.

Does the Actor access or store personal data?

It returns only publicly accessible Reel data. Most fields — engagement counts, video metadata, format URLs — are not personal data. uploader_id and ownerId are numeric identifiers of the Reel's owner and could be considered personal data under GDPR or CCPA if combined with other information; the Actor itself does not resolve, store or link these ids to any name or contact detail beyond what Facebook's own page already exposes to an anonymous visitor. Lawful basis for storing and using any personal data you derive from the output sits with you.

Does Facebook Videos Scraper With Reactions & Comment Counts work with Claude, ChatGPT, and other AI agent tools?

It's callable as a standard HTTP endpoint through the Apify API, so any agent framework that can make a request — LangChain, CrewAI, a custom tool definition, or a direct API call from Claude or ChatGPT's tool-use interface — can invoke it and receive typed JSON directly. Every response is already structured; no HTML parsing step is needed before passing a row into an LLM context window.

How does Facebook Videos Scraper With Reactions & Comment Counts compare to other Facebook scrapers?

Checked on the Apify Store on 26 July 2026: whoareyouanas/facebook-group-scraper is the stronger choice if you need group or Page posts with a full six-reaction breakdown — it doesn't target Reels specifically. apify/facebook-comments-scraper is the stronger choice if you need individual comment records with replies and commenter profile links rather than a comment total. lanky_quantifier/facebook-public-scraper covers Page posts and explicitly notes in its own documentation that Reels "may not be fully supported in all cases." This Actor's advantage is depth on one surface: Reel engagement totals anchored to the correct video, with evidence to prove it, plus the underlying downloadable video record — none of the three above document that anchoring problem or solve it.

ℹ️ Disclaimer

Facebook Videos Scraper With Reactions & Comment Counts extracts only publicly available data from Facebook. This tool is intended for lawful use cases only. Users are responsible for complying with Facebook's Terms of Service and applicable data protection laws in their jurisdiction.