Facebook Video Search Scraper avatar

Facebook Video Search Scraper

Pricing

$19.99/month + usage

Go to Apify Store
Facebook Video Search Scraper

Facebook Video Search Scraper

Extract Facebook video search results effortlessly! This actor scrapes video titles, URLs, views, upload dates, page names, and descriptions from Facebook search results. Perfect for market research, content tracking, trend analysis, and competitive insights. Fast, accurate, and easy to use.

Pricing

$19.99/month + usage

Rating

0.0

(0)

Developer

Scraper Engine

Scraper Engine

Maintained by Community

Actor stats

0

Bookmarked

9

Total users

1

Monthly active users

3 days ago

Last modified

Share

Facebook Video Search Scraper — Video URL, Caption and Duration

Facebook Video Search Scraper turns a keyword into a dataset of public Facebook Watch video results: watch URL, video ID, caption, thumbnail, duration, creator profile and relative post time. Type one keyword per line, set how many videos you want per keyword, and typed JSON rows stream into the dataset as each search page lands — no HTML parsing, no selectors, no Facebook login. Every row records the keyword that produced it, so one run can cover a whole topic list.

⚠️ What this Actor does not return. Exact view, play, reaction and comment counts, parsed publish dates, hashtag entities and creator video catalogues are not carried by the Facebook search response this Actor reads, so they are not in the output — they belong to separate specialised versions of this Actor. What you get is what the search surface itself carries.

⚠️ The proxy input has a floor. Even with useApifyProxy set to false and no custom URLs, the run still routes through Apify Residential. From an unproxied datacentre address Facebook's search API answers HTTP 200 with rate-limit error 1675004 and returns zero results, so the Actor makes the substitution and announces it in the log rather than handing you an empty dataset.

What is Facebook Video Search Scraper?

Facebook Video Search Scraper is an Apify Actor that runs a keyword search against Facebook Watch and writes every public video result to a dataset as one row. It reads Facebook's own search response — the server-rendered search page plus the SearchCometResultsPaginatedResultsQuery GraphQL endpoint that page itself calls — and normalises both result shapes Facebook returns into a single rectangular row.

It runs logged out end to end: no Facebook account, no cookie, no token, no credential of any kind is used or requested. The only credential involved is your Apify token.

It is built for content researchers tracking what a topic looks like on Facebook video, social teams doing creator discovery, and developers or AI engineers feeding public video metadata into a pipeline.

What Facebook video data is publicly available to scrape?

Facebook Watch search is a logged-out surface: anyone can open facebook.com/watch/search?q=… and see video tiles with title, caption, thumbnail, duration, creator name and posting age. What is not there is the engagement layer — the search payload carries no numeric view, reaction or comment count for a result.

Data CategoryPublicly available on Watch searchGated behind login or absent from this surface
Video title, caption, watch URL, video ID✅ Public
Thumbnail image URL and duration✅ Public
Creator or page name, profile URL, verification badge✅ Public
Relative posting age ("3 days ago", "May 23")✅ Public
Result shape — video tile, reel, feed story✅ Public
Exact numeric view, play, reaction and comment countsNot in the search payload
Exact publish timestamp as a parsed dateNot in the search payload
Direct video file or download URLNot in the search payload
Personalised result rankingLogin
Private, friends-only and group-restricted videosLogin

Facebook Video Search Scraper only returns publicly visible data — what any visitor sees on the Watch search page. Nothing behind a login wall.

What data can I extract with Facebook Video Search Scraper?

Every row carries the video's identity and link, its display metadata, the creator profile behind it, and the provenance fields that tell you which keyword and which payload shape produced it. Eighteen top-level keys, three of which are nested objects.

