# Facebook Search Scraper - Public Posts by Keyword (`dami_studio/facebook-search-scraper`) Actor

Search public Facebook posts by keyword, without signing in. One row per post: text, author, group, link, exact time, photos and videos, and reaction, comment, share and view counts. One search ends after 65 to 100 posts; add dates to get hundreds more, or ask for recent posts or videos only.

- **URL**: https://apify.com/dami_studio/facebook-search-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Social media, Marketing, Lead generation
- **Stats:** 5 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.44 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Search public Facebook posts by keyword and get one row per post**: the text, who posted it and in which group, the link, the exact time, the media and the reaction, comment, share and view counts.

Type a word or phrase and the actor returns the public posts Facebook's own search finds for it, without signing in. The catch: Facebook ends one search after about 65 to 100 posts. Give it a date window and it goes through the dates a week or so at a time, which is how you get hundreds of posts for a busy topic.

| | |
|---|---|
| **Input** | Search terms, plus optional dates, recent posts or videos only |
| **Output** | One row per post: text, author, group, link, time, media type, image and video links, reactions, comments, shares, video views |
| **Ceiling** | About 65 to 100 posts per term for a plain search; up to 1,000 per term with a date window; 100 terms a run |
| **Speed** | About 160 to 190 posts a minute in test runs |
| **Account needed** | None from you |
| **Price** | $0.46 per 1,000 posts, flat on every plan. The free plan's $5 a month covers about 10,800 posts |

### 🔍 What Facebook Search Scraper does

It searches Facebook's public posts the way the search box does for someone who isn't signed in: posts from Pages, public groups and public profiles. Each search term is its own search.

Every post is checked before it goes into your dataset:

- **The words.** A post is kept when every word you searched appears in its text, its author's name or its group's name. Facebook also returns looser matches, and those are left out unless you switch the check off.
- **The dates.** Facebook applies a date window loosely, and on some searches its Recent posts filter mixes in old posts. Each post's time is checked: anything outside your dates, or older than a week with recent posts, is dropped.
- **Videos.** With videos only, anything that isn't a video post is dropped.

When Facebook has nothing that matches, it still returns posts, just unrelated ones: a search for gibberish gets about ten. So if none of the first page's posts contain your words, the search stops there with a free note.

### 📋 What data you get from each Facebook post

| What you get | Field |
|---|---|
| The post's id and link | `postId`, `url` |
| When it was posted, in UTC and as a Unix time | `time`, `timestamp` |
| The text | `text` |
| Who posted it | `authorName`, `authorId`, `authorUrl` |
| The public group it was posted in, if any | `groupName`, `groupId`, `groupUrl` |
| What it carries | `mediaType` (`photo`, `album`, `video`, `link`, `text`, `shared post`), `isReel` |
| Images, largest first | `imageUrls` |
| The video's page, cover and length | `videoUrl`, `videoThumbnailUrl`, `videoDurationSeconds` |
| The post a share points to | `sharedPostUrl` |
| Reactions, comments, shares, video views | `reactionsCount`, `commentsCount`, `sharesCount`, `videoViewsCount` |
| Which search found it, and its place in that search | `searchTerm`, `position` |
| Whether every searched word is in it | `containsSearchWords` |

### ▶️ How to scrape Facebook posts by keyword

