Skip to the content.

Audit a long welcome thread (>35 comments) with posts:getCommentsFull

Use case: Your community has a pinned “Introduce yourself” thread that grew to hundreds of comments. You want to verify that every new member got a personalized welcome reply from you — but Skool’s REST API caps comment listings at ~35 per thread. How do you scrape the full thread and find members you haven’t replied to?

This recipe uses posts:getCommentsFull — a Playwright-based action that bypasses the REST cap by scrolling the thread page and extracting every comment from the DOM. Costs $0.05 per invocation (the scrape fee) but returns the full thread.

When you need this

What you’ll need

The recipe

Step 1 — Get the full comment tree

curl -X POST "https://api.apify.com/v2/acts/cristiantala~skool-all-in-one-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "posts:getCommentsFull",
    "groupSlug": "your-community-slug",
    "cookies": "auth_token=...; client_id=...; aws-waf-token=...",
    "params": {
      "postId": "3bc910b12bd0443f886e13c5ac28a08a",
      "postSlug": "paso-2-presentate"
    }
  }'

Response shape (each comment is a flat object with isReply flag):

[
  {
    "id": "scraped-0",
    "content": "Hi! I'm Jane, founder of...",
    "author": { "firstName": "Jane", "lastName": "Doe", "slug": "janedoe" },
    "isReply": false
  },
  {
    "id": "scraped-1",
    "content": "Welcome Jane! Tell us about...",
    "author": { "firstName": "Cristian", "lastName": "Tala", "slug": "cristian" },
    "isReply": true
  }
]

Step 2 — Identify top-level comments without admin replies

Group the flat array into top-level + nested replies, then filter:

// pseudo-code in n8n Code node or Python
const ADMIN_NAMES = ['Cristian Tala'];

let topLevels = [];
let current = null;
for (const c of comments) {
  if (c.isReply) {
    if (current) current.replies.push(c);
  } else {
    current = { ...c, replies: [] };
    topLevels.push(current);
  }
}

const unanswered = topLevels.filter(tl => {
  // Skip top-level from admin (it's a regular post, not an intro)
  if (ADMIN_NAMES.some(n => tl.author.firstName + ' ' + tl.author.lastName === n)) {
    return false;
  }
  // Did any admin reply?
  return !tl.replies.some(r =>
    ADMIN_NAMES.some(n => r.author.firstName + ' ' + r.author.lastName === n)
  );
});

console.log(`${unanswered.length} members need a welcome reply`);

Step 3 — Generate personalized drafts (optional)

Pipe each unanswered intro through an LLM with your community context:

// For each unanswered, call LLM
for (const member of unanswered) {
  const prompt = `You are the founder of [community name]. A new member just introduced themselves:

"${member.content}"

Write a warm, specific, 3-5 sentence welcome reply that:
- References ONE concrete detail from their intro (not generic praise)
- Offers ONE actionable tip relevant to what they shared
- Invites them to post a specific question in the feed

Tone: founder-to-founder, no marketing fluff, no emojis in chains.`;

  const reply = await llm(prompt);
  drafts.push({ member: member.author.firstName, content: reply });
}

Step 4 — Post the replies (with human approval)

Don’t auto-post. Generate drafts, send to Telegram or a review queue, only post after manual approval:

curl -X POST "https://api.apify.com/v2/acts/cristiantala~skool-all-in-one-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "posts:createComment",
    "groupSlug": "your-community-slug",
    "cookies": "auth_token=...; ...",
    "params": {
      "rootId": "3bc910b12bd0443f886e13c5ac28a08a",
      "parentId": "<comment_id_of_the_member>",
      "content": "Approved draft text..."
    }
  }'

Note: the comment_id of the member is NOT the same as scraped-0 (that’s a synthetic ID from Playwright). For posting replies you need the real Skool ID, which posts:getCommentsFull doesn’t return. Either:

The current limitation: posts:getCommentsFull is read-only audit. For programmatic replies to old comments, combine with posts:getComments (REST, ~35 most recent) to get real IDs and cross-reference by author + content. Resolving an arbitrary comment ID for older comments programmatically is a known gap — track issues on the repo if you hit this.

Pricing

Action Cost When charged
posts:getCommentsFull $0.05 Per invocation (the scrape fee)
Dataset items returned $0.005 × N One result per comment in thread

For a 200-comment thread: ~$0.05 + (200 × $0.005) = ~$1.05 per audit run. Most communities run this once per week or monthly.

Why not just use posts:getComments?

  posts:getComments (REST) posts:getCommentsFull (Playwright)
Speed ~2s ~30-60s
Cost $0 (just dataset) $0.05 + dataset
Max comments per thread ~35 (25 head + 10 tail) All (verified up to 200+)
Returns real comment IDs Yes No (synthetic scraped-N)
Best for New threads, recent comments Audits, long threads

Use posts:getComments for day-to-day. Use posts:getCommentsFull when you specifically need full coverage and audit insights are worth more than the $0.05.