Skip to content

Repository files navigation

SkoolApp

A Python client for automating a Skool.com account. It covers community, post, comment, member, and webhook management and ships a small CLI on top.

Why this exists

Skool does not publish a first-party REST API. The options available today are:

SurfaceAuthCoverageStability
Zapier integration (built in to Skool)per-group API keytriggers + a handful of actionsstable
SkoolAPI gatewayhttps://docs.skoolapi.com (community-built)x-api-secret headercommunities, posts, comments, members, chats, webhooks; expandingstable, preferred
Direct skool.com endpoints (reverse-engineered, browser-style session)email + password ⇒ cookie / bearer tokenessentially everything the web UI doesfragile — internal routes change without notice
Apify scrapersthird-partyread-onlyscraper-grade, fine for analytics

skoolapp wraps the middle two options so you can stay on the stable gateway for the 95% case and drop into the direct client only where coverage is missing.

Important: Any automation of a Skool account must comply with Skool's Terms of Service. Use credentials you own or have been given automation rights on, and do not use the direct client to scrape communities you don't belong to.

Installation

pip install -e .[dev]

Python 3.9+.

Quick start (gateway, recommended)

fromskoolappimportSkoolClientwithSkoolClient(api_secret="sk_live_...") asclient:
community=client.communities.get("my-community")
post=client.posts.create(
group=community["id"],
title="Release notes v3",
body="New classroom features shipped today.",
)
forreplyinclient.comments.list(post["id"], limit=20):
print(reply["author"]["name"], reply["body"])
client.webhooks.create(
group=community["id"],
url="https://example.com/skool/webhook",
events=["post.created", "comment.created", "member.joined"],
)

The client reads SKOOLAPI_API_SECRET from the environment by default, so SkoolClient() with no args works once you've exported the secret.

Direct (fallback) client

fromskoolappimportSkoolDirectClientwithSkoolDirectClient(email="you@example.com", password="...") asclient:
client.login()
group=client.get_group("my-community")
foriteminclient.feed("my-community", sort="new", limit=10):
print(item["title"])
client.create_post(
group="my-community",
title="Live from the direct client",
body="Skool hasn't exposed this yet on the gateway.",
)

CLI

Every resource is wired into skoolapp:

export SKOOLAPI_API_SECRET=sk_live_...
skoolapp communities list
skoolapp community show my-community
skoolapp posts list --group my-community --limit 5 --sort new
skoolapp post create --group my-community --title "Hi" --body "Hello"
skoolapp members list --group my-community --role admin
skoolapp webhook create \
--group my-community \
--url https://example.com/webhook \
--event post.created --event comment.created

Output is JSONL for list commands and pretty JSON for single-object commands, so you can pipe it straight into jq.

Webhook verification

fromskoolapp.webhooksimportverify_signatureok=verify_signature(
payload=request.body,
signature_header=request.headers["X-Skool-Signature"],
secret="whsec_...",
)

The header looks like t=<unix>,v1=<hex> and the digest is HMAC-SHA256(secret, f"{t}.{body}"). Signatures older than five minutes are rejected to defang replay attacks.

Architecture

src/skoolapp/
├── __init__.py # public re-exports
├── client.py # SkoolClient (SkoolAPI gateway, x-api-secret)
├── direct.py # SkoolDirectClient (skool.com session login)
├── transport.py # httpx wrapper: retries, pagination, JSON decode
├── errors.py # SkoolAPIError hierarchy
├── webhooks.py # HMAC signature verification
├── cli.py # `skoolapp` CLI entry point
└── resources/
├── sessions.py # POST /v1/sessions/
├── communities.py # /v1/communities
├── posts.py # /v1/posts
├── comments.py # /v1/posts/{id}/comments (comments are posts!)
├── members.py # /v1/members
└── webhooks.py # /v1/webhooks/

Every resource module is independent — the only shared surface is the Transport, which handles retries (exponential backoff on 429/5xx and network errors), cursor pagination, and JSON decoding into typed exceptions.

Testing

pip install -e .[dev]
pytest

Tests use respx to mock HTTP, so no network access is required.

Roadmap

  • Courses / classroom endpoints (SkoolAPI gap — currently only reachable via the direct client).
  • Async client flavor (SkoolAsyncClient) built on httpx.AsyncClient.
  • Typed response models via pydantic once the gateway schemas stabilize.

License

MIT. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages