Facebook Video Search Scraper - Description & Hashtags
Pricing
Pay per usage
Facebook Video Search Scraper - Description & Hashtags
Facebook Video Search Scraper extracts videos from Facebook search results, capturing titles, descriptions, URLs, views, reactions, comments, shares, and timestamps. Ideal for content research, trend analysis, competitor monitoring, and automating structured Facebook video data collection.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
SimpleAPI
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
0
Monthly active users
3 days ago
Last modified
Categories
Share
Facebook Video Search Scraper — Captions, Hashtags and Video Links
Search Facebook Watch by keyword and get back one row per public video, carrying the full caption text and Facebook's own structured hashtag entities — tag name, /hashtag/ URL and stable numeric ID — plus the creator, thumbnail, duration and a direct video link. Every row is typed JSON keyed on query, videoUrl, fullDescription, hashtags, hashtagSource and video_owner_profile. Content strategists, hashtag researchers and social listening teams use it to see the exact wording and tags creators attach to a topic before publishing their own. No Facebook login is required — everything is read logged out.
🎬 What is Facebook Video Search Scraper - Description & Hashtags?
It is an Apify Actor that runs a keyword through Facebook's own Watch search and returns every matching public video as one structured dataset row, including the complete caption and Facebook's linkified hashtag entities — not a screen-scraped snippet. No Facebook account, login or cookie is required; the Actor searches and reads video pages logged out.
- Discovery is by keyword, not URL or category — one search phrase per line, each searched separately against
facebook.com/watch/search - Core identifying fields on every row —
videoUrl,facebookId,video_owner_profile(creator name, profile URL, verification status) - The secondary data that drives the buying decision —
fullDescription(the complete caption) and a structured hashtag block:hashtags,hashtagUrls,hashtagIds,hashtagCount,hashtagSource - Temporal and media fields —
relative_time_string,video_duration_text,thumbnail_image - A real keep-if filter —
mustContainHashtagsdrops any video that uses none of your listed tags - Standard Apify dataset export — JSON, CSV, Excel or XML, or read straight through the Apify API
What data can I extract with Facebook Video Search Scraper?
Every row combines a caption/hashtag block with the underlying video's identity, creator and timing fields.
| Field | Example value | Use case |
|---|---|---|
query | "street food" | Which of your keywords produced this row |
videoUrl | "https://www.facebook.com/reel/1180213145174950/" | Direct link to watch or embed the video |
fullDescription | "#cricket #fans #cricketlovers \n#indiacricket" | The complete caption as written |
hashtags | ["cricket","fans","cricketlovers","indiacricket"] | Tag names, deduplicated, in caption order |
hashtagUrls | ["https://www.facebook.com/hashtag/cricket?__eep__=6", ...] | Browsable /hashtag/ page for each tag |
hashtagIds | ["222029194603396", ...] | Facebook's stable numeric hashtag ID, for joins |
hashtagCount | 4 | len(hashtags) |
hashtagSource | "entity" | Which mechanism produced hashtags for this row |
hashtagsFromText | ["cricket","fans","cricketlovers","indiacricket"] | A plain-text #tag scan of the same caption |
matchedRequiredHashtags | [] | Which of your mustContainHashtags this row matched |
descriptionSource | "in_payload" | Where fullDescription came from |
descriptionStatus | "in_payload" | Outcome of the caption-recovery attempt |
descriptionLength | 44 | Character count of fullDescription |
renderShape | "story" | Which of Facebook's two result view models this row came from |
mentionedPages | [] | Tagged profile/Page/Group entities in the caption |
video_owner_profile | {"name":"Cricket Lover","url":"...","is_verified":false, ...} | Creator identity and verification flag |
relative_time_string | "21 days ago" | Human-readable publish recency |
video_duration_text | "14:57" | Video length as displayed |
thumbnail_image | {"uri":"https://scontent...jpg"} | Preview image |
title | "Sikorsky's S-97 Raider..." | Video title, where Facebook supplies one |
save_description | "..." | The caption exactly as Facebook's search response carries it |
label | "..." | Facebook's accessibility label for the tile |
facebookId | "1180213145174950" | Facebook's video ID |
video | {"id":"1180213145174950"} | Video object as returned by Facebook |
video_broadcast_status | null | Broadcast status as reported by Facebook |
facebookUrl | "https://www.facebook.com/watch/search?q=cricket" | The search page this row was found on |
inputUrl | "https://www.facebook.com/watch/search?q=cricket" | Same value as facebookUrl |
rowType | "video" | video for a result, error for an accounting row |
errorReason | "rate_limited" | Present only on rowType: "error" rows — see below |
🏷️ Caption and hashtag fields
fullDescription, hashtagSource, hashtagIds and matchedRequiredHashtags are the fields that decide whether a video is useful for tag research: hashtagSource tells you whether the tags came from Facebook's own linkified entity list (entity, with stable IDs) or a text scan (regex, no IDs), descriptionStatus tells you whether the caption is the complete text or a tile snippet, and matchedRequiredHashtags proves which of your filter tags actually matched. A hashtag researcher counting tag co-occurrence across a niche joins on hashtagIds rather than tag strings, since Facebook's own ID is stable across spelling variants.
🎯 Identity and targeting fields
query tells you which keyword produced the row, renderShape tells you which Facebook view model it came from, and video_owner_profile carries the creator's name, profile URL and verification flag for filtering by publisher. relative_time_string and video_duration_text let you segment by recency and length without a second request.
Why not build this yourself?
Facebook does not publish a public API that lets a third-party app search Watch videos by keyword and read back captions and hashtag entities — the Graph API's public surface does not expose this search. A DIY scraper means reverse-engineering an internal GraphQL endpoint instead, and the maintenance load is the real cost.
Facebook's Watch search runs on an internal GraphQL operation identified by a numeric doc_id that has to be pulled out of the page's own JS bundles — the bundle discovery has to scan escaped-slash URLs embedded in JSON, not just src="..." attributes, or the operation id goes stale on the next bundle reshuffle. The search response itself arrives in two different, differently-shaped result edges under the same GraphQL typename (video_metadata_model tiles and story edges), and only one of them ships Facebook's own hashtag entity list — the other has to be recovered from the video's own page, which is refused to a normal browser user-agent and only served to a search-engine-crawler user-agent class. On top of that, Facebook answers a datacenter proxy exit to the search endpoint with an HTTP 200 and a rate-limit error carrying zero results rather than an honest block, so a scraper that doesn't recognize that response as a wall reports an empty run as a successful one. Facebook Video Search Scraper - Description & Hashtags handles the doc_id resolution, both result shapes, the caption-recovery hop, and the wall/rate-limit detection so you get structured rows instead of an integration project.
Build your own if you need a use case this Actor doesn't cover, or already run comparable GraphQL-scraping infrastructure. Use this Actor for a working keyword-to-caption-and-hashtag pipeline today.
How to use data extracted from Facebook Video Search Scraper
📝 Content and SEO strategists
Search your target topic, then read fullDescription and hashtags across the result set to see the actual wording and tag vocabulary creators use before writing your own caption. hashtagSource: "entity" rows carry Facebook's own tag IDs, so you can distinguish an authoritative tag from a text-scan guess when deciding which ones to reuse.
📊 Hashtag researchers and analysts
Run a keyword list on a schedule and count tag frequency and co-occurrence with hashtags and hashtagIds, joining on the numeric ID rather than the tag string so spelling or capitalization variants don't split a count. mustContainHashtags narrows a run to videos already using specific tags, and matchedRequiredHashtags records which ones matched each row.
🔎 Social listening and brand monitoring
Re-run the same keyword list on a schedule via the Apify scheduler and diff fullDescription and hashtags over time to watch how the language around a product, event or brand shifts. video_owner_profile tells you which creators and Pages are publishing on the topic and how often.
🤖 AI agents and automated pipelines
fullDescription and hashtags are typed, normalized JSON with no HTML to strip, so a row can be indexed into a vector store or handed to an agent tool directly. An agent framework calling the Apify API can search a topic and read back structured caption and hashtag data in the same request-response cycle it uses for any other tool call.
⬇️ Input
Required: startUrls.
| Parameter | Required | Type | Description | Example value |
|---|---|---|---|---|
startUrls | Yes | array | One search phrase per line — for example street food, cricket highlights, ev charging. Each line is searched separately on Facebook Watch and every returned row is tagged with the keyword it came from. These are keywords, not URLs. | ["cricket", "street food"] |
maxVideos | No | integer (min 1, max 1000, default 10) | How many videos to collect for each keyword. Facebook returns roughly 7-8 results per page, so larger numbers mean more pages and longer runs. Results are de-duplicated by video ID across pages and keywords. | 25 |
extractHashtags | No | boolean (default true) | Read Facebook's own linkified hashtag entities out of the caption — each one gives a tag name, a browsable /hashtag/ URL and a stable tag ID. When Facebook returns no entity list for a caption, the tags are read from the text instead and hashtagSource says so. | true |
includeFullDescription | No | boolean (default true) | About 60% of results carry Facebook's own hashtag entity list in the search response. The rest are tile-shaped and carry caption text only. Turn this on to fetch those videos' own pages, which returns Facebook's hashtag entities — name, /hashtag/ URL and numeric ID — for 33 of 38 measured tile rows, and confirms the caption text (identical on 38/38, so it corrects nothing but proves nothing was cut). Costs one request per tile-shaped video. descriptionStatus records what happened per row. | true |
mustContainHashtags | No | array (default []) | One tag per line, with or without the leading #. A video is kept when it carries at least one of them, and matchedRequiredHashtags lists which ones matched. Leave empty to keep everything. | ["streetfood"] |
includeMentionedPages | No | boolean (default false) | Off by default on purpose. Profile, Page and Group tags inside captions are rare — measured at 2 of 59 videos (3%) — and Facebook returns name: null on them, so only the short name, ID and URL are usable. Expect an empty array on almost every row. | false |
proxyConfiguration | No | object | Your selection is honoured: custom proxy URLs are tried first, then the Apify groups you pick, then Apify Residential as the final tier. Residential is the default because Facebook's search endpoint answers datacentre exits with a rate-limit error and zero results. | {"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]} |
Input example
{"startUrls": ["street food", "ev charging"],"maxVideos": 25,"extractHashtags": true,"includeFullDescription": true,"mustContainHashtags": ["streetfood"],"includeMentionedPages": false,"proxyConfiguration": { "useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"] }}
Common pitfall: despite the field name, startUrls only accepts search keywords, not Facebook URLs. Pasting an actual Facebook URL does not open that page — it gets searched as literal text against Watch search and will usually return nothing useful. Type the topic itself, one per line.
⬆️ Output
Typed, normalized JSON, one row per video (or one accounting row per keyword that produced nothing — see below). Export as JSON, CSV, Excel or XML, or read the dataset through the Apify API.
{"query": "cricket","videoUrl": "https://www.facebook.com/reel/1180213145174950/","fullDescription": "#cricket #fans #cricketlovers \n#indiacricket","hashtags": ["cricket", "fans", "cricketlovers", "indiacricket"],"hashtagCount": 4,"hashtagSource": "entity","hashtagUrls": ["https://www.facebook.com/hashtag/cricket?__eep__=6","https://www.facebook.com/hashtag/fans?__eep__=6","https://www.facebook.com/hashtag/cricketlovers?__eep__=6","https://www.facebook.com/hashtag/indiacricket?__eep__=6"],"hashtagIds": ["222029194603396", "149550058542367", "511137268924703", "419268324830427"],"hashtagsFromText": ["cricket", "fans", "cricketlovers", "indiacricket"],"matchedRequiredHashtags": [],"descriptionSource": "in_payload","descriptionStatus": "in_payload","descriptionLength": 44,"renderShape": "story","mentionedPages": [],"video_owner_profile": {"__typename": "User","id": "100071552850071","__isNode": null,"__isActor": "User","name": "Cricket Lover","url": "https://www.facebook.com/people/Cricket-Lover/100071552850071/","is_verified": false},"relative_time_string": "21 days ago","video_duration_text": "14:57","thumbnail_image": { "uri": "https://scontent-phx1-1.xx.fbcdn.net/v/t15.5256-10/..." },"title": "#cricket #fans #cricketlovers \n#indiacricket","save_description": "#cricket #fans #cricketlovers \n#indiacricket","label": null,"facebookId": "1180213145174950","video": { "id": "1180213145174950" },"video_broadcast_status": null,"facebookUrl": "https://www.facebook.com/watch/search?q=cricket","inputUrl": "https://www.facebook.com/watch/search?q=cricket","rowType": "video"}
errorReason does not appear on this row at all — it is not null, the key is simply absent. It exists only on rowType: "error" accounting rows, which carry every other field as null or empty and explain why a keyword produced nothing (search_page_blocked, no_cursor, rate_limited, blocked, no_response, all_results_filtered_out_by_mustContainHashtags, facebook_returned_no_result_edges, no_parsable_videos). Accounting rows are pushed without the charged_event_name used for real video rows, so a blocked or empty keyword is never billed — filter them out downstream with rowType == "video".
How do you filter and target specific videos?
Four real controls decide what lands in your dataset. Keyword precision comes first — Facebook Watch search matches on the phrase you supply, so a specific phrase ("street food vendor mumbai") returns a tighter, more on-topic set than a broad single word. mustContainHashtags is a keep-if-matches-any-of filter: a video survives if it carries at least one of your listed tags, and matchedRequiredHashtags on the surviving rows shows you which tag actually matched — useful for auditing whether the filter is doing what you expect. Completeness controls trade cost for data depth: extractHashtags off skips hashtag work entirely (hashtagSource: "disabled"), and includeFullDescription off skips the one extra request per tile-shaped video, leaving those rows with descriptionStatus: "snippet_only" and no hashtag IDs. Volume is maxVideos per keyword — Facebook serves roughly 7-8 results per page, so a higher target means more pages and a longer run, and results are de-duplicated by video ID across both pages and keywords so the same video posted under two searched topics only appears once.
{ "startUrls": ["mumbai street food"], "maxVideos": 50 }
{ "startUrls": ["ev charging"], "mustContainHashtags": ["evcharging", "electricvehicle"] }
{ "startUrls": ["cricket"], "extractHashtags": true, "includeFullDescription": false }
▶️ Want to try other Facebook scrapers?
| Scraper Name | What it extracts |
|---|---|
| Facebook Page Content Strategy Scraper | Post-level topic keywords and content patterns from Facebook Pages |
| Facebook Group Engagement Analyzer | Engagement scoring and leaderboards for posts inside a Facebook Group |
| Instagram Reels Hashtag Scraper | Reels discovered by hashtag, keyword or URL, with play counts and audio metadata |
| YouTube Channel Keyword Research Scraper | Channels and videos discovered by seed keyword, with SEO keyword extraction |
How to extract Facebook video data programmatically
Facebook Video Search Scraper - Description & Hashtags runs as a standard Apify Actor — one API call in, structured JSON dataset back out.
Python example
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("<YOUR_USERNAME>/facebook-video-search-scraper-description-hashtags").call(run_input={"startUrls": ["street food", "ev charging"],"maxVideos": 25,"mustContainHashtags": ["streetfood"],})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row.get("rowType") == "video":print(row["query"], row["videoUrl"], row["hashtags"])
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 and map query, videoUrl, fullDescription and hashtags straight into spreadsheet columns, or feed the same JSON into a CRM/content-calendar import step through the Apify API.
Is it legal to scrape Facebook video search data?
Yes. This Actor reads public Facebook Watch videos and the captions and hashtags their creators chose to attach — content posted publicly and visible to any logged-out visitor. These are content and business records, not personal profile data, so the obligations that apply are Facebook's terms of service and database/unfair-competition rules around systematic reuse of a compiled dataset, not GDPR-style personal-data rules.
One exception: with includeMentionedPages turned on, a caption may tag a profile, Page or Group by name — a rare event, but where it happens the row carries a name, ID and URL that can identify an individual. This is incidental to the caption data this Actor exists to collect, not its purpose, and it is off by default for that reason.
Consult legal counsel for commercial applications involving bulk storage of personal data.
❓ Frequently asked questions
What if a keyword search returns zero matching videos?
The run does not silently return an empty dataset. It writes a single rowType: "error" accounting row for that keyword, with errorReason set to why (facebook_returned_no_result_edges, no_parsable_videos, all_results_filtered_out_by_mustContainHashtags, or a block/rate-limit reason), and moves on to the next keyword. Accounting rows are never billed.
Can I get the full caption and hashtags together, or just one?
Both come on the same row. fullDescription is the complete caption text and hashtags/hashtagUrls/hashtagIds are the structured tag list read out of it — you don't choose one or the other. extractHashtags and includeFullDescription each control how much extra work goes into filling those fields, not whether they exist on the row.
How accurate is the caption and hashtag data?
The Actor returns the caption and hashtag entities exactly as Facebook's own search response or video page carries them at request time. Accuracy depends on whether the creator has since edited or deleted the caption. Re-run the keyword periodically if you need current data rather than a one-time snapshot.
How many videos can I get per run?
maxVideos accepts 1 to 1000 per keyword. Facebook returns roughly 7-8 results per page, so a large maxVideos means more pages and a longer run. Results are de-duplicated by video ID across pages and across every keyword in the same run, so the same video found under two searched topics is only saved once.
What is hashtagSource and why does it matter?
It records which of two independent mechanisms produced the hashtags list on that row: entity means Facebook's own linkified hashtag entities were present in the search response or recovered from the video page (with stable numeric IDs in hashtagIds); regex means no entity list was available and the tags were read out of the caption text instead (hashtagIds will be null for those); none means no tags were found either way; disabled means extractHashtags was turned off. hashtagsFromText always carries the text-scan read regardless of source, so you can compare the two instead of trusting a silent merge.
Does Facebook Video Search Scraper work with Claude, ChatGPT and AI agent frameworks?
Yes. It is callable as a standard HTTP endpoint through the Apify API, so any agent framework that can make a request — LangChain, CrewAI, n8n, a custom tool definition — can invoke it and receive typed JSON directly.
How does Facebook Video Search Scraper - Description & Hashtags compare to other Facebook video scrapers?
Checked on the Apify Store on 2026-07-26: apify/facebook-video-search-scraper documents a broader field set focused on video metadata (duration, views, owner info, broadcast status) and accepts either a keyword or a Facebook search URL, with PAY_PER_EVENT pricing. powerai/facebook-video-search-scraper documents location and date-range filters alongside keyword search but a narrower output (no structured hashtag entities). Neither competitor's README documents a hashtag-entity mechanism, a caption-recovery hop, or a mustContainHashtags filter — this Actor's caption and hashtag depth, including which of two extraction paths produced each row, is not documented on either listing as of the date checked.
Can I use it without a Facebook account or API key?
Yes. No Facebook account, login, cookie or API key is needed — the Actor searches and reads video pages logged out. The only credential required is your Apify account to run the Actor and read the dataset.
Conclusion
Facebook Video Search Scraper - Description & Hashtags turns a list of topics into a structured dataset of public Facebook videos, each carrying the complete caption and Facebook's own hashtag entities rather than a snippet to re-parse. It is built for content strategists, hashtag researchers and social listening teams who need to know what creators actually say and tag on a topic, with a stable schema and a documented filter (mustContainHashtags) for narrowing results precisely. Start a run from the Apify Console or the Apify API with your keyword list to get the first rows in your dataset.