Skip to the content.

Edit published Skool posts via API

Need to fix a typo in a pinned post, swap a stale link across 20 announcements, or update the title of an evergreen feed post after a rebrand? The Skool UI lets you edit one post at a time. The API does the same write, scriptable — so you can run a corrections pass over hundreds of posts in one go.

The catch: the posts:update action has one silent-fail gotcha that wasted half a day of debugging when we first hit it. This recipe shows the canonical shape and the read-then-write + verify-fetch pattern that makes updates reliable.

Quick reference (TL;DR for agents)

   
Goal Update title, content, or label of an existing Skool post
Stack Any HTTP client + the Apify-hosted actor
Actions used posts:getposts:updateposts:get (verify)
Setup time ~5 min
Ongoing cost $0.01 × N posts updated
Key gotcha Body MUST be flat (no metadata wrapper) or Skool returns 200 OK and silently ignores the update
Mandatory pattern write + verify-fetch — never trust the 200

Prerequisites

The non-obvious thing about posts:update

There are two body shapes that LOOK valid to a developer reading the network tab, but only one actually works:

//  THIS GETS 200 OK BUT THE UPDATE IS SILENTLY IGNORED
{
  "post_type": "generic",
  "group_id": "...",
  "metadata": {
    "title": "New title",
    "content": "New body"
  }
}

//  THIS WORKS  flat body, no wrapper
{
  "title": "New title",
  "content": "New body",
  "attachments": "",
  "labels": "existing_label_id_or_empty",
  "video_links": "",
  "video_ids": []
}

We caught this by capturing the Skool admin UI’s own update call with API Reverse Engineer — the UI sends the flat shape, and that’s the only one Skool actually persists. The metadata-wrapped shape returns a clean 200 OK response but the post in the database doesn’t change.

The actor (posts:update) sends the correct flat shape internally. You only see this gotcha if you’re calling the Skool API directly without the wrapper.

Step 1 — Read the current post (to preserve labels)

If the post has a category label assigned, Skool requires labels in the update body or returns 400 "one or more labels required". Always read first:

{
  "action": "posts:get",
  "cookies": "...",
  "groupSlug": "your-community",
  "params": { "postId": "abc123...32hex" }
}

Save labelId (or metadata.labels) for the update call. If the post has no label, this can be empty.

Step 2 — Update

{
  "action": "posts:update",
  "cookies": "...",
  "groupSlug": "your-community",
  "params": {
    "postId": "abc123...32hex",
    "title": "New title here",
    "content": "New body content as plain text. Mentions use [@Name](obj://user/{userId}) syntax.",
    "labels": "preserve_the_labelId_from_step_1"
  }
}

Field rules:

Field Rules
title String. Empty string clears the title.
content Plain text only. No TipTap, no Markdown — posts use raw text. Linebreaks are \n.
labels Pass the existing labelId to preserve, or empty string if post had none. Required if post had a label.
video_ids Must be array [], never string "" — passing string returns 500.

Step 3 — Verify (never trust the 200)

{ "action": "posts:get", "cookies": "...", "groupSlug": "your-community", "params": { "postId": "..." } }

Assert that title and content match what you sent. This step is non-negotiable. Skool’s write endpoints have a documented history of returning success while silently no-op’ing — the only proof an update actually applied is a read-back showing the new value.

If verify fails: most likely you sent the wrapped metadata shape (you’re calling the API directly, not the actor) or you missed labels for a labelled post.

Production patterns

Bulk find-and-replace across the feed. List all posts containing a stale URL, run the swap, verify each.

# pseudo-flow
posts:filter --query "old-url.com"for each: posts:get → posts:update (replace string) → posts:get (verify)

Edit a pinned announcement. Pinned posts in the feed get the most eyeballs. The API lets you update them without unpinning (avoiding the “new post” notification spam that re-pinning triggers).

Correct typos in onboarding pinned threads. The “Start here” post in Cágala, Aprende, Repite’s feed is updated this way when steps change — no notification fired, content updates in place.

Production gotchas

See also


Use this in production — no setup

The hardest part of building Skool automation isn’t the API logic — it’s the auth (cookies expire every ~3.5 days, WAF token rotation, weekly Skool buildId changes). The Skool All-in-One API actor on Apify handles all of that — including the flat-body shape for posts:update.

→ Open the actor on Apify

New to Skool? Launch your community here — 14-day free trial.