# Facebook Posts Search Scraper (No Login) (`tropical_thunderstuck/facebook-post-search`) Actor

Search public Facebook posts by keyword. No account, cookies or proxy needed. Fresh posts with text, author, time, links, media and engagement counts. Batch hundreds of keywords in one run.

- **URL**: https://apify.com/tropical_thunderstuck/facebook-post-search.md
- **Developed by:** [Sam Roback](https://apify.com/tropical_thunderstuck) (community)
- **Categories:** Social media, News
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.05 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Facebook Posts Search Scraper (No Login)

Search public Facebook posts by keyword and get fresh results with text, author, time,
links, media and engagement counts. **No Facebook account, no cookies and no proxy needed.**

### Why use it

- **No login, no bans.** It never signs in, so there are no accounts to get checkpointed and no cookies to refresh.
- **Fresh posts.** Combines Facebook's recent-posts filter with an age cutoff, then drops anything older than you ask for.
- **Batch keywords.** Put hundreds of keywords in one run and they're searched in parallel. Far cheaper and faster than one run per keyword.
- **Finds what plain search misses.** Facebook returns nothing for some exact phrases, such as "plane crash" or "bomb threat". The actor can search reworded variants that still match.
- **Drop-in input.** Accepts the single-keyword input used by other Facebook search scrapers (`keyword`, `results_limit`, `since`, `filter_by_recent_posts`), so switching is easy.

### Use cases

- Monitoring breaking news, emergencies and local incidents
- Brand and product mentions
- Market and trend research across languages
- Feeding posts into alerting, dashboards or AI pipelines

### Input

One keyword:

```json
{
    "keyword": "power outage",
    "results_limit": 25,
    "since": "1d"
}
```

Many keywords in one run:

```json
{
    "keywords": ["wildfire evacuation", "power outage", "road closed", "inondation"],
    "results_limit": 25,
    "since": "6h",
    "concurrency": 8
}
```

| Field | Default | What it does |
|---|---|---|
| `keyword` / `keywords` | | Search phrase(s). Any language works. |
| `results_limit` | 25 | Maximum posts per keyword |
| `since` | `1d` | Only keep posts newer than `30m`, `6h`, `1d`, `1w`. Empty means no cutoff |
| `filter_by_recent_posts` | true | Ask Facebook for recent posts |
| `rewrite_blocked_phrases` | true | Search reworded variants of phrases Facebook blanks out |
| `max_pages` | 4 | Pages fetched per keyword (about 10 posts per page) |
| `concurrency` | 8 | Keywords searched at the same time |
| `active_until` | | Optional end date: runs started after it exit without searching |

### Output

One row per post:

```json
{
    "input": "power outage",
    "search_query": "power outage",
    "post_id": "1234567890123456",
    "create_time": 1791850927000,
    "text": "Crews are working to restore power to about 2,000 customers...",
    "post_url": "https://www.facebook.com/examplepage/posts/pfbid0abc...",
    "author_username": "Example Electric Cooperative",
    "author_user_id": "100000000000000",
    "author_profile_url": "https://www.facebook.com/examplepage",
    "image_list": ["https://scontent.xx.fbcdn.net/..."],
    "video_list": [],
    "type": "photo",
    "like_count": 18,
    "comment_count": 7,
    "share_count": 3
}
```

`create_time` is Unix time in milliseconds. `search_query` differs from `input` when a
blocked phrase was reworded.

Each run also saves a summary to the `OUTPUT` record in its key-value store: posts per
keyword, pages fetched, posts dropped for being too old, and any errors.

### Tips

- **Batch your keywords.** One run with 200 keywords costs a fraction of 200 single-keyword runs.
- **Scheduling.** For monitoring, schedule a run every 5 to 30 minutes and de-duplicate on `post_id`.
  Repeat searches mostly return new posts mixed in with ones you've seen.
- **Results order.** Facebook ranks results by relevance within the time window, not strictly newest first.
- **Memory.** 1 GB is the sweet spot. Smaller runs are slower for about the same cost.

### Limits

- Only public posts that Facebook shows to logged-out visitors are returned.
- Some phrases are blocked outright and have no working rewrite, for example anything containing "stab" or "suicide".
- Group-only, private and friends-only posts are never included.
- Facebook can change its site at any time. If results drop, the actor will be updated.

### Is it legal to scrape Facebook?

This actor only collects publicly visible posts and does not log in. You are responsible
for how you use the data, including complying with Facebook's terms, data-protection laws
such as GDPR, and any rules that apply to personal data in your jurisdiction. If in doubt,
get legal advice.

# Actor input Schema

## `keyword` (type: `string`):

One search phrase. Use Keywords for several.

## `keywords` (type: `array`):

Search phrases, one per line. A single run handles many keywords in parallel, which is much cheaper than one run per keyword.

## `results_limit` (type: `integer`):

Maximum distinct posts kept per keyword. A page holds about 10.

## `since` (type: `string`):

Only keep posts newer than this: 30m, 6h, 1d or 1w. Leave empty for no age cutoff (results are then relevance-ranked and can be months old).

## `filter_by_recent_posts` (type: `boolean`):

Ask Facebook for recent posts. Combined with the age cutoff this gives the freshest results.

## `rewrite_blocked_phrases` (type: `boolean`):

Facebook returns nothing for some exact phrases, such as 'plane crash' or 'bomb threat'. When on, those are searched as reworded variants ('aircraft crash', 'threat bomb') that still return matching posts.

## `max_pages` (type: `integer`):

Upper bound on requests per keyword (per rewrite variant).

## `concurrency` (type: `integer`):

Keywords searched at the same time.

## `sessions` (type: `integer`):

Separate anonymous browsing sessions. Raise only if you see errors at high concurrency.

## `min_wait_time_in_sec` (type: `integer`):

Random pause between pages of one keyword, lower bound.

## `max_wait_time_in_sec` (type: `integer`):

Random pause between pages of one keyword, upper bound.

## `active_until` (type: `string`):

Optional ISO timestamp (UTC), such as 2026-12-31T00:00:00Z. Runs started after it exit immediately without searching, so a schedule can't keep spending after an end date.

## `function` (type: `string`):

Accepted for compatibility with other Facebook scrapers. Only facebook_search_scraper is supported.

## `strict_search` (type: `boolean`):

Accepted for compatibility with other Facebook scrapers; has no effect.

## `cookies` (type: `array`):

Accepted for compatibility with other Facebook scrapers; ignored, because this actor never logs in.

## Actor input object example

```json
{
  "keywords": [
    "wildfire evacuation",
    "power outage"
  ],
  "results_limit": 25,
  "since": "1d",
  "filter_by_recent_posts": true,
  "rewrite_blocked_phrases": true,
  "max_pages": 4,
  "concurrency": 8,
  "sessions": 1,
  "min_wait_time_in_sec": 0,
  "max_wait_time_in_sec": 1
}
```

# Actor output Schema

## `posts` (type: `string`):

One item per post: keyword, post id, time (epoch ms), text, link, author, media and reaction, comment and share counts.

## `summary` (type: `string`):

Totals for the run and, per keyword, posts kept, pages fetched, posts dropped as older than the cutoff, and errors.

# 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 = {
    "keywords": [
        "wildfire evacuation",
        "power outage"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tropical_thunderstuck/facebook-post-search").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 = { "keywords": [
        "wildfire evacuation",
        "power outage",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tropical_thunderstuck/facebook-post-search").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 '{
  "keywords": [
    "wildfire evacuation",
    "power outage"
  ]
}' |
apify call tropical_thunderstuck/facebook-post-search --silent --output-dataset

```

## MCP server setup

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

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/p7eTzpvl4yD9Zpv9l/builds/LzuLDCfc6oLchYoUc/openapi.json
