Skool All-in-One API
Pricing
from $5.00 / 1,000 results
Skool All-in-One API
The most complete Skool API on Apify. Read AND write posts, comments, members and more. Automatic authentication with cookie caching. Perfect for AI agents and community automation.
Pricing
from $5.00 / 1,000 results
Rating
5.0
(1)
Developer
Cristian Tala S.
Actor stats
2
Bookmarked
21
Total users
11
Monthly active users
6.1 days
Issues response
15 hours ago
Last modified
Categories
Share
The most complete Skool automation on Apify. Full read AND write access to posts, comments, members, and more. The only Skool actor with write capabilities.
What's new in 0.2.0 (Apr 2026): Expected errors (bad credentials, missing params, not-found, rate-limit, etc.) are now returned as structured failure payloads in the dataset without marking the run as
FAILED. This keeps your integrations clean and only flags real bugs. See the Error Handling section.
What makes this different
- Read AND Write — Create posts, reply to comments, approve members. Not just scraping.
- Fast cookie-based auth — Login once (~10s), then reuse cookies for all calls (~2s each).
- Nested comments — Full comment trees with replies, not flat lists.
- User mentions — Tag users with
[@Name](obj://user/{id}). - AI-agent friendly — Clean
action:operationformat, perfect for n8n, OpenClaw, Claude, and other agents.
How much does it cost?
| Scenario | Estimated cost |
|---|---|
| Login (once every ~3.5 days) | ~$0.02 (Playwright) |
| List 1 page of posts (~32 posts) | ~$0.005 |
| Get comments on a post | ~$0.005 |
| Create a post | ~$0.01 (write fee) + ~$0.005 |
| Reply to a comment | ~$0.01 (write fee) + ~$0.005 |
| Approve 10 pending members | ~$0.10 (write fees) + ~$0.005 |
Platform compute costs (~$0.002/run) included. Write operations have premium pricing.
Recommended Flow (Fast)
Step 1: Login once — get cookies
{"action": "auth:login","email": "your@email.com","password": "your-password","groupSlug": "your-community"}
Output:
{"success": true,"cookies": "auth_token=eyJ...; client_id=abc...; aws-waf-token=xyz...","expiresAt": "2026-03-30T06:00:00.000Z","expiresInDays": 3.5,"note": "Pass this cookies string in subsequent calls to skip Playwright login."}
Step 2: Use cookies for all operations (fast, ~2s each)
{"action": "posts:list","cookies": "auth_token=eyJ...; client_id=abc...; aws-waf-token=xyz...","groupSlug": "your-community","params": { "page": 1 }}
Step 3: When cookies expire (~3.5 days) → login again
If you get an error about expired auth, simply run auth:login again.
Alternative: email + password every time (slower)
You can skip Step 1 and pass email + password directly in any action. This uses Playwright every time (~10s instead of ~2s), but it's simpler for one-off operations.
{"action": "posts:list","email": "your@email.com","password": "your-password","groupSlug": "your-community","params": { "page": 1 }}
Input Reference
| Field | Required | Description |
|---|---|---|
action | Always | What to do: auth:login, posts:list, posts:createComment, etc. |
groupSlug | Always | Community slug from URL: skool.com/{slug} |
cookies | Option A | Cookie string from auth:login. Fast, no browser. |
email | Option B | Skool email. Uses Playwright, slower. Required for auth:login. |
password | Option B | Skool password. Required for auth:login. |
params | Varies | Action-specific parameters (see below). |
Actions Reference
Authentication
| Action | Auth | Description |
|---|---|---|
auth:login | email + password | Login via Playwright, returns cookies for reuse. Run once every ~3.5 days. |
Posts (read)
| Action | Params | Description |
|---|---|---|
posts:list | page? (default 1), sortType? | List posts from community feed |
posts:get | postId | Get single post by ID |
posts:getComments | postId | Get comment tree with nested replies |
Posts (write — $0.01 per operation)
| Action | Params | Description |
|---|---|---|
posts:create | title, content, labelId? | Create new post |
posts:update | postId, title?, content? | Edit post or comment |
posts:delete | postId | Delete post or comment |
posts:pin | postId | Pin post to top |
posts:unpin | postId | Unpin post |
posts:vote | postId, vote ("up" or "") | Like or unlike |
posts:createComment | content, rootId, parentId | Reply to post or comment |
Members (read)
| Action | Params | Description |
|---|---|---|
members:list | page? | List active members |
members:pending | — | List pending approval requests |
Members (write — $0.01 per operation)
| Action | Params | Description |
|---|---|---|
members:approve | memberId | Approve pending member |
members:reject | memberId | Reject pending member |
members:ban | memberId | Ban member |
Content Format
Posts and comments use plain text, NOT HTML.
CORRECT: "Hello world! Great post."WRONG: "<p>Hello world! Great post.</p>"
Mentioning Users
Tag users in posts and comments:
[@Display Name](obj://user/{userId})
Example: Hey [@John Smith](obj://user/abc123def456...)! Welcome.
Get user IDs from members:list.
Important: Posts = Comments
In Skool, posts and comments are the same object.
- Reply to a post:
rootId = postId,parentId = postId - Reply to a comment (nested):
rootId = postId,parentId = commentId - Edit a comment: use
posts:updatewith the comment's ID - Delete a comment: use
posts:deletewith the comment's ID
There is no comments: namespace. Everything is posts:.
Example Workflows
Auto-approve pending members
1. auth:login → save cookies2. members:pending → get list of pending members3. For each: members:approve with memberId
Reply to unanswered posts
1. auth:login → save cookies2. posts:list → filter by commentCount === 03. For each: posts:createComment with rootId = postId, parentId = postId
Create post with user mention
{"action": "posts:create","cookies": "...","groupSlug": "my-community","params": {"title": "Weekly Update","content": "Great work this week [@John Smith](obj://user/abc123...)! Keep it up.","labelId": "category-id"}}
Get full comment thread for a post
{"action": "posts:getComments","cookies": "...","groupSlug": "my-community","params": { "postId": "32-char-hex-post-id" }}
Returns nested tree:
[{"id": "comment-1","content": "Great post!","replies": [{ "id": "reply-1", "content": "Thanks!", "replies": [] }]}]
Output
Post object
{"id": "32-char-hex","title": "Post Title","content": "Plain text content","author": { "id": "...", "firstName": "John", "lastName": "Smith", "slug": "john-smith" },"createdAt": "2026-03-26T18:00:00Z","likes": 5,"commentCount": 12,"isPinned": false,"url": "https://www.skool.com/community/post-slug"}
auth:login output
{"success": true,"cookies": "auth_token=...; client_id=...; aws-waf-token=...","expiresAt": "2026-03-30T06:00:00Z","expiresInDays": 3.5}
Error Handling
Starting in v0.2.0, the actor distinguishes between recoverable/expected errors (bad credentials, resource not found, rate limit, etc.) and unhandled bugs.
- Expected errors: the run completes with status
SUCCEEDEDin Apify and pushes a structured failure payload to the dataset. Your integration can inspect the first item to decide next action. - Unhandled bugs: the run fails hard (Apify
FAILEDstatus) so they are visible to the maintainer and can be fixed.
This change means that a caller passing wrong credentials, a bad postId, or hitting a rate limit is no longer charged with a "failed" run in the actor's public stats. The error is delivered cleanly to your dataset.
Failure payload shape (dataset)
{"success": false,"action": "posts:createComment","error": "post not found: 3bc910b1","errorCode": "NOT_FOUND","errorCategory": "not_found","statusCode": 404,"retryable": false,"hint": "Verify the ID provided in params. If it was valid before, the resource may have been deleted."}
Error categories
errorCategory | Meaning | Typical retryable | What to do |
|---|---|---|---|
input_validation | Missing/bad params (no postId, no action, etc.) | false | Fix the actor input and rerun |
auth_error | Login failed, captcha, bad password, AUTH_ERROR, WAF_EXPIRED | true | Re-run auth:login to get fresh cookies |
not_found | NotFoundError from Skool (post/member not found) | false | Verify the ID; resource may have been deleted |
rate_limited | 429 from Skool | true | Back off and retry after a few minutes |
skool_api_error | 4xx from Skool other than above (e.g. "cannot update to same role", missing labelId) | usually false | Inspect error and fix params; if 5xx, retry later |
scraping_error | BuildIdStaleError when Skool dashboard HTML changed | true | Re-run auth:login to refresh buildId |
Recognising a failure in your caller
const items = await fetchDatasetItems(runId);const first = items[0];if (first.success === false) {console.error(`[${first.errorCategory}/${first.errorCode}] ${first.error}`);if (first.retryable) {// back off, refresh auth, or retry — based on errorCategory} else {// surface to user, adjust input}return;}// success path
Common errors → how to fix
Error message / errorCode | Cause | Solution |
|---|---|---|
INPUT_VALIDATION "Missing required params.postId" | Missing field | Add the field in params |
AUTH_ERROR "Login failed — still on login page after 30s" | Wrong password, WAF challenge, or Skool rate-limited the login | Verify credentials; try again from a browser first |
WAF_EXPIRED | Cookies older than ~3.5 days | Re-run auth:login |
BUILDID_STALE | Skool deployed a new dashboard | Re-run auth:login |
NOT_FOUND | Invalid postId / memberId | Double-check the ID source |
RATE_LIMIT | Hit 20-30 writes/min ceiling | Wait 60-120s, then retry |
SKOOL_API_ERROR (400) "cannot update to same role" | Member already has that role | No-op: skip |
SKOOL_API_ERROR (422) "must select category" | Community requires labelId | Pass labelId in params |
Integration Examples
n8n Workflow
- HTTP Request node →
auth:login→ save cookies to variable - HTTP Request node →
posts:listwith cookies → process posts - IF node → filter unanswered posts
- HTTP Request node →
posts:createCommentwith cookies
Apify API (JavaScript)
// Login onceconst loginRun = await fetch('https://api.apify.com/v2/acts/cristiantala~skool-all-in-one-api/runs?token=YOUR_TOKEN', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ action: 'auth:login', email: '...', password: '...', groupSlug: '...' })}).then(r => r.json());// Get cookies from datasetconst items = await fetch(`https://api.apify.com/v2/actor-runs/${loginRun.data.id}/dataset/items?token=YOUR_TOKEN`).then(r => r.json());const cookies = items[0].cookies;// Use cookies for fast operationsconst listRun = await fetch('https://api.apify.com/v2/acts/cristiantala~skool-all-in-one-api/runs?token=YOUR_TOKEN', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ action: 'posts:list', cookies, groupSlug: '...', params: { page: 1 } })}).then(r => r.json());
Roadmap
- Posts CRUD (create, read, update, delete)
- Comments with nesting (reply to posts and comments)
- Members (list, pending, approve, reject, ban)
- User mentions
- Cookie-based fast auth
- Courses (classroom) — read and create lessons
- Events — list, create, RSVP
- Analytics — engagement, revenue, member growth
- Chat / DMs
- Search
- File uploads
- "Send email to all members" toggle
Technical Details
- Built on
skool-jsTypeScript library - Reads use Next.js SSR data endpoints (fast, no browser)
- Writes use
api2.skool.comREST endpoints - Playwright only for
auth:login(cookie extraction) - Rate limiter: 60 reads/min, 30 writes/min
- Auto-retry on transient errors with exponential backoff