Field NameDescription
facebookUrlThe Facebook Watch search URL this row came from, built from your keyword
videoUrlAbsolute watch link — a /videos/<id>/ or /reel/<id>/ permalink. A page to open, not a media file to download
labelFacebook's own accessibility label for the tile: caption, creator name, "Verified", spoken duration and, on tile results, a view-count phrase, all in one string
thumbnail_imageObject holding the thumbnail, or null when the payload carries no image
thumbnail_image.uriDirect fbcdn URL of the thumbnail JPEG
video_duration_textRuntime as displayed — m:ss, or h:mm:ss for anything over an hour
videoObject holding the video node, or null when the payload carries no node
video.idFacebook's internal video node identifier
video_owner_profileCreator or page object — always present, always the same seven keys
video_owner_profile.__typenameOwner entity type as Facebook labels it: User, Page, InstagramUserV2
video_owner_profile.idNumeric owner identifier
video_owner_profile.__isNodeRequested by the query but never returned on this surface — emitted as declared, always null
video_owner_profile.__isActorActor-interface type name when Facebook returns it
video_owner_profile.nameCreator or page display name
video_owner_profile.urlAbsolute profile URL — Facebook's own url, or built from its uri_token when only that is present
video_owner_profile.is_verifiedBoolean. true when Facebook returns is_verified: true or a verification_status of BLUE_VERIFIED, GOLD_VERIFIED or VERIFIED
video_broadcast_statusLive-broadcast status. The search surface rarely carries it — expect null
save_descriptionPost caption / description text as published
titleVideo title. On tile results this is Facebook's own title; on feed-story results it is the post message, so it matches save_description
relative_time_stringHuman posting age
facebookIdPublic video id parsed out of the watch URL — ?v=, /videos/<id> or /reel/<id> — falling back to the node id
inputUrlByte-identical duplicate of facebookUrl, kept because it is part of the output contract
queryThe keyword that produced this row
renderShapeWhich payload shape the row was parsed from: meta, story or ssr. null on notice rows
rowTypevideo for a result row, run_notice for an uncharged accounting row
errorReasonnull on video rows. On notice rows, why the keyword produced nothing
errorDetailnull on video rows. On notice rows, a short human-readable detail

Video and creator fields

videoUrl, facebookId and video.id identify the video three ways: the permalink you open, the public id inside that permalink, and Facebook's internal node id. They can differ — a reel permalink and a page-video permalink expose their id in different path shapes, and facebookId handles both. title, save_description and label carry the text: the first two are the clean caption fields, while label is Facebook's accessibility string and is the densest single field in the row.

video_owner_profile is the creator side. name, url and id identify the uploader, and __typename tells you whether that uploader is an individual User, a Page, or a cross-posted InstagramUserV2 — which matters in practice, because Instagram-sourced owners often arrive with no name or url at all.

Display, timing and provenance fields

video_duration_text and thumbnail_image.uri are the display layer — duration as Facebook renders it, and the thumbnail as a direct fbcdn URL you can fetch. There is no direct video download URL anywhere in the output, because the search payload does not contain one; videoUrl is a watch page.

relative_time_string behaves differently by result shape, and that is worth knowing before you parse it. On tile results (renderShape: "meta") it is Facebook's own string and can include a view-count clause — "May 23 · 210K views". On feed-story results (renderShape: "story") Facebook ships no such string, so the Actor derives one from the story's exact unix creation_time"6 days ago" — with no view clause, because the payload holds no view count for those results and one is never invented.

query, renderShape and rowType are provenance. query lets you fan one dataset back out per keyword. renderShape tells you which of the three parse paths produced the row. rowType separates real results from accounting rows.

🤖 Add-on: Need additional Facebook data?

Companion Scraper-Engine Actors cover the parts this one deliberately leaves out. Facebook Videos Scraper goes at videos from a specific page or profile rather than from a search query. Facebook Hashtag Search Scraper searches by hashtag instead of free text. Facebook Posts Scraper and Facebook Pages Recent Posts Scraper cover the text-post side of the same accounts you discover here.

Why not build this yourself?

Meta's Graph API does not expose a general Facebook video search to third-party developers — there is no public endpoint that takes an arbitrary keyword and returns other people's public videos. Check Meta's current published Graph API documentation for which video and page endpoints do exist and under what permissions and app-review process, because those terms change and are not reproduced here. That gap is why a scraper exists at all, and it is also why building one yourself means owning three moving parts.

