# Skool Community Scraper (`dami_studio/skool-group-scraper`) Actor

Paste Skool community links and get one row each: name, description, member count, price, owner, rating and content counts. You supply the links; it does not search Skool.

- **URL**: https://apify.com/dami\_studio/skool-group-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Lead generation, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.80 / 1,000 community scrapeds

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

## Skool Community Scraper

Start with the limit, because it decides whether this is any use to you: **you supply the community links.** Paste them in and you get one row back for each: name, description, how many members, whether it costs money and how much, who runs it, the rating and the amount of content inside. It does not search Skool and it cannot find communities for you. If what you need is a list of communities matching a topic, this is the wrong tool and you should not buy it. If you already have the links, it turns them into a clean table. No account, no cookies, no login, no browser.

- Up to 500 community links per run, one row each.
- A link that turns out to be dead, renamed, a member profile or a Skool site page comes back as a free row saying exactly which, and is never charged.
- The price is read from the field the join button actually quotes, not the first one that looks like money. Checked against 30 live communities: 20 show a price on the button, and all 20 matched.
- No members. Member lists, names and profiles are not collected, not read and not written.
- Empty input returns one labelled sample row, free, so you can see the shape before spending anything.

### Price

**$2.80 per 1,000 communities**, plus a **$0.0005 start fee per run**.

This is a flat rate on every plan, free or paid. There are no volume tiers, no minimum spend, no subscription and no add-on fees. What you read here is what you pay on day one and on day four hundred.

| Communities | Total cost |
|---|---|
| 1 | $0.0033 |
| 10 | $0.0285 |
| 100 | $0.2805 |
| 500 | $1.4005 |

A run reads up to 500 communities, so those totals are what a single run costs. Bigger jobs split across runs and pay the start fee once per run.

#### What is actually charged

- **One `community-scraped` event per community row written to the dataset.** Nothing else is metered per row.
- **Free:** the sample row an empty run returns, and every diagnostic row: a blocked target, a dead URL, a search that matched nothing. Those rows all carry `"charged": false`.
- A community that no longer exists costs nothing. The check that finds out is about a kilobyte.
- Duplicate links in the same run are folded together before anything is read, so pasting the same community twice is charged once.
- If the first 25 links all fail and nothing has come back, the run stops there and tells you, rather than grinding through the other 475.
- A run that finds nothing costs the start fee and nothing more.
- Rows never leave the dataset without a charge, and are never charged without a row.

### Input

```json
{
  "groupUrls": [
    "https://www.skool.com/makerschool",
    "https://www.skool.com/skoolkit",
    "adcreatorslab"
  ],
  "maxItems": 50
}
```

| Field | What it does |
|---|---|
| `groupUrls` | The community links, one per line. A full link works, and so does the bare name at the end of it. Up to 500 per run. |
| `maxItems` | A spending cap. The run stops after this many communities even if you pasted more links, and the ones it skipped are never charged. Leave it alone to read everything you supplied. Hard ceiling 500. |
| `proxyUrls` | Leave empty. Fill it in only if you want the traffic to leave through proxy servers you already pay for, as `http://user:pass@host:port`. |

Run it with **empty input** and you get one clearly labelled sample row, free, so you can see the output shape before you spend anything.

### Output

One row per community. A real row from a real run:

```json
{
  "ok": true,
  "charged": true,
  "recordType": "community",
  "groupUrl": "https://www.skool.com/skoolkit",
  "slug": "skoolkit",
  "name": "SkoolKit | Skool CRM Toolkit",
  "description": "Skool CRM tools for Skool community owners: schedule posts, recover failed payments, and auto-DM members. All right inside Skool.",
  "memberCount": 962,
  "isPaid": true,
  "priceLabel": "$2.00 per month",
  "priceAmount": 2,
  "priceCurrency": "usd",
  "priceInterval": "month",
  "annualPriceAmount": null,
  "hasFreeTrial": false,
  "ownerHandle": "markmars",
  "ownerName": "Mark Mars",
  "ownerUrl": "https://www.skool.com/@markmars",
  "reviewCount": 57,
  "reviewAverage": 5,
  "adminCount": 2,
  "onlineMemberCount": 66,
  "postCount": 338,
  "courseCount": 2,
  "moduleCount": 18,
  "ruleCount": 3,
  "aboutText": "Skool CRM tools for Skool community owners.\n\nRun your community on autopilot and get your evenings back.",
  "logoUrl": "https://assets.skool.com/f/7b0107e600b2446ba033d109dc00f881/63309a73524d48009ccba069902602dd7b5ed552ed1646fcbd79c3ed8d3b30b6.jpg",
  "coverImageUrl": "https://assets.skool.com/f/7b0107e600b2446ba033d109dc00f881/82a6ebcab72045719914639adb99d07ceb1fd219ffc745e897732484ef4ee7f3.jpg",
  "groupId": "7b0107e600b2446ba033d109dc00f881",
  "createdAt": "2026-07-06T12:51:27.723842Z",
  "updatedAt": "2026-09-20T09:13:50.018909Z",
  "scrapedAt": "2026-09-20T09:45:31.144Z"
}
```