1. Open [Facebook Search Scraper](https://apify.com/dami_studio/facebook-search-scraper) and click **Try for free**.
2. Type one or more words or phrases into **Search terms**, one per line.
3. Set **Posts per search term**. For more than about 100 posts on one term, fill in **Posted from**.
4. Tick **Recent posts** or **Videos only** if you want them.
5. Click **Start**, then download the rows as JSON, CSV or Excel, or read them through the Apify API.

Start it with every field empty to see the row shape first: one labelled sample post, not charged.

### 💰 How much does it cost to scrape Facebook posts?

**$0.46 per 1,000 posts.** Flat on every Apify plan, no volume tiers. On the free plan, the $5 Apify gives you each month covers about 10,800 posts.

You pay for posts that come back. The sample row and every note row are free, and so are posts the checks left out (missing words, outside your dates, not a video) and posts that turn up twice. If you set a maximum charge for the run, it stops before the first post it couldn't pay for.

### 📥 What you give it

```json
{
  "searchTerms": ["electric car", "coffee"],
  "maxPostsPerSearch": 300,
  "startDate": "2026-08-01",
  "endDate": "2026-09-30"
}
```

| Field | Default | What it is |
|---|---|---|
| `searchTerms` | none, the box starts at `electric car` | Words or phrases to search for, one per line, in any language. Up to 100. |
| `maxPostsPerSearch` | `50` | The most posts from one term, 1 to 1,000. A plain search ends after about 65 to 100. |
| `recentPosts` | `false` | Facebook's Recent posts filter, checked: only posts from the last 7 days are kept. Can't be combined with dates. |
| `startDate` | none | Only posts from this day on, as `YYYY-MM-DD`, read in UTC. |
| `endDate` | none | Only posts up to and including this day. With only a start date, the window runs to today. |
| `videosOnly` | `false` | Only video posts, reels included. |
| `onlyPostsWithEveryWord` | `true` | Keep only posts where every searched word appears in the text, the author's name or the group's name. Off returns everything Facebook's search does, marked with `containsSearchWords`. |

Dates have to be `YYYY-MM-DD`. Anything else, like "last week", stops the run before it searches, with a free note saying what to write. So do a start date in the future, a start after the end, and recent posts together with dates.

### 📤 What you get back

A real row from a test run on 3 October 2026 (search "coffee"), image link shortened:

```json
{
  "recordType": "post",
  "postId": "1083580981083414",
  "url": "https://www.facebook.com/firstbloomcoffee/posts/pfbid0AbckUBKoknuamg6NpvW9rjTVBHfTxBWjsg9jaLM2VV1GYdWch6sP8YNWzzoUV3j6l",
  "text": "👻 Announcing Cleveland’s first ever coffee crawl!\n\n🎃 Mark your calendars for the last week of October when all your favorite coffee shops in town will be featuring a special $5 drink!\n\nIt’s like trick-or-treating for grown-ups. 😜",
  "time": "2026-10-01T14:29:12.000Z",
  "timestamp": 1790864952,
  "authorName": "First Bloom Coffee and Bakery",
  "authorId": "100082945672195",
  "authorUrl": "https://www.facebook.com/firstbloomcoffee",
  "groupName": null,
  "groupId": null,
  "groupUrl": null,
  "mediaType": "photo",
  "isReel": false,
  "imageUrls": ["https://scontent-iad3-1.xx.fbcdn.net/v/t39.99422-6/828831765_...n.png?..."],
  "videoUrl": null,
  "videoThumbnailUrl": null,
  "videoDurationSeconds": null,
  "sharedPostUrl": null,
  "reactionsCount": 871,
  "commentsCount": 223,
  "sharesCount": 279,
  "videoViewsCount": null,
  "searchTerm": "coffee",
  "position": 5,
  "containsSearchWords": true,
  "scrapedAt": "2026-10-03T16:28:56.833Z"
}
```

| Field | Worth knowing |
|---|---|
| `authorUrl` | `null` on about one post in seven, mostly people posting in groups; `authorId` is always there |
| `authorName` | "Anonymous participant" on anonymous group posts |
| `text` | `null` on the rare photo or video posted without a caption |
| `groupName`, `groupId`, `groupUrl` | `null` unless the post was made in a public group |
| `imageUrls`, `videoThumbnailUrl` | Facebook's own links; they stop working after a few days, so save the files you need |
| `videoViewsCount` | Only on video posts |
| `reactionsCount` | All reactions together, not split by type |
| `position` | The order this search returned the post in, after the checks |

### 🧾 Reading the output

Every real post has `recordType: "post"`. Filter on that and you have only the posts you paid for.

An empty run gives one sample row with `_sample: true` and `recordType: "sample"`. Notes have `_diagnostic: true`, `ok: false`, an `errorCode` and a plain `message`, and are never charged:

| `errorCode` | Means |
|---|---|
| `NO_MATCHES` | Facebook returned posts, but none of the first ones contain your words, so the search stopped |
| `NO_RESULTS` | Nothing to return: no posts, or none inside your dates, none from the last week with recent posts, or none that were videos |
| `BAD_INPUT` | A setting or term couldn't be used; the message says what to change |
| `NOT_ANSWERED` | Facebook didn't answer that search this time; try it again later |
| `PARTIAL` | Facebook stopped answering partway through a term; the posts before that are in the dataset |
| `NOT_REACHED` | The run stopped (time, your maximum charge, or Facebook not answering) before this term |
| `SEARCH_UNAVAILABLE` | Facebook's post search didn't accept the request at all; nothing was searched |
| `MAX_CHARGE_TOO_LOW` | Your maximum charge doesn't cover a single post |

The key-value store also holds `RUN_REPORT`: each term's status, posts returned, pages read and what the checks left out.

### 💡 What people use it for

- **Hearing what people say about a brand or product.** A shop owner searching their own name finds the group posts nobody tagged them in.
- **Topic research.** What people post about electric cars, a diet or a new phone, with the counts showing which posts landed.
- **Daily monitoring.** Recent posts on a schedule, keeping only post ids you haven't seen.
- **Finding the groups where a topic lives.** `groupName` across a few hundred posts shows which public groups talk about it most.

A recipe with our other Facebook actors: search a topic here, keep the posts with the most `commentsCount`, then pass their `url` values to the Facebook Comments Scraper to read the conversation under each.

### 🚧 What it does not do

- No private posts, private groups or friends-only posts. Only what Facebook shows people who aren't signed in.
- A plain search ends after about 65 to 100 posts, however many exist. Dates are the way past that.
- Results lean towards what a visitor in the United States sees, so English words bring mostly American posts.
- No photos-only filter. Facebook doesn't show photo results to people who aren't signed in.
- With dates or recent posts, videos only keeps the video posts among the normal results, so it reads more pages for fewer posts.
- No comments, only their count. Those come from the [Facebook Comments Scraper](https://apify.com/dami_studio/facebook-comments-scraper).
- No follower counts or contact details for the authors. Those come from the [Facebook Pages Scraper](https://apify.com/dami_studio/facebook-pages-scraper).

### 🧭 Which Facebook scraper do you need?

| If you want | Use |
|---|---|
| Public posts about a word or phrase, from anywhere on Facebook | This one |
| Every recent post of Pages you already know | [Facebook Posts Scraper](https://apify.com/dami_studio/facebook-posts-scraper) |
| The posts inside a public group | [Facebook Groups Scraper](https://apify.com/dami_studio/facebook-groups-scraper) |
| The comments under posts or reels you found | [Facebook Comments Scraper](https://apify.com/dami_studio/facebook-comments-scraper) |
| Name, likes and followers of the Pages behind the posts | [Facebook Pages Scraper](https://apify.com/dami_studio/facebook-pages-scraper) |
| Events about a topic or in a city | [Facebook Events Scraper](https://apify.com/dami_studio/facebook-events-scraper) |
| The video file of a reel or video post | [Facebook Video Downloader](https://apify.com/dami_studio/facebook-video-downloader) |

### ❓ Questions people ask

#### Do I need a Facebook account to search Facebook posts?

No. It reads the public posts Facebook shows people who aren't signed in. Nothing of yours is used and nothing is posted.

#### Why are the posts different from what I see when I search on Facebook?

Your own search is shaped by your friends, groups, language and country. A run sees what Facebook shows an anonymous visitor, leaning towards one in the United States. A private window won't let you check, because Facebook doesn't open its search page there, but every post link in the dataset does open in one.

#### How do I get more than about 100 posts for one search term?

Fill in **Posted from**, and **Posted until** if you want an end, then raise **Posts per search term**. The window is searched in slices, newest first, and each slice brings its own posts. In a test, "pizza" from January to September 2026 reached the 1,000-post cap within the window's last 19 days, every post inside the dates.

#### Why did my search return a note instead of posts?

`NO_MATCHES` means Facebook's results didn't contain your words, which is what it does when it has nothing on a topic. `NO_RESULTS` means nothing was left after your dates, the recent check or the videos filter. Neither is charged.

#### Can I call it from code or connect it to an AI assistant?

Yes. The [API tab](https://apify.com/dami_studio/facebook-search-scraper/api/python) has code for Python, JavaScript and the command line. For Claude, ChatGPT or another MCP client, connect `https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/facebook-search-scraper`. Either way it runs on your Apify account at the same price.

#### Is scraping Facebook posts legal?

It reads public posts only. Names and posts are still personal data under laws such as GDPR, and Facebook has its own terms, so have a reason and read them. Apify's [write-up on scraping and the law](https://blog.apify.com/is-web-scraping-legal/) is a starting point; we are not lawyers.

### 🆘 If something breaks

Open an issue on the Issues tab with the run link and the search terms you used. A note row's `errorCode` and `message` help too.

# Actor input Schema

## `searchTerms` (type: `array`):

Words or phrases to search Facebook's public posts for, one per line, in any language. Each term is a separate search. Up to 100 terms a run.

## `maxPostsPerSearch` (type: `integer`):

Stop a search term after this many posts. A plain search ends after about 65 to 100 posts, because that is as far as Facebook's results go for one search. Set a date window to go further: the window is searched a week or so at a time, and each slice brings its own posts.

## `recentPosts` (type: `boolean`):

Facebook's own Recent posts filter, checked: only posts from the last 7 days are kept. Facebook's recent results are mostly from the last day or two, but on some searches it mixes in older posts; those are left out and not charged. It can't be combined with a date window.

## `startDate` (type: `string`):

Only posts from this day on, written as YYYY-MM-DD. Leave both dates empty for no date limit. Facebook applies dates loosely, so every post is checked and the ones outside your dates are left out and not charged. Dates are read in UTC.

## `endDate` (type: `string`):

Only posts up to and including this day, written as YYYY-MM-DD. With only "Posted from" set, the window runs up to today.

## `videosOnly` (type: `boolean`):

Only video posts, reels included. Without other filters it searches Facebook's video results; with dates or recent posts it keeps the video posts among the normal results, so fewer come back. Photos-only isn't offered: Facebook doesn't show photo results to people who aren't signed in.

## `onlyPostsWithEveryWord` (type: `boolean`):

On: a post is kept only when every word you searched appears in its text, its author's name or the name of the group it was posted in. Off: every post Facebook's search returns, marked with containsSearchWords. Either way, a search whose first results contain none of the words stops there with a free note instead of returning unrelated posts. Posts left out are never charged.

## Actor input object example

```json
{
  "searchTerms": [
    "electric car"
  ],
  "maxPostsPerSearch": 50,
  "recentPosts": false,
  "videosOnly": false,
  "onlyPostsWithEveryWord": true
}
```

# Actor output Schema

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

One row per public post. Free rows marked \_diagnostic say when a search found nothing, found only unrelated posts, or couldn't run.

## `report` (type: `string`):

Each search term with its status, the posts returned, the pages read and the posts left out by the checks, and why the run stopped.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchTerms": [
        "electric car"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/facebook-search-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "searchTerms": ["electric car"] }

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/facebook-search-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "electric car"
  ]
}' |
apify call dami_studio/facebook-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/facebook-search-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5dehu6e2Pf3rmxmlO/builds/mmfK6HKo7wY436ahR/openapi.json