The first is the operation id. Facebook's search results come from a persisted GraphQL query whose doc_id is not a constant — it is minted into the search page and into the rsrc.php JS bundles, and it rotates. This Actor resolves it fresh on every run: inline from the search HTML when it is there, otherwise by scanning up to 120 bundle URLs found by un-escaping the page first, because a regex that only reads plain src="…" attributes finds a small fraction of the bundles a page actually references.

The second is that one search returns two completely different result shapes. Some edges are classic video tiles (video_metadata_model); others are feed stories (SearchNativeVideoViewModel, roles VIDEOS_MBR_ENGAGEMENT, SOCIAL_VIDEOS, KV_RETRIEVAL_VIDEOS). A parser that only understands tiles silently drops the rest, and an entire page of results can be story-shaped. Both are parsed here and normalised onto one row.

The third is access. Facebook's search rate limit is time-based as well as address-based, so rotating exits without waiting simply burns the exit budget. Residential egress, a progressive back-off, wall detection that keys on body size rather than status code alone, and a fallback that recovers page one from HTML already downloaded — that is infrastructure to build, run and pay for before you have parsed a single row.

How to use Facebook Video Search Scraper

Facebook Video Search Scraper runs on Apify. Start it from the Apify Console, or call it through the Apify API.

  1. Open Facebook Video Search Scraper on Apify and click Try for free
  2. Type your search phrases into Keywords (one per line) (startUrls) — this is the only required input. Despite the field name these are keywords, not URLs
  3. Set Videos per keyword (maxVideos) — how many rows you want for each keyword, 1 to 1000, default 10
  4. Leave Proxy configuration as it is unless you have your own residential egress. The default resolves to Apify Residential, which is the tier Facebook's search API answers
  5. Click Start, then download the dataset as JSON, CSV or Excel — or read it through the Apify API

Short, focused phrases match best. street food behaves like a search; a whole sentence usually does not.

How to scale to bulk video extraction

startUrls is a list, so one run can cover many keywords: paste one phrase per line and each is searched in turn. Plain strings and { "url": "…" } objects are both accepted, so output from another tool can normally be fed in unchanged. Identical keywords are collapsed before the run starts.

maxVideos is per keyword, not a run total — eight keywords at maxVideos: 100 is a target of 800 rows. De-duplication is also per keyword, keyed on facebookId then videoUrl, so a video that legitimately matches two of your keywords appears once under each, and query tells you which is which.

What can you do with Facebook video search data?

  • 🎬 A content researcher mapping a niche runs ten topic keywords at maxVideos: 200, then groups by video_owner_profile.name and video_owner_profile.is_verified to see which creators own that topic on Facebook video.
  • 📈 A social strategist planning a format test pulls video_duration_text and title across a keyword set to see which lengths and hook styles actually surface in Facebook's own search ranking.
  • 🔎 A brand monitoring analyst schedules the same brand keywords weekly and diffs on facebookId to catch new videos mentioning the brand, using relative_time_string to separate fresh uploads from resurfaced back catalogue.
  • 🤝 A partnerships lead doing creator discovery keeps only rows where video_owner_profile.__typename is Page, collects video_owner_profile.url, and builds an outreach shortlist scoped to a single topic.
  • 🤖 An AI engineer building a topic-monitoring agent indexes title, save_description and label into a vector store keyed on query and videoUrl, so an agent can answer "what is being posted about this topic on Facebook video this week" against live search results rather than a stale export.

Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

How does Facebook Video Search Scraper handle rate limits and blocking?

No CAPTCHA is solved and no browser is launched — this Actor makes plain HTTP requests and reads Facebook's own JSON. What it does handle is Facebook's two real refusal signatures, neither of which a status-code check catches.

The wall is HTTP 400 with a body of roughly 1.5 KB and no serpResponse container in it. Classification keys on status plus body size plus the absence of that container, with a 4 KB ceiling, rather than on a status list. On the search page a wall triggers a user-agent escalation: the run starts on a desktop Chrome string and steps through a crawler-class user agent before giving up on that page.