#### Field notes

- `memberCount`: total members at the moment the row was read. `onlineMemberCount` is how many were online right then, so it moves minute to minute.
- `isPaid`: true when joining costs money. A community counts as free only when Skool carries no price for it at all.
- `priceAmount`: in whole currency units, so 99 means ninety-nine, not ninety-nine cents. `priceLabel` is the same figure written out.
- `priceInterval`: `month` or `year`, matching what the join button says.
- `annualPriceAmount`: the yearly price when the community also sells one. Often absent, and absent does not mean there is no annual option, only that Skool did not publish one here.
- `hasFreeTrial`: true when the community is running a trial. Those show START FREE TRIAL on the button instead of the price, so the price in the row is what you pay once the trial ends.
- `ownerHandle`: the owner's public Skool handle, and `ownerUrl` is their profile. Nothing else about them is collected.
- `reviewAverage`: the published rating, 1 to 5. Null on communities that have not been rated, which is not the same as zero.
- `postCount`: posts written in the community since it opened. `courseCount` and `moduleCount` are how much classroom material sits inside it: the counts only, never the content.
- `aboutText`: the long write-up the community shows people deciding whether to join. `description` is the short one-liner.
- `slug`: the last part of the link, and a stable key to join on when you re-run.

Every real row carries `"charged": true`. Sample rows carry `"_sample": true` and diagnostic rows carry `"_diagnostic": true` with an `errorCode` you can filter on, and neither is ever billed.

### How it works

- It reads the community page Skool serves to anyone, and takes the structured record the page is built from rather than picking text out of the rendered layout. That record carries more than the page shows.
- There is a lighter, data-only version of the same reply, and the run uses it for every community after the first. Measured on three communities back to back: about 23% fewer bytes and roughly half the time.
- The address that lighter reply lives at changes whenever Skool ships an update, so it is worked out fresh at the start of every run and never hardcoded. If Skool ships mid-run, the run notices and carries on.
- Every reply is checked against the community that was asked for before a row is written. A link that quietly forwards somewhere else is reported, not billed. One Skool address answers 200 and hands back an entirely different community.
- Rate limits on the target are handled automatically.

### What people use it for

- Sizing up a list of communities before joining any of them: members, price and rating side by side beats opening forty tabs.
- Tracking the same set of communities weekly and diffing on `memberCount` and `priceAmount` to see who is growing and who just raised their price.
- Building a directory or newsletter about a niche, where you already keep the links and need the numbers kept current.
- Checking your own community against the ones you consider peers, on the numbers rather than on impressions.
- Research on the creator-community economy: the rows are flat and typed, so they load into a spreadsheet without cleaning.

### What it will not do

Worth being blunt about, because it is the difference between this being useful and being a waste of your money.

- **It does not find communities.** There is no search, no topic browse, no "give me the top ten fitness communities". You bring the links; it fills in the facts. A run with no links returns a free sample row and stops.
- **It does not return members.** No member lists, no member names, no member profiles. The payload a community page is built from does contain people (reviewers, user ids, free-text review bodies), and none of it is read or written. You get the review count and the average, which are numbers about the community, not about people.
- **It does not go inside.** Posts, comments, chat, classroom lessons and videos are all out of scope. You get how many courses and modules exist, not what is in them.
- **There is no category field, and that is not an oversight.** Skool sorts communities into nine categories on its own browse page, but a community's own record carries no category, no tag and no label. Thirty communities were checked, plus ninety browse entries, plus the page meta tags: nothing. Rather than guess one from the description, the field is simply not there.

### Reading the output

Every run writes three kinds of row and they are easy to tell apart.

- **Real rows** carry `"charged": true` and `"recordType": "community"`. One billed event each.
- **The sample row** carries `"_sample": true` and `"charged": false`. There is exactly one, only when the input had no links, and it exists so you can look at the output before you spend anything.
- **Diagnostic rows** carry `"_diagnostic": true`, `"charged": false` and an `errorCode` you can switch on:

| `errorCode` | What happened |
|---|---|
| `NOT_FOUND` | No community at that link. Closed, renamed, or a typo. |
| `REDIRECTED` | The link forwards to a different community. The row names which one so you can re-run it. |
| `NOT_A_GROUP` | The link is a Skool page rather than a community. |
| `WRONG_GROUP` | Skool answered with a different community than the one asked for. |
| `BAD_INPUT` | The link is a member profile, a Skool site page, or not a Skool link at all. |
| `INPUT_LOOKS_WRONG` | 25 links in a row failed and nothing had come back, so the run stopped early. |
| `LIMIT_REACHED` | Your `maxItems` cap stopped the run. One row for the whole remainder, with a `skipped` count. |
| `TIME_BUDGET` | The run ran out of time before reaching this link. |
| `BLOCKED` / `RATE_LIMITED` / `NETWORK` | Skool would not answer. Worth re-running. |

If you only want data, filter on `charged == true`. That count always equals the number of events you were billed for, so the dataset is its own invoice.

