Facebook Posts Search Scraper: Engagement & Metadata Extraction
Pricing
from $5.99 / 1,000 results
Facebook Posts Search Scraper: Engagement & Metadata Extraction
π Facebook Posts Search Scraper extracts public Facebook posts by keywords or hashtagsβcapturing text, author, date, URL, reactions, shares, comments & media. π Ideal for social listening, brand monitoring, trend analysis & market research. β‘ Fast, reliable CSV/JSON export.
Pricing
from $5.99 / 1,000 results
Rating
0.0
(0)
Developer
Scrapier
Maintained by CommunityActor stats
0
Bookmarked
26
Total users
5
Monthly active users
17 days ago
Last modified
Categories
Share
Facebook Posts Search Scraper β Posts, Engagement & Contact Leads
Facebook Posts Search Scraper: Engagement & Metadata Extraction searches public Facebook posts by keyword and returns structured post records β engagement metrics, post text, and page identifiers β for any term you supply. It can also enrich each discovered page with public contact details and fetch top-level comments into a separate dataset. Every response is structured JSON, ready to pass to an LLM, index into a vector store, or feed a monitoring pipeline. Give it a keyword and see what Facebook actually surfaces for it.
What is Facebook Posts Search Scraper: Engagement & Metadata Extraction?
It is a keyword-search Actor: you give it one or more search terms, it discovers matching public Facebook posts (via a site:facebook.com search), and it extracts each post's engagement numbers, text, and page identity. Its real differentiator is what happens after discovery β it can compute a contact-quality lead score for each post's page and write a deduplicated, ranked lead list to a separate Key-Value Store record, instead of just repeating the same page's contact block on every matching post. No Facebook account or login is required β it reads only pages a logged-out visitor can already see.
- Keyword-driven post discovery β every entry in
searchTermsruns as its own independent search - Engagement metrics per post β likes, comments, and shares parsed straight from the post
- Optional page contact enrichment β email, phone, website, address pulled from each page's own public contact card
- Contact-quality lead scoring β a 0β4 completeness score and a quality tier, deduplicated into one row per page
- Optional top-level comment collection into a separate, uncharged dataset
- Country and language targeting for the underlying search, plus a post-age filter
What data can you get with Facebook Posts Search Scraper: Engagement & Metadata Extraction?
The Actor writes three distinct result types depending on which options are enabled: a scored post record for every discovered post, an optional child comment record for each fetched comment, and an optional deduplicated lead record per page.
| Result Type | Extracted Fields | Primary Use Case |
|---|---|---|
| Facebook post | facebookUrl, pageId, pageName, facebookId, postId, postFacebookId, url, topLevelUrl, time, timestamp, likes, comments, shares, text, link, thumb, searchQuery, contact, contactCompleteness, contactQuality, commentsFetched, scrapedAt | Keyword trend research, engagement tracking, content sourcing |
| Comment (child dataset) | parentPostId, parentPostUrl, commentId, feedbackId, commentUrl, commentDate, commentText, authorName, authorId, authorProfileUrl, authorProfilePicture, likesCount, repliesCount, threadingDepth, facebookCommentId, keyword, isChild, type, scrapedAt | Sentiment sampling, audience reaction analysis |
Lead (Key-Value Store record LEADS) | pageId, pageName, facebookUrl, contact, contactCompleteness, contactQuality, postsMatched, samplePostUrl, searchQueries | Lead generation, outreach prioritization |
Contact-quality lead scoring and deduplicated leads
When enableContactLookup is on, every post's page is fetched once (cached across posts from the same page) and its public email/phone/website/address are extracted. Because the same page often surfaces through several matching posts, attaching contact to every post row would repeat the same lead many times. This Actor instead deduplicates by page at the end of the run and writes one row per page to the Key-Value Store record LEADS, ranked by contactCompleteness descending β so a lead-generation user gets a clean, ranked list instead of N duplicate contact blobs. A row looks like:
{"pageId": "104958773168472","pageName": "Riverside FC Fan Page","facebookUrl": "https://www.facebook.com/riversidefcfans","contact": { "email": "info@riversidefc-fans.example", "phone": "+44 20 7946 0958", "website": "https://riversidefc-fans.example", "address": null },"contactCompleteness": 3,"contactQuality": "moderate","postsMatched": 4,"samplePostUrl": "https://www.facebook.com/riversidefcfans/posts/998211","searchQueries": ["football"]}
Child comments dataset
When maxCommentsPerPost is greater than 0, the Actor fetches each post's top-level comments and writes them to a separate, uncharged dataset named comments-<runId>. Each comment carries its own author, text, like count, reply count, and a parentPostId linking it back to the post that surfaced it β useful for sampling audience reaction on a keyword without paying for every individual comment as a billed row.
How does Facebook Posts Search Scraper: Engagement & Metadata Extraction differ from the official Facebook API?
Meta's Graph API does not expose an endpoint for arbitrary keyword-based search across public posts β it is built around objects you already have an ID and access token for, not open-ended discovery.
| Feature | Meta Graph API | This Scraper |
|---|---|---|
| Keyword-based public post discovery | Not supported β no public post-search endpoint | Yes, one search per searchTerms entry |
| Access requirements | App review, access token, and page-level permissions | No Facebook login or developer app |
| Engagement metrics on arbitrary posts | Only for pages/posts you manage or that grant access | Returned for any discovered public post |
| Page contact/lead extraction | Not part of the API surface | Optional, via enableContactLookup |
| Comment retrieval | Requires a page token and permissions | Optional, via maxCommentsPerPost |
| Output shape | Raw per-endpoint JSON you must assemble yourself | Normalized dataset rows + child dataset + lead KV store |
The Graph API is the right tool when you already manage the Page or App and only need data you're authorized for. This Actor is the fit when the input is a keyword, not a known Page, and you want whatever public posts and pages actually match it.
How to scrape Facebook with Facebook Posts Search Scraper: Engagement & Metadata Extraction?
- Open the Actor on its Apify Store page and click Run
- Enter one or more terms in
searchTermsβ every field is optional, so leaving it blank runs the built-in default (football) - Set the real query controls you need:
maxResultsPerQuery,postAgeFilter,targetCountry,targetLanguage,enableContactLookup,maxCommentsPerPost - Start the run
- Read results from the default dataset as JSON or CSV, the
comments-<runId>dataset if comments were enabled, and theLEADSKey-Value Store record if contact lookup was enabled
{"searchTerms": ["digital marketing agency", "tech news"],"maxResultsPerQuery": 25,"targetCountry": "us","enableContactLookup": true}
How to run multiple queries in one job
searchTerms accepts an array β each term becomes its own independent search, and the Actor runs all of them concurrently (asyncio.gather over one task per term) instead of one after another. Posts from every term land in the same dataset, tagged with searchQuery so you can split the results back out per keyword afterward.
β¬οΈ Input
| Parameter | Required | Type | Description | Example Value |
|---|---|---|---|---|
searchTerms | No | array | Keywords to search for on Facebook. Each term runs as its own search. Accepts the legacy searchQueries key too. | ["football", "tech news"] |
maxResultsPerQuery | No | integer | Posts to collect per search term (1β5000, default 10). Accepts the legacy maxPosts key too. | 25 |
postAgeFilter | No | string | Only keep posts published within this window: "" (all time), 24h, 7d, 30d, 90d. Accepts the legacy postTimeRange key too. | "7d" |
targetCountry | No | string | Biases the underlying Google search toward a country (SERP gl param). Blank = no bias. | "us" |
targetLanguage | No | string | Language for the underlying Google search (SERP hl param). Blank = English default. | "en" |
enableContactLookup | No | boolean | When on, fetches each post's page and extracts public email/phone/website/address into contact. Off by default. | true |
maxCommentsPerPost | No | integer | Top-level comments to fetch per post into the uncharged comments-<runId> dataset (0β500). 0 disables it. | 20 |
proxyConfiguration | No | object | Proxy settings for the Facebook page requests. | { "useApifyProxy": true } |
No parameter is required β an empty input still runs against the built-in default search term.
Example input:
{"searchTerms": ["digital marketing agency"],"maxResultsPerQuery": 25,"postAgeFilter": "30d","targetCountry": "us","targetLanguage": "en","enableContactLookup": true,"maxCommentsPerPost": 10,"proxyConfiguration": { "useApifyProxy": true }}
Common pitfall: contact, contactCompleteness, and contactQuality are always present on every row, but they stay empty/0/null unless enableContactLookup is explicitly set to true β leaving it off (the default) will not produce a LEADS record at all, even if your search terms return plenty of posts.
β¬οΈ Output
Results are typed, normalized JSON with a consistent schema across runs, exportable as JSON or CSV from the dataset. Some fields are best-effort β extracted from post HTML with regex patterns β so a field can legitimately be null when Facebook's markup didn't expose it for a given post.
Scraped results
[{"facebookUrl": "https://www.facebook.com/riversidefcfans","pageId": "104958773168472","pageName": "Riverside FC Fan Page","facebookId": "104958773168472","postId": "998211004821377","postFacebookId": "998211004821377","url": "https://www.facebook.com/riversidefcfans/posts/998211004821377","topLevelUrl": "https://www.facebook.com/riversidefcfans/posts/998211004821377","time": "2026-07-18 14:02:10","timestamp": 1784476930,"likes": 341,"comments": 58,"shares": 12,"text": "Matchday preview: five things to watch this weekend.","link": "https://www.facebook.com/riversidefcfans/posts/998211004821377","thumb": "https://scontent.fplg1-1.fna.fbcdn.net/v/example.jpg","searchQuery": "football","contact": { "email": "info@riversidefc-fans.example", "phone": "+44 20 7946 0958", "website": "https://riversidefc-fans.example", "address": null },"contactCompleteness": 3,"contactQuality": "moderate","commentsFetched": 10,"scrapedAt": "2026-07-25T09:14:02.113Z"},{"facebookUrl": "https://www.facebook.com/citysportsnetwork","pageId": "220938471029384","pageName": "City Sports Network","facebookId": "220938471029384","postId": "771029384552910","postFacebookId": "771029384552910","url": "https://www.facebook.com/citysportsnetwork/posts/771029384552910","topLevelUrl": "https://www.facebook.com/citysportsnetwork/posts/771029384552910","time": "2026-07-19 08:47:55","timestamp": 1784537275,"likes": 1204,"comments": 190,"shares": 67,"text": "BREAKING: transfer window update for this weekend's fixtures.","link": "https://www.facebook.com/citysportsnetwork/posts/771029384552910","thumb": null,"searchQuery": "football","contact": { "email": null, "phone": null, "website": "https://citysportsnetwork.example", "address": null },"contactCompleteness": 1,"contactQuality": "weak","commentsFetched": 10,"scrapedAt": "2026-07-25T09:14:37.884Z"},{"facebookUrl": "https://www.facebook.com/weekendfootballtalk","pageId": "553012938471029","pageName": "Weekend Football Talk","facebookId": "553012938471029","postId": "119028374652910","postFacebookId": "119028374652910","url": "https://www.facebook.com/weekendfootballtalk/posts/119028374652910","topLevelUrl": "https://www.facebook.com/weekendfootballtalk/posts/119028374652910","time": "2026-07-20 19:03:41","timestamp": 1784632921,"likes": 89,"comments": 14,"shares": 3,"text": "Who's your player of the week? Drop your pick below.","link": "https://www.facebook.com/weekendfootballtalk/posts/119028374652910","thumb": "https://scontent.fplg1-2.fna.fbcdn.net/v/example2.jpg","searchQuery": "football","contact": { "email": null, "phone": null, "website": null, "address": null },"contactCompleteness": 0,"contactQuality": "none","commentsFetched": 10,"scrapedAt": "2026-07-25T09:15:02.221Z"}]
How can I use the data extracted with Facebook Posts Search Scraper: Engagement & Metadata Extraction?
- Trend and topic research: run a keyword across a date window using
postAgeFilterand rank the returned posts bylikes/comments/sharesto see which angles on a topic are actually landing on Facebook right now. - Competitive content analysis: search for a competitor's name or campaign phrase, pull back the pages and posts that mention it, and compare
pageName,text, and engagement fields across every result to see who owns the conversation. - AI engineers and LLM developers: an agent issues a query through
searchTerms, receives structured post JSON back, and passestext,likes,comments, andsharesto a model as grounded context instead of a hallucinated summary. - Social listening: track
searchQueryresults over repeated runs to catch new posts and pages that start mentioning a brand, product, or keyword, usingcontactCompleteness/contactQualityto prioritize which newly discovered pages are worth a follow-up.
How do you monitor Facebook keyword mentions and leads over time?
Monitoring here means re-running the same searchTerms on a schedule and diffing what comes back, rather than treating any single run as a final answer β Facebook's public post index for a keyword changes as new posts get published and old ones fall out of the search window. Between runs, compare postId values to spot newly discovered posts, and diff likes, comments, and shares for posts you've already seen to track how they're still gaining traction. If contact lookup is enabled, compare the LEADS Key-Value Store record run over run: a page moving from contactQuality: "weak" to "strong", or a brand-new pageId appearing in the list, both signal something worth a follow-up.
A concrete loop: schedule a run across your keyword set β pull the new dataset and the LEADS record β diff postId and pageId sets against the previous run's export β alert on any new page whose contactCompleteness clears your threshold, or any post whose likes/comments/shares jumped sharply. Keep each run's dataset export (or copy it to your own storage) so the next run has something concrete to diff against β without a saved prior export, you only ever see the current snapshot, never the delta. The Actor itself has no built-in scheduler or webhook delivery β Apify's platform-level Schedules feature is the mechanism for triggering repeat runs, and downstream diffing has to happen in your own pipeline against the exported dataset.
Integrate Facebook Posts Search Scraper: Engagement & Metadata Extraction and automate your workflow
Facebook Posts Search Scraper: Engagement & Metadata Extraction works with any language or tool that can send an HTTP request.
REST API with Python
import requestsTOKEN = "YOUR_APIFY_TOKEN"ACTOR = "scrapier~facebook-posts-search-scraper-engagement-metadata-extraction"url = f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items"payload = {"searchTerms": ["football"], "maxResultsPerQuery": 20}resp = requests.post(url, params={"token": TOKEN}, json=payload, timeout=180)resp.raise_for_status()posts = resp.json()for post in posts[:3]:print(post["pageName"], post["likes"], post["comments"])
MCP for query-grounded AI agents
Apify actors, including this one, are reachable through Apify's official @apify/actors-mcp-server, which exposes a chosen actor as a callable tool over MCP:
npx -y @apify/actors-mcp-server --actors=scrapier/facebook-posts-search-scraper-engagement-metadata-extraction
An agent in a compatible client β Claude Desktop, Claude Code, Cursor, or any other MCP-aware client β can then issue a keyword query, receive the structured post JSON, and ground its answer in results that were actually returned, instead of guessing.
Scheduled monitoring and delivery
The Actor has no built-in scheduler or webhook push. Apify's platform-level Schedules feature can trigger repeat runs on a cron-style interval, and Apify's Webhooks can notify an external endpoint when a run finishes so you can pull the fresh dataset from there.
Is it legal to scrape Facebook post search results?
Scraping publicly accessible Facebook posts is generally lawful β this Actor returns only what any logged-out visitor can already see, nothing behind a login wall. Post authors' names and any extracted page contact details (email, phone, address) are personal data, so processing them falls under GDPR in the EU/UK and CCPA/CPRA in California: you need a lawful basis to store or reuse them, and data-subject rights (access, deletion) may apply. Facebook's own Terms of Service separately restrict automated data collection, which is a contractual matter distinct from the legality of scraping public data. Scraping for one-off research carries a different risk profile than storing contact details in bulk for outreach. Consult your legal team for commercial use cases involving bulk data storage.
Frequently asked questions
Can I target results toward a specific country or language?
Yes β targetCountry biases the underlying Google search toward a country's results (the gl SERP parameter), and targetLanguage sets the search UI/results language (hl). Leaving either blank reproduces the base behavior: no country bias, English.
How do I limit results to recent posts only?
Set postAgeFilter to 24h, 7d, 30d, or 90d. The filter is enforced against each post's actual extracted timestamp before it's saved β a post with no discoverable timestamp is skipped entirely rather than assumed to be in range.
How does this Actor handle Facebook's anti-bot measures?
It routes search discovery through Apify's GOOGLE_SERP proxy group and page/comment fetches through Apify Proxy (with a residential proxy specifically for the comments endpoint), sends browser-like request headers, and retries failed fetches with backoff before giving up on a URL.
Does it extract contact-quality leads?
Yes, when enableContactLookup is on. Each post row gets contactCompleteness (0β4) and contactQuality, and a deduplicated, ranked version β one row per page instead of one per post β is written to the Key-Value Store record LEADS. With the option off, these fields are 0/null and no LEADS record is produced.
How many results does it return per query?
Up to maxResultsPerQuery per search term, configurable from 1 to 5,000 (default 10). This is a per-term cap set in the input schema, not a platform-wide limit β running five terms at 100 each can return up to 500 posts total.
How do I monitor a keyword's Facebook mentions over time?
Schedule repeat runs of the same searchTerms using Apify's Schedules feature, then diff the exported dataset's postId values against the previous run to find new posts, and diff likes/comments/shares on posts you've already seen to track how they're still moving.
Does it work with Claude, ChatGPT, and AI agent frameworks?
Yes. It's callable as a plain HTTP endpoint by any agent framework, and it's also reachable through Apify's @apify/actors-mcp-server, which lets MCP-aware clients like Claude Desktop or Claude Code call it directly as a tool.
How does this differ from other Facebook scrapers in the same catalog?
This Actor is search-driven: the input is a keyword, and it discovers whichever public posts and pages happen to match. A page-driven Facebook scraper instead takes a specific Page URL and analyzes that page's own timeline β a fundamentally different starting point that answers "how is this page doing" rather than "who is talking about this keyword."
Can I use this without managing proxies or a Facebook account?
Yes. Apify Proxy is used by default for every request this Actor makes, and no Facebook login or developer account is required β it only reads pages a logged-out visitor can already see.
Your feedback
Found a bug or a field that doesn't match what's in this README? Let us know through the Actor's Issues tab on Apify Store or your Apify Console support channel β reports like this directly shape what gets fixed next.