The rate limit is HTTP 200 carrying {"errors":[{"message":"Rate limit exceeded","code":1675004}]} — a success status with no results in it. Because that limit is time-based as well as address-based, rotating the exit alone does not clear it, so the Actor waits before it rotates: a progressive back-off of 4 seconds per attempt up to a 30-second maximum, then a fresh exit, then the same cursor is retried. Each proxy tier gets a bounded budget of six exits before it falls back to Residential; when Residential itself is exhausted the run stops rather than spinning.

Two more behaviours matter. A healthy exit can answer HTTP 200 with an empty edge list and has_next_page: false — that page is re-asked on a rotated exit up to twice before it is believed. And if the GraphQL path delivers nothing at all for a keyword, the Actor recovers page one from the search HTML it already downloaded, at no extra request, tagging those rows renderShape: "ssr".

When a keyword still ends with zero result rows, the Actor writes one typed uncharged run_notice row naming the reason instead of failing the run. Pagination follows has_next_page and end_cursor and never the parse yield, so a page that produces no parseable rows does not end the keyword early.

⬇️ Input

Three parameters. One is required, one has a ceiling worth knowing about, and one behaves differently from how it reads — all three are covered below the table.

ParameterRequiredTypeDescriptionExample Value
startUrlsYesarrayKeywords, one per line. Despite the field name these are keywords, not URLs — each line is sent to Facebook Watch search as typed. Plain strings and { "url": "…" } objects are both accepted, and duplicates are collapsed. Prefilled with cricket and football.["cricket highlights", "street food bangkok"]
maxVideosNointegerVideo rows to collect per keyword. Minimum 1, maximum 1000, default 10. Values outside that range are clamped; a non-numeric value falls back to 10 with a log warning. Results are paged 20 at a time.50
proxyConfigurationNoobjectApify Proxy settings. Custom proxyUrls are tried first, then the Apify proxy groups you pick (with apifyProxyCountry when set), then Apify Residential as the terminal fallback. Prefilled with {"useApifyProxy": false}.{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}

On startUrls: the name is historical and the field is not URL-validated. Pasting a Facebook URL into it searches Facebook for that URL as a phrase, which is almost never what you want. Type the phrase itself.

On maxVideos: this is a target, not a promise, and the schema maximum is not the only limit. Facebook decides how deep a keyword goes — a narrow phrase can run out well before your target, and the run log reports the shortfall rather than padding the dataset. Above the maximum of 1000 sits one more rail that is the Actor's own rather than Facebook's: pagination stops after 60 pages per keyword, which at 20 results a page is a safety net around the 1000-row ceiling rather than a cap you will meet first.

On proxyConfiguration: your selection is honoured, with one exception — useApifyProxy: false and no custom URLs is not. The run uses Apify Residential anyway and logs a warning explaining why. Supply proxyUrls, or pick apifyProxyGroups, to override that default. If no proxy configuration can be built at all, the run continues on the direct connection and warns that yield will be reduced.

Example input