### Limitations

- You supply the links. There is no discovery, no search and no topic browse, and a run with no links returns a free sample row rather than going looking.
- No category field. Skool does not attach one to a community record, so there is nothing to return.
- No members, no posts, no comments and no classroom content, by design.
- Member, post and online counts are a snapshot at read time and will have moved by the time you read them.
- Every price seen so far has been in US dollars. A community priced in another currency will carry that currency code, but none has turned up to confirm how it renders.
- A community running a free trial shows START FREE TRIAL on its join button instead of a price. The row still carries the subscription price, which is what you pay when the trial ends, and `hasFreeTrial` tells you that is the situation.
- Skool carries more than one price field and they can disagree. The row uses the current billing price, which is the one the join button quotes. One community in the sample of thirty had an older field showing $19 while its button said $47.
- A rating and a review count are missing on communities nobody has rated. Null there means unrated, not zero.
- A link that forwards to another community is reported and not read, so you are never charged for a community you did not ask for. Re-run it with the link the diagnostic row names.
- Hard ceiling of 500 communities per run. Larger jobs split across runs.
- If the first 25 links all fail and nothing has been returned, the run stops and says the input looks wrong instead of working through the rest. Once a single community has come back, that check is off for the rest of the run, so a list that merely starts with dead links still gets read in full.
- It reads what Skool shows a logged-out visitor. Anything a community keeps behind its join wall stays there.

### Questions

**Can it find communities for me, by topic or keyword?**

No. This reads community links you already have. There is no search and no browse. If finding them is the job you need done, this will not do it, and a run with no links returns a free sample row rather than pretending otherwise.

**What happens if a link is dead or the community was renamed?**

You get one uncharged diagnostic row saying which, and the run carries on to your other links. Nothing is billed for a link that returned no community. If the link forwards to a different community, the row names the new one so you can re-run it.

**Does it return the members?**

No. Not their names, not their profiles, not a list of them. You get the member count and the online count, which are numbers about the community. The review count and average rating are aggregates too.

**Is the price reliable?**

It is the figure the community's own join button quotes. That was checked against thirty live communities; twenty of them show a price on the button and all twenty matched. The other ten were running a free trial, so their button offers the trial instead of a price. Those rows carry the subscription price and `hasFreeTrial: true`.

**Can I run this on a schedule?**

Yes. Nothing is held between runs, so the same input is safe to repeat. Join on `slug` to line up this week against last week.

**Do I need a proxy or a Skool account?**

Neither. The run brings its own addresses and the cost is already inside the price. There is no login anywhere in it.

**Will the run fail if something goes wrong?**

No. A dead link, a blocked request or a broken reply produces an uncharged diagnostic row explaining what happened, and the run still finishes as succeeded. A failed run would still bill the start fee, which would mean paying to be told something went wrong.

**How do I get exactly the rows I paid for?**

Filter the dataset on `"charged": true`. Sample and diagnostic rows are always false, and the number of charged rows always equals the number of billed events.

# Actor input Schema

## `groupUrls` (type: `array`):

One link per community, for example https://www.skool.com/makerschool. A bare community name works too. Up to 500 per run. Links that turn out to be member profiles, Skool site pages or dead communities come back as free rows explaining why, and are never charged.

## `maxItems` (type: `integer`):

A spending cap. The run stops after this many communities even if you pasted more links, and the ones it skipped are never charged. Leave it alone to read every link you supplied. Hard ceiling 500.

## `proxyUrls` (type: `array`):

Leave this empty. The run brings its own addresses and the cost of them is already inside the price. Fill it in only if you specifically want the traffic to leave through proxy servers you already pay for, as http://user:pass@host:port.

## Actor input object example

```json
{
  "groupUrls": [
    "https://www.skool.com/makerschool",
    "https://www.skool.com/adcreatorslab"
  ],
  "maxItems": 50
}
```

# Actor output Schema

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

Every row in the default dataset: groupUrl, slug, name, description, memberCount, isPaid, priceLabel, priceAmount, priceCurrency, priceInterval, annualPriceAmount, hasFreeTrial, ownerHandle, ownerName, ownerUrl, reviewCount, reviewAverage, adminCount, onlineMemberCount, postCount, courseCount, moduleCount, ruleCount, aboutText, logoUrl, coverImageUrl, groupId, createdAt, updatedAt. An empty, blocked or unmatched run returns a single uncharged row explaining what happened instead.

# 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 = {
    "groupUrls": [
        "https://www.skool.com/makerschool",
        "https://www.skool.com/adcreatorslab"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/skool-group-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 = {
    "groupUrls": [
        "https://www.skool.com/makerschool",
        "https://www.skool.com/adcreatorslab",
    ],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/skool-group-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 '{
  "groupUrls": [
    "https://www.skool.com/makerschool",
    "https://www.skool.com/adcreatorslab"
  ],
  "maxItems": 50
}' |
apify call dami_studio/skool-group-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/skool-group-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/Nu20SC2vZbsTMz5mL/builds/tUaE1auy6KsNuiUwQ/openapi.json
