mcpField
T
threads

threads

github.com/acedatacloudMarketing & growth1749 installsUpdated 1d ago
Repository

About threads

threads is a skill for operating the Threads API with curl and jq. It covers publishing text, image, video, and carousel posts, checking recent posts, and handling credentials and publishing constraints.

Key features

  1. 01Threads API requests with curl and jq
  2. 02Publish text and media posts
  3. 03Create carousel posts
  4. 04List recent posts
  5. 05Credential and publishing guidance

Use cases

  1. 01Publish a confirmed post to a Threads account
  2. 02Share image, video, or carousel content
  3. 03Review recent posts and retrieve permalinks

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/acedatacloud/skills --skill threads

After installing, type/threadsin Claude Code to confirm it loaded.

Details

From the project README

Call the Threads API (graph.threads.net) with curl + jq. The connector injects one credential: $THREADS_ACCESS_TOKEN (a long-lived Threads access token with the threads_basic + threads_content_publish scopes). Never echo it.

Resolve the caller's Threads user id once (every publish needs it):

ME=$(curl -sS "https://graph.threads.net/v1.0/me?fields=id,username&access_token=$THREADS_ACCESS_TOKEN")
TID=$(echo "$ME" | jq -r .id)
echo "$ME"     # {"id":"<THREADS_USER_ID>","username":"..."}

Errors are JSON with error.message / error.code — show them verbatim. 401 / OAuthException → the token expired or lacks scope; reconnect the Threads connector.

Publish a post (two-step: create container → publish)

Confirm the text with the user first (it publishes as their real account). Text ≤ 500 characters (emoji counted as UTF-8 bytes).

Step 1 — create a media container. Text-only (guard the 500-byte limit first):

TEXT="Shipping one API for AI images → posters, cards, mockups. https://platform.acedata.cloud #AI #API"
[ "$(printf %s "$TEXT" | wc -c)" -le 500 ] || { echo "text exceeds Threads 500-byte limit — shorten it"; }
CID=$(curl -sS -X POST "https://graph.threads.net/v1.0/$TID/threads" \
  --data-urlencode "media_type=TEXT" \
  --data-urlencode "text=$TEXT" \
  -d "access_token=$THREADS_ACCESS_TOKEN" | jq -r .id)
echo "container=$CID"

Image post — add media_type=IMAGE + a public image_url (Threads cURLs it server-side, so it must be on a public server); video uses media_type=VIDEO + video_url:

CID=$(curl -sS -X POST "https://graph.threads.net/v1.0/$TID/threads" \
  --data-urlencode "media_type=IMAGE" \
  -d "image_url=https://cdn.acedata.cloud/xxxx.jpg" \
  --data-urlencode "text=caption here" \
  -d "access_token=$THREADS_ACCESS_TOKEN" | jq -r .id)

Step 2 — publish the container. Text posts publish immediately; for IMAGE / VIDEO / carousel containers you MUST wait ≥30s first so Threads can fetch/process the upload — publishing too early fails or returns no id:

sleep 30   # REQUIRED for IMAGE / VIDEO / carousel; skip for TEXT-only posts
curl -sS -X POST "https://graph.threads.net/v1.0/$TID/threads_publish" \
  -d "creation_id=$CID" -d "access_token=$THREADS_ACCESS_TOKEN" | jq .
# → {"id":"<THREADS_MEDIA_ID>"}

If threads_publish returns no id, the container isn't ready yet — poll GET /v1.0/<CID>?fields=status&access_token=$THREADS_ACCESS_TOKEN until status=FINISHED, then retry publish.

Get the public URL of the published post and hand it to the user:

curl -sS "https://graph.threads.net/v1.0/<THREADS_MEDIA_ID>?fields=id,permalink&access_token=$THREADS_ACCESS_TOKEN" | jq -r .permalink

Carousel (2–20 items)

Create each child with is_carousel_item=true, then a media_type=CAROUSEL container with children=<ID1>,<ID2>,..., then publish the carousel id. Links: add link_attachment=<URL> (text-only posts, ≤5 links). Topic tag: topic_tag=<TAG>.

List my recent posts

curl -sS "https://graph.threads.net/v1.0/$TID/threads?fields=id,text,permalink,timestamp&limit=20&access_token=$THREADS_ACCESS_TOKEN" | jq '.data'

Gotchas

  • 500-char limit; emoji count as their UTF-8 byte length.
  • Media must be a public URL — Threads server-side cURLs image_url/video_url; local files won't work. Upload to a public host / cdn.acedata.cloud first.
  • Wait ~30s before publishing media containers (text publishes instantly); if threads_publish doesn't return an id, poll GET /<container-id>?fields=status.
  • Rate limit: 250 published posts per 24h per profile (a carousel counts as 1).
  • Threads tokens differ from Facebook/Instagram tokens — they come from the Threads OAuth flow on graph.threads.net, not graph.facebook.com.

Record the output

After you successfully publish and obtain the live permalink, call the built-in publish_artifact tool ONCE so the user can track it in My Outputs:

publish_artifact(kind="message", channel="threads", title="<title>", url="<the REAL permalink>", status="delivered")

Use the real returned URL — never fabricate one. Call it once per published item, only after delivery is confirmed; skip it (or use status="failed") if publishing failed. See _shared/artifacts.md.

Comments

Markdown supported