{
"startUrls": [
"cricket highlights",
"street food bangkok",
"tesla model y review"
],
"maxVideos": 50,
"proxyConfiguration": {
"useApifyProxy": true,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}

⬆️ Output

Every row carries the same 18 top-level keys in the same order, run after run, whether it came from a video tile, a feed story, the server-rendered fallback or the accounting path. Missing values arrive as null — no key is ever omitted, so the dataset stays rectangular and downstream code never needs a presence check. Rows are pushed live as each page finishes. Export as JSON, CSV or Excel, or read the dataset through the Apify API.

Each saved video row is billed as one row_result event. run_notice rows are pushed deliberately uncharged: an empty or blocked answer must not bill.

errorReason on a notice row is one of no_results, search_page_unavailable, wall, rate_limited, http_error, no_serp_container, unparseable, network_error or unhandled_error. To read only real results, filter on rowType:

videos = [row for row in items if row["rowType"] == "video"]

Example output

A tile result (renderShape: "meta"):

{
"facebookUrl": "https://www.facebook.com/watch/search?q=street+food+bangkok",
"videoUrl": "https://www.facebook.com/thaistreeteats/videos/1184920365577431/",
"label": "Bangkok's best boat noodles, 40 baht a bowl\nThai Street Eats\nVerified\nVideo length:3minutes; 12seconds\nJun 14 \n 486K views",
"thumbnail_image": {
"uri": "https://scontent-lhr8-1.xx.fbcdn.net/v/t15.5256-10/512884190_1184920372244097_1902884471933840112_n.jpg?stp=c0.246.1080.605a_dst-jpg_s403x403_tt6&_nc_cat=104&ccb=1-7&_nc_sid=e3495b&_nc_zt=23"
},
"video_duration_text": "3:12",
"video": {
"id": "1184920365577431"
},
"video_owner_profile": {
"__typename": "Page",
"id": "100064312887401",
"__isNode": null,
"__isActor": "Page",
"name": "Thai Street Eats",
"url": "https://www.facebook.com/thaistreeteats",
"is_verified": true
},
"video_broadcast_status": null,
"save_description": "Boat noodles on the Victory Monument canal side - 40 baht a bowl and the queue starts at 10am. Full guide in the comments.",
"title": "Bangkok's best boat noodles, 40 baht a bowl",
"relative_time_string": "Jun 14 · 486K views",
"facebookId": "1184920365577431",
"inputUrl": "https://www.facebook.com/watch/search?q=street+food+bangkok",
"query": "street food bangkok",
"renderShape": "meta",
"rowType": "video",
"errorReason": null,
"errorDetail": null
}

A feed-story result from the same keyword (renderShape: "story") — note the derived relative_time_string with no view clause, and title matching save_description:

{
"facebookUrl": "https://www.facebook.com/watch/search?q=street+food+bangkok",
"videoUrl": "https://www.facebook.com/reel/802315447719064/",
"label": "Chinatown pad thai at midnight #bangkok #streetfood\nNoodle Nomad\nVideo length:0minutes; 51seconds",
"thumbnail_image": {
"uri": "https://scontent-lhr8-2.xx.fbcdn.net/v/t51.75761-15/509771233_17942118820993356_2233910478440713654_n.jpg?stp=c0.434.1080.605a_dst-jpg_s403x403_tt6&_nc_cat=107&ccb=1-7&_nc_sid=e3495b&_nc_zt=23"
},
"video_duration_text": "0:51",
"video": {
"id": "802315447719064"
},
"video_owner_profile": {
"__typename": "User",
"id": "100089114370288",
"__isNode": null,
"__isActor": "User",
"name": "Noodle Nomad",
"url": "https://www.facebook.com/people/Noodle-Nomad/100089114370288/",
"is_verified": false
},
"video_broadcast_status": null,
"save_description": "Chinatown pad thai at midnight #bangkok #streetfood",
"title": "Chinatown pad thai at midnight #bangkok #streetfood",
"relative_time_string": "6 days ago",
"facebookId": "802315447719064",
"inputUrl": "https://www.facebook.com/watch/search?q=street+food+bangkok",
"query": "street food bangkok",
"renderShape": "story",
"rowType": "video",
"errorReason": null,
"errorDetail": null
}

An uncharged accounting row for a keyword that returned nothing:

{
"facebookUrl": "https://www.facebook.com/watch/search?q=zqx+nonexistent+phrase",
"videoUrl": null,
"label": null,
"thumbnail_image": null,
"video_duration_text": null,
"video": null,
"video_owner_profile": {
"__typename": null,
"id": null,
"__isNode": null,
"__isActor": null,
"name": null,
"url": null,
"is_verified": false
},
"video_broadcast_status": null,
"save_description": null,
"title": null,
"relative_time_string": null,
"facebookId": null,
"inputUrl": "https://www.facebook.com/watch/search?q=zqx+nonexistent+phrase",
"query": "zqx nonexistent phrase",
"renderShape": null,
"rowType": "run_notice",
"errorReason": "no_results",
"errorDetail": "0 edge(s) seen over 1 page(s)"
}

One coercion to know about before you filter: video_owner_profile.is_verified is false both when Facebook says the owner is not verified and when Facebook says nothing at all. Feed-story results carry no verification field, so false there means "not stated", not "not verified". Treat is_verified: true as the reliable signal.

How does it work?

Facebook Watch search is backed by a persisted GraphQL query that the search page itself calls. Facebook Video Search Scraper follows the same route a browser does. It requests facebook.com/watch/search?q=<keyword> through your proxy, reads the pagination end_cursor and the SearchCometResultsPaginatedResultsQuery operation id out of that page — inline when present, otherwise from the page's JS bundles — then posts to /api/graphql/ and pages through data.serpResponse.results.edges twenty results at a time until your target is reached or Facebook reports no further page.

Each edge is then normalised. Video tiles and feed stories arrive as different structures and are both mapped onto one row shape, with every lookup done by key rather than by position, so a moved value nulls one field instead of failing the row. Because the Actor parses Facebook's structured search response instead of rendered markup, a Facebook front-end redesign does not change your field names. If the GraphQL path yields nothing for a keyword, page one is recovered from the search HTML already downloaded — no extra request is made.

Only publicly visible search data is collected. No account, cookie, token or session is used.

Integrations

Facebook Video Search Scraper is an Apify Actor, so it works with anything that can call the Apify API or consume a dataset — schedules, webhooks, no-code platforms and agent frameworks included.

Calling Facebook Video Search Scraper from Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("<YOUR_USERNAME>/facebook-video-search-scraper-pay-per-events").call(run_input={
"startUrls": ["street food bangkok", "cricket highlights"],
"maxVideos": 50,
"proxyConfiguration": {"useApifyProxy": True, "apifyProxyGroups": ["RESIDENTIAL"]},
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if row["rowType"] != "video":
continue
print(row["query"], row["title"], row["video_duration_text"], row["videoUrl"])

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request. Skip anything whose rowType is not video and you are left with results only.

No-code tools (n8n, Make, Zapier)

In n8n, use the Apify node — or an HTTP Request node pointed at the Apify run endpoint with your token — and pass the same JSON input shown above; a Filter node on rowType drops the notice rows. In Make, the Apify module supports run-and-wait, so a weekly keyword sweep can feed a Google Sheets or Airtable step directly. Apify's own schedules and webhooks cover the recurring case with no external tool at all: schedule the keyword list daily, and have a webhook push each finished dataset into your warehouse.

Scraping publicly accessible video listings is broadly treated as permissible where no authentication is bypassed, and Facebook Video Search Scraper reads only Facebook's logged-out Watch search results — no account, no cookie, no session.

This output does identify people, so treat it accordingly. video_owner_profile.name, video_owner_profile.id, video_owner_profile.url and video_owner_profile.__typename name the uploader, and when __typename is User that uploader is an individual rather than a brand page. label, title and save_description can also contain names. That makes these rows personal data under GDPR and comparable regimes: if you store them you need a lawful basis — a documented and assessed legitimate interest, or consent — plus a retention limit and a route to honour access and deletion requests. CCPA/CPRA treats the same fields as personal information about California residents.

Consult legal counsel if your use case involves bulk storage of personal data, profiling individual creators, or building a contact list out of these fields. Facebook's own terms of service apply independently of the data-protection question.

❓ Frequently asked questions

What Facebook video fields does Facebook Video Search Scraper return?

The five most used are videoUrl, title, video_duration_text, video_owner_profile.name and relative_time_string. Eighteen top-level keys are returned per row, three of them nested objects — see the data fields table above for all of them, nested keys included.

Does Facebook Video Search Scraper require a Facebook account or login?

No. The Actor runs logged out end to end — no Facebook account, cookie, token or session is used or requested. The only credential you need is your Apify token.

How many videos can I extract in one run?

maxVideos accepts 1 to 1000 and applies per keyword, so a run's ceiling is your keyword count multiplied by that number. Results are paged 20 at a time. Facebook decides the real depth of any given keyword: a narrow phrase can exhaust before your target, and the log reports the shortfall rather than padding the dataset. A second rail, the Actor's own rather than Facebook's, stops pagination after 60 pages per keyword.

What happens when a keyword returns zero results?

The keyword still produces exactly one row — an uncharged run_notice row with rowType: "run_notice" and an errorReason explaining why — and the run continues with your remaining keywords. errorReason is no_results when Facebook simply had nothing, or one of search_page_unavailable, wall, rate_limited, http_error, no_serp_container, unparseable, network_error or unhandled_error when something upstream got in the way; errorDetail adds the specifics, such as how many edges were seen over how many pages. Because that row carries the full key set, the dataset stays rectangular. Filter with row["rowType"] == "video" to keep results only, or read the notice rows deliberately to see which keywords under-delivered and why.

Can I search multiple Facebook keywords at once?

Yes. startUrls is a list — one keyword per line, each searched in turn. Duplicate keywords are collapsed before the run starts. De-duplication of results happens per keyword, so a video matching two of your keywords appears once under each, tagged by query.

Does Facebook Video Search Scraper work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP endpoint 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. Give the agent the keyword and maxVideos, and have it branch on rowType.

How does Facebook Video Search Scraper compare to other Facebook video search scrapers?

Checked on the Apify Store on 25 July 2026: apify/facebook-video-search-scraper is the most used of the three and documents the same field names this Actor emits — facebookUrl, videoUrl, label, thumbnail_image, video_duration_text, video, video_owner_profile, video_broadcast_status, save_description, title, relative_time_string, facebookId, inputUrl — and accepts a keyword or a Facebook search URL; in the example output published on its listing, facebookId repeats the search URL rather than the video id. powerai/facebook-video-search-scraper documents extra filters this Actor does not have — recent_videos, location_uid, start_date and end_date — with an output shape of its own (video_id, video_url, author, time_and_views_raw). igview-owner/facebook-search-scraper documents a page-count input (maxPages, 1–100) rather than a video-count target. None of the three READMEs documents uncharged notice rows for empty keywords, per-shape parsing of feed-story results, or a server-rendered fallback — those are not documented on their listings, which is not the same as saying they are unsupported.

Does Facebook Video Search Scraper return data in a format LLMs can use directly?

Yes. Every row is typed, normalized JSON with the same field names on every run and no missing keys. No HTML parsing, no selectors. Pass a row straight into an LLM context window, index it into a vector store, or hand it to an agent tool.

What happens when Facebook changes its layout or anti-bot system?

The scraper is maintained, and because it reads Facebook's structured search response rather than rendered markup, a visual redesign generally does not affect it. The two parts that do move are handled at runtime: the GraphQL operation id is resolved fresh on every run instead of being hard-coded, and both result shapes Facebook currently returns are parsed, with unparsed edges counted and logged rather than silently dropped. Your field names and types do not change on your end.

Can I use Facebook Video Search Scraper without managing proxies or browser infrastructure?

Yes. There is no browser to run — the Actor makes plain HTTP requests — and the proxy ladder is automatic: custom URLs, then your chosen Apify groups, then Apify Residential, with a bounded exit budget per tier, a progressive back-off before each rotation, and user-agent escalation on the search page. You never create a proxy account or rotate an IP yourself. One thing it does not do is solve CAPTCHAs: it detects and backs off Facebook's wall and rate-limit responses rather than solving challenges.

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

For RAG indexing: title, save_description and label carry the most descriptive text per row and chunk cleanly, with videoUrl as the citation anchor and query as a natural partition key. For training data: video_duration_text, facebookId, renderShape, video_owner_profile.__typename and video_owner_profile.is_verified are the most structurally consistent fields across records. All values return as typed primitives or plain nested objects with null for absent data, so no normalization pass is needed before indexing.

Scraper NameWhat it extracts
Facebook Videos ScraperVideos from a specific Facebook page or profile rather than from a search
Facebook Hashtag Search ScraperPublic posts and videos behind a Facebook hashtag
Facebook Posts ScraperText posts, captions and post metadata from Facebook pages
Facebook Pages Recent Posts ScraperThe newest posts from a list of Facebook pages
Youtube Search ScraperVideo search results for the same keyword on YouTube
Tiktok Video Scraper Creator Profile ContactTikTok videos with creator profile details

💬 Your feedback

Found a bug, or need a field that is in Facebook's search payload but not in the output? Open an issue on the Actor's Issues tab. Reports that include the exact keyword and the input JSON you ran are the fastest to reproduce and fix.