If you run a community on Skool, you've probably noticed something annoying about the member list: the email field is blank for most members. Not your bug. Skool's server-rendered member list returns email: "" for everyone except the authenticated user. Even community owners get masked emails for third parties when calling GET /users/{id}.
The admin panel has an "Export" button that does give you everything — emails, tiers, LTV, survey answers — but only if you click it manually. For a CRM sync pipeline, that's not enough.
This post shows how to call that same export programmatically via the Skool All-in-One API actor on Apify. One HTTP request, full member roster as JSON, ready to pipe into NocoDB / Airtable / Postgres / anywhere.
It's the foundation of the CRM single-source-of-truth pipeline I run for Cágala, Aprende, Repite (700+ members).
What you'll need
- An Apify account (free tier covers this — the export costs ~$0.05/run)
- Skool admin/owner cookies for your community
- A community where you're admin (export is admin-only)
If you've never used the actor before: it wraps Skool's entire REST surface so you don't have to handle cookies, WAF token rotation, weekly buildId changes, or the 3-step async pattern Skool's export uses internally. One JSON POST → parsed response.
The one-liner that returns your full roster
curl -X POST \
"https://api.apify.com/v2/acts/cristiantala~skool-all-in-one-api/run-sync-get-dataset-items?token=$APIFY_TOKEN&timeout=60&build=latest" \
-H "Content-Type: application/json" \
-d '{
"action": "members:export",
"cookies": "auth_token=...; client_id=...; aws-waf-token=...",
"groupSlug": "your-community",
"params": { "status": "active" }
}'
Response (one item per member, already parsed — no CSV parsing on your side):
[
{
"firstName": "Maria",
"lastName": "Lopez",
"email": "maria@example.com",
"invitedBy": "Cristian Tala",
"joinedDate": "2026-04-12 18:14:56",
"survey": [
{ "question": "What's your LinkedIn?", "answer": "linkedin.com/in/marialopez" },
{ "question": "What are you building?", "answer": "SaaS for restaurants" },
{ "question": "How did you find us?", "answer": "Newsletter" }
],
"tier": "standard",
"ltv": "$0"
}
]
The actor handles the underlying 3-step flow Skool's admin UI runs when you click Export:
-
POST /groups/{id}/request-bulk-action?type=bulk-export-csv→ returns{token, file_id} -
GET /wait?token={token}→ polls untilcompleted(2-8 sec typically) -
POST /files/{file_id}/download-url→ signed CloudFront URL → GET that → CSV body → parse to JSON
You don't see any of that. One call in, parsed JSON out.
Filter what you export
{
"params": {
"status": "active",
"tiers": ["premium", "vip"]
}
}
| Param | Values | Default |
|---|---|---|
status |
active / cancelling / churned / banned
|
active |
tiers |
array of your community's tier slugs | all tiers |
Useful for cohort analysis: "all premium members who joined in the last 30 days" → filter status: active, tiers: ["premium"], then client-side filter by joinedDate.
The data quality reality nobody mentions
Measured against a real production community of 700+ members:
-
~68% of members have a populated
emailfield. The other ~32% (typically members who joined via the Skool Discovery network — the platform's "browse communities" feature) don't share their email with the community owner. Skool keeps that field empty in the export. This is structural, not a bug. - ~88% have a LinkedIn URL in their first survey answer (when the apply form asks for it). Apply-form answers are far more complete than the email field.
-
invitedByis empty unless the member came through a tracked Skool referral link.
For the ~32% with no email, fall back to the LinkedIn URL from the survey. That's typically enough to identify the member for outreach.
What the export does NOT include
One important gap: the acquisition source (Joined from LinkedIn / Joined from your blog / Joined from Skool network) is NOT in the export. It lives in member.metadata.attrSrcComp on the SSR member object — accessed via a separate call:
{ "action": "members:list", "cookies": "...", "groupSlug": "...", "params": { "all": true } }
members:list returns joinedFrom / joinedVia per member — the missing piece for attribution analytics. Merge both responses by memberId if you need the full picture.
Pipe straight to your CRM
Two production patterns I use:
A. Nightly snapshot → NocoDB. Cron runs the export, diffs against yesterday's, upserts into a personas table keyed by email. New rows get joined_at, missing rows get churned_at. This is the single-source-of-truth for the CRM.
B. On-demand cohort pull. Export tier: ["premium"], filter rows where joinedDate >= 30 days ago, send each to a personalized email sequence in Postmark. The survey array gives the personalization data: their LinkedIn, what they're building, where they heard about you.
# JSON snapshot
curl ... > members-$(date +%F).json
# Or convert to CSV with jq
curl ... | jq -r '
(.[0] | [.firstName, .lastName, .email, .joinedDate, .tier, .ltv] | @csv),
(.[] | [.firstName, .lastName, .email, .joinedDate, .tier, .ltv] | @csv)
' > members-$(date +%F).csv
Gotchas I hit so you don't have to
-
build.staleerror = expired cookies, not a bug. Skool'saws-waf-tokenrotates every ~3.5 days. Re-runauth:loginwith email+password to get fresh cookies. The actor returns a clearerrorCode: BUILDID_STALEwith the exact JSON payload you need to re-auth. - Don't poll the export every 5 minutes. Designed for daily/weekly snapshots. The CSV is generated on-demand by Skool — calling it on every webhook is wasteful and you'll eventually hit rate limits.
- Large communities (10K+ members) can take 30+ seconds to generate. Set your HTTP client timeout to at least 90 sec.
-
invitedByis empty unless the member came through a tracked referral. All blanks doesn't mean broken — just means no member came through your ref link.
Why this actor exists
I run my own Skool community alone — no team, no virtual assistants. Approving members, replying to posts, publishing courses, exporting member data for follow-up. Everything in this actor (and the 20 production recipes in the docs) exists because I needed it to keep up with the daily work as a solo admin.
If you're running a Skool community without a team and the manual admin clicks are eating your day, the actor is built for you.
→ Use the Skool All-in-One API actor on Apify — pay-per-event (~$1.50/mo typical).
→ Full recipe with all the edge cases
New to Skool? Launch your community here — 14-day free trial.
Top comments (0)