# Facebook Group Finder with Fit Score (`kingly_contest_7cb/facebook-group-finder`) Actor

Find public Facebook groups for your niche by keyword. Get member count, posts per day and month, description, privacy and a transparent 0-100 fit score, so community marketers can see where their audience is active. No Facebook login; group-level public data only.

- **URL**: https://apify.com/kingly_contest_7cb/facebook-group-finder.md
- **Developed by:** [Carina Hatton](https://apify.com/kingly_contest_7cb) (community)
- **Categories:** Social media, Lead generation, Marketing
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 group results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 Group Finder with Fit Score

Type in a few keywords like `boutique owners` or `keto recipes` and get back a list of **public Facebook groups** about them. Each group comes with its **member count**, **posts per day and per month**, its description, and a **0–100 fit score** that tells you how well it matches your niche.

It's built for **community marketers**, and it's also useful for agencies and coaches who need to find out where their audience already hangs out.

- No Facebook login, cookies or account needed
- Several keywords per run, and multi-word phrases work
- Groups found by more than one keyword show up only once
- The fit score is transparent and deterministic, and every part of it is explained below
- Only group-level metadata is collected, never anything about members (see [Privacy](#privacy))

### How it works

1. **Discovery.** Facebook's own group search only works when you're logged in, so it isn't used. Instead, the Actor runs two search-engine queries for each keyword (`site:facebook.com/groups "keyword"` and `"keyword" facebook group`) and reads several result pages. It keeps every link that points to `facebook.com/groups/...`.
2. **Group details.** For each group it opens the group's public **About** page through a residential proxy. Facebook shows that page to logged-out visitors. From it the Actor reads:
   - name, privacy, member count and post activity
   - description, topics, locations and creation date
3. **Filter and score.** It applies your privacy and minimum-members filters, drops groups that don't match any keyword, calculates the fit score and saves each group.

### Input

| Field                    | What it does                                                                                                                                                                                                                             | Default                                   |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `keywords`               | Topics to search. Phrases are fine.                                                                                                                                                                                                      | `boutique owners`, `shopify store owners` |
| `maxResultsPerKeyword`   | Maximum number of groups to return per keyword                                                                                                                                                                                           | 20                                        |
| `privacyFilter`          | `PUBLIC` returns public groups only. `ANY` also includes private groups, whose size, activity and description Facebook still shows publicly.                                                                                             | `PUBLIC`                                  |
| `minMembers`             | Skips groups smaller than this. Groups with an unknown size are also skipped when this is set.                                                                                                                                           | empty                                     |
| `nicheDescription`       | Optional sentence about your audience or offer. It feeds 30% of the relevance score.                                                                                                                                                     | empty                                     |
| `requireKeywordMatch`    | Keeps a group only if its name, description or topics contain every *distinctive* word of the keyword it was found for (see [Fit score](#fit-score-0100)). You aren't charged for skipped groups.                                        | on                                        |
| `searchEngine`           | How groups are found (see below).                                                                                                                                                                                                        | `google_actor`                            |
| `maxSearchPagesPerQuery` | Number of result pages read per query (2 queries per keyword). Leave it empty for automatic: 3 pages when `maxResultsPerKeyword` is 10 or less, 5 pages above that. More pages can find more groups but cost more (see [Costs](#costs)). | auto                                      |
| `proxyConfiguration`     | Proxy used for Facebook requests. Keep it on Residential.                                                                                                                                                                                | Residential                               |
| `healthCheck`            | Monitoring mode (see [Health check](#health-check))                                                                                                                                                                                      | off                                       |

**Search engine options**

- `google_actor` calls Apify's *Google Search Scraper* Actor. This is the recommended default. That run is started in your Apify account and billed separately (see [Costs](#costs)).
- `google` uses Google through Apify Proxy's Google SERP group. It doesn't start an extra Actor, but in testing it was slower and found fewer groups.
- `duckduckgo` is free but often blocked by DuckDuckGo's bot check.

If the chosen engine finds nothing for a keyword, the Actor tries DuckDuckGo once.

Example:

```json
{
    "keywords": ["boutique owners", "shopify store owners"],
    "maxResultsPerKeyword": 20,
    "privacyFilter": "PUBLIC",
    "minMembers": 1000,
    "nicheDescription": "online clothing boutique owners looking for more customers"
}
```

### Output

You get one dataset item per group. Any value Facebook doesn't show publicly is `null`; nothing is estimated or guessed. This example is built from the "Foodie" group's public About page as it was fetched on Oct 30, 2026, scored for the keywords `["foodie", "food lovers"]`. The description is shortened here, and `discoveredVia` and `scrapedAt` are illustrative:

```json
{
    "groupId": "330526660347434",
    "name": "Foodie",
    "url": "https://www.facebook.com/groups/330526660347434/",
    "privacy": "PUBLIC",
    "visibility": "VISIBLE",
    "memberCount": 269784,
    "memberCountIsApproximate": false,
    "memberCountText": "269,784 total members",
    "postsPerDay": 333.33,
    "postsPerMonth": 10000,
    "postsLastDay": 0,
    "activityText": "No posts today · 10.0K in the last month",
    "activityTextSource": "page_text",
    "activityStatus": "active",
    "description": "We Are not food geek, but We really love to eat...... This group is made out of love for food. ...",
    "location": null,
    "topics": null,
    "createdDate": "2012-04-17",
    "createdText": "Group created on April 17, 2012",
    "matchedKeywords": ["foodie"],
    "fitScore": 98,
    "fitScoreBreakdown": { "relevance": 40, "size": 22.6, "activity": 25, "accessibility": 10 },
    "keywordRelevance": [
        { "keyword": "foodie", "score": 1 },
        { "keyword": "food lovers", "score": 0.32 }
    ],
    "nicheCoverage": null,
    "discoveredVia": "google_actor",
    "scrapedAt": "2026-10-30T22:00:00.000Z"
}
```

Field notes:

- **`postsPerMonth`** is Facebook's own "posts in the last month" number. **`postsPerDay`** is `postsPerMonth / 30`, rounded to 2 decimals.
- **`activityText`** is Facebook's visible wording when the page includes it (`activityTextSource: "page_text"`). Otherwise it's spelled out from Facebook's own post counts (`"structured_data"`). If Facebook shows no activity at all, the counts are `null` and the text says "Activity not shown publicly by Facebook" (`"not_shown"`).
- **`activityStatus`** is `active` when Facebook reports posts in the last month, `dormant` when Facebook itself reports 0 posts in the last month, and `unknown` when it shows no activity. A `0` is only ever Facebook's own figure, never a guess.
- **`memberCountIsApproximate`** is `true` only when Facebook showed a rounded figure such as "104.6K members" and no exact count.
- **`location`** lists the places the group names in its About section, or `null` when there are none.
- **`createdDate`** is the creation date Facebook shows on the About page.

You can export the results as CSV, Excel or JSON from the **Groups** view, or from **All fields**. A `DIAGNOSTICS` record in the key-value store explains every run, including how many pages were read, blocked or filtered.

**A run never "succeeds" with zero results.** If nothing is found, the run fails with a status message that says why: the search was blocked, no groups were found, Facebook showed a login wall, or everything was filtered out. You aren't charged for results on those runs.

### Costs

A run has two separate costs:

1. **This Actor:** **$0.008 per group** saved to the dataset (the `group-result` event, $8 per 1,000 groups), plus Apify's standard Actor start event ($0.00005 per run at the default 1 GB memory). Filtered, failed or skipped groups aren't charged. Platform usage of this Actor's own run is included in the price.
2. **Google Search Scraper (only with `searchEngine: google_actor`, the default):** each run starts one run of [apify/google-search-scraper](https://apify.com/apify/google-search-scraper) **in your own Apify account**. It's billed to you separately, at that Actor's own pay-per-event price, and shows up as its own run in your Console.

The Google part depends on the number of result pages: **pages = keywords × 2 queries × pages per query**. With the automatic page setting, that's 6 pages per keyword when you ask for up to 10 groups per keyword, and 10 pages per keyword above that.

Approximate Google Search Scraper cost per keyword, from its price list as shown on Oct 30, 2026 ($0.0045 per page on the Free plan, $0.0025 on Bronze, plus $0.00005 per run). Check its Pricing tab for current prices.

| Groups per keyword               | Pages per keyword | Free plan    | Bronze       |
| -------------------------------- | ----------------- | ------------ | ------------ |
| up to 10 (3 pages per query)     | 6                 | about $0.027 | about $0.015 |
| more than 10 (5 pages per query) | 10                | about $0.045 | about $0.025 |

Measured in our tests on Oct 30, 2026 (Bronze): 3 keywords at 5 groups each used 18 Google pages ($0.045); 1 keyword at 20 groups used 10 pages ($0.025). Search results change over time, so these are examples, not guarantees.

To keep costs predictable, set a maximum cost per run when you start the Actor. The Actor runs with 1 GB of memory by default (maximum 2 GB); it doesn't need more.

### Fit score (0–100)

The fit score is the sum of four parts. It's deterministic: the same group data and input always give the same score.

| Part              | Points | Formula                                                                                                             |
| ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| **Relevance**     | 0–40   | `40 × R` (see below)                                                                                                |
| **Size**          | 0–25   | `25 × min(1, log10(members + 1) / 6)`. 1,000,000 members earns the full 25.                                         |
| **Activity**      | 0–25   | `25 × min(1, log10(postsPerMonth + 1) / log10(301))`. 300 or more posts a month (about 10 a day) earns the full 25. |
| **Accessibility** | 0–10   | Public = 10, Private but visible = 4, otherwise 0                                                                   |

**How R is calculated.** Words are lower-cased, simple plurals are trimmed and common filler words are ignored ("group", "community", "the", and so on). Each keyword's words are then split into **distinctive** words and **generic** ones such as "recipes", "owners", "store", "business", "tips" or "beginners". In "keto recipes" the distinctive word is "keto"; in "shopify store owners" it's "shopify". If a keyword is made only of generic words, all of them count as distinctive.

- **Each keyword gets a score from 0 to 1:**
  - **0 unless every distinctive word appears** somewhere in the group's name, description or topics. A shared generic word alone, like "recipes", doesn't count.
  - 1.0 if the whole keyword phrase appears in the group **name**
  - otherwise 0.75 if the phrase appears in the **description or topics**
  - otherwise `0.6 × (weighted share of keyword words in the name) + 0.4 × (weighted share in the description and topics)`, where distinctive words weigh 1 and generic words 0.25
- **Combining keywords:** `K` is the best score across your keywords.
- **Without a niche description:** `R = K`.
- **With a niche description:** `R = 0.7 × K + 0.3 × min(1, 2 × N)`, where `N` is the share of your niche words found in the group's name, description and topics.

If the member count or the post count is `null`, that part scores 0; it's never estimated. The output includes `fitScoreBreakdown` and a per-keyword `keywordRelevance`, so you can see exactly where each score came from.

The score is a sorting aid, not a quality judgement. A small group with high relevance can be a better fit for you than a huge general one.

### Privacy

This Actor only collects **group-level public metadata**, meaning the information Facebook shows about the group itself on its public About page to anyone who isn't logged in.

- It does **not** collect member lists, admins or moderators, post authors, commenters, profile links, or any other personal data.
- It does **not** log in, use cookies, or join groups.
- It does **not** read the contents of posts. Activity comes only from Facebook's aggregate counts.

You're responsible for how you use the results. Respect each group's rules and Facebook's terms, and don't spam groups.

### Health check

Set `"healthCheck": true` to run three fixed keywords: `boutique owners`, `shopify store owners` and `keto recipes`, with up to 3 groups each and privacy set to `ANY`.

In this mode the Actor saves **no** dataset items, so there are no result charges. It writes a `HEALTH_CHECK` record (`ok`, `emptyKeywords`, sample rows and stats) and **fails** if any keyword returns zero groups. You can schedule it daily on Apify and alert on failed runs.

### FAQ

**Why not use Facebook's own group search?** It needs a logged-in account, and logged-out requests to it return "not found". This Actor never uses an account.

**Why do I need a residential proxy?** Facebook shows a login wall to datacenter IPs and to any IP that views more than a few pages. In our tests, datacenter proxy requests hit the login wall on every group page we tried, while residential requests got through. With a residential proxy, every retry uses a new IP.

**Why does a very busy group show exactly 10,000 posts a month?** Facebook seems to cap or round very high activity figures (it shows "10.0K"). Treat 10,000 as "10,000 or more". This hasn't been fully verified.

**Why does a big group show 0 posts a month?** Because Facebook's own About page says so: it shows "No posts today" and "No posts in the last month", and its post counts are 0. These groups are flagged `activityStatus: "dormant"`. Many are large but inactive, or have been renamed. If Facebook shows no activity at all, the counts are `null` instead.

**Does it find every group on a topic?** No. It finds the groups that search engines have indexed, so niche or new groups can be missing. To find more, add related keywords such as "boutique owners", "boutique wholesale" and "online boutique", or raise `maxSearchPagesPerQuery`.

**Why are some groups not about my keyword?** Search engines sometimes return loosely related groups. `requireKeywordMatch` (on by default) removes groups that don't contain your keyword's distinctive words ("keto" in "keto recipes"), and the relevance part of the score ranks the rest.

**Are private groups included?** Only when `privacyFilter` is `ANY`. Facebook still shows the size, activity and description of private groups publicly, but you'd need to join before you could post.

**How am I charged?** You're charged $0.008 per group saved to the dataset (the `group-result` event), plus a $0.00005 Actor start event per run. Groups that are filtered out, failed or skipped aren't charged, and the run stops at your maximum cost per run. With `searchEngine: google_actor`, the Google Search Scraper run is started in your account and billed separately. See [Costs](#costs).

**What if a run finds nothing?** It fails with a clear message. See the `DIAGNOSTICS` record for details.

# Actor input Schema

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

Topics to find groups for. Multi-word phrases are fine (e.g. "boutique owners"). Each keyword is searched separately; groups found by several keywords appear once.

## `maxResultsPerKeyword` (type: `integer`):

Stop after this many groups (that pass your filters) for each keyword. You pay per group returned.

## `privacyFilter` (type: `string`):

PUBLIC = only public groups (anyone can see posts). ANY = also include private groups (their member count, activity and description are still shown publicly by Facebook).

## `minMembers` (type: `integer`):

Skip groups smaller than this. Leave empty for no limit. Groups with unknown member count are skipped when this is set.

## `nicheDescription` (type: `string`):

Optional. A sentence describing your audience or offer, e.g. "women's clothing boutique owners who sell online and want more customers". Its words count for 30% of the relevance part of the fit score.

## `requireKeywordMatch` (type: `boolean`):

Search engines sometimes return unrelated groups. When on, a group is kept only if its name, description or topics contain every distinctive word of the keyword it was found for (generic words like 'recipes', 'owners' or 'store' don't count on their own). Skipped groups are not charged.

## `searchEngine` (type: `string`):

How groups are discovered (Facebook's own group search needs a login). google_actor = calls the apify/google-search-scraper Actor (recommended; its run, a few cents per keyword set, is billed to your account separately). google = Google via Apify's Google SERP proxy (no extra Actor run, but slower and found fewer groups in testing). duckduckgo = free, but often rate-limited.

## `maxSearchPagesPerQuery` (type: `integer`):

Each keyword runs 2 search queries; this caps how many result pages (10 results each) are read per query. Leave empty for automatic: 2 pages when you ask for up to 5 groups per keyword, 3 pages up to 10, 5 pages above that. Each page costs a Google Search Scraper page event, so more pages cost more.

## `maxConcurrency` (type: `integer`):

Keep low to avoid Facebook rate limits.

## `proxyConfiguration` (type: `object`):

Facebook shows a login wall to IPs that view many pages. Residential proxy gives each request a new IP.

## `healthCheck` (type: `boolean`):

For monitoring only: ignores keywords and runs 3 fixed keywords (boutique owners, shopify store owners, keto recipes), max 3 groups each, pushes no dataset items and fails if any keyword returns nothing. Result is in the HEALTH_CHECK key-value record.

## Actor input object example

```json
{
  "keywords": [
    "boutique owners"
  ],
  "maxResultsPerKeyword": 3,
  "privacyFilter": "PUBLIC",
  "requireKeywordMatch": true,
  "searchEngine": "google_actor",
  "maxSearchPagesPerQuery": 2,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "healthCheck": false
}
```

# Actor output Schema

## `groups` (type: `string`):

No description

## `allFields` (type: `string`):

No description

## `diagnostics` (type: `string`):

No description

# 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": [
        "boutique owners"
    ],
    "maxResultsPerKeyword": 3,
    "maxSearchPagesPerQuery": 2,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kingly_contest_7cb/facebook-group-finder").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": ["boutique owners"],
    "maxResultsPerKeyword": 3,
    "maxSearchPagesPerQuery": 2,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("kingly_contest_7cb/facebook-group-finder").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": [
    "boutique owners"
  ],
  "maxResultsPerKeyword": 3,
  "maxSearchPagesPerQuery": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call kingly_contest_7cb/facebook-group-finder --silent --output-dataset

```

## MCP server setup

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

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/eRcz3zPDrTAE8gkr0/builds/iSOH0fKnnC7g8mnQO/openapi.json
