Skip to main content
Platforms

Threads

Publish text, images, image carousels, and videos to Meta's Threads via the PostPeer API.

Overview

Publish text, images (single or carousel), and videos to Threads via Meta's Threads Graph API. PostPeer handles the OAuth, the long-lived token exchange, and the two-phase container/publish flow Threads uses internally.

Threads is its own Meta product — separate from Facebook and Instagram in terms of API and OAuth, even though all three live under the same parent company.

Quick Start

1. Connect a Threads account

curl https://api.postpeer.dev/v1/connect/threads \
  -H "x-access-key: YOUR_API_KEY"

Response:

{
  "url": "https://threads.net/oauth/authorize?..."
}

Redirect the user to that URL. They authorize Threads access (the consent screen lists threads_basic and threads_content_publish). After they confirm, PostPeer's callback creates one integration tied to their Threads account.

2. Find the account ID

curl "https://api.postpeer.dev/v1/connect/integrations?platform=threads" \
  -H "x-access-key: YOUR_API_KEY"

The returned entry has id (the integration id you'll use as accountId), displayName (the user's @handle), imageUrl, and platformUserId (the Threads user id).

3. Publish a post

curl -X POST "https://api.postpeer.dev/v1/posts" \
  -H "x-access-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Shipping something new today. Stay tuned.",
    "platforms": [
      { "platform": "threads", "accountId": "abc123" }
    ],
    "publishNow": true
  }'

Response:

{
  "success": true,
  "status": "published",
  "platforms": [
    {
      "platform": "threads",
      "success": true,
      "platformPostId": "17841401234567890",
      "platformPostUrl": "https://www.threads.net/@username/post/..."
    }
  ]
}

Features

Text-only posts

Just content. Up to 500 characters.

Single image

Attach one image mediaItem. Caption goes in content.

{
  "content": "New shot from this morning",
  "platforms": [{ "platform": "threads", "accountId": "abc123" }],
  "mediaItems": [
    { "type": "image", "url": "https://cdn.example.com/photo.jpg" }
  ]
}

Attach 2 to 10 image mediaItems and PostPeer handles the multi-step container build for you (each image → child container → parent CAROUSEL container → publish).

{
  "content": "A few shots from the launch event",
  "platforms": [{ "platform": "threads", "accountId": "abc123" }],
  "mediaItems": [
    { "type": "image", "url": "https://cdn.example.com/1.jpg" },
    { "type": "image", "url": "https://cdn.example.com/2.jpg" },
    { "type": "image", "url": "https://cdn.example.com/3.jpg" }
  ]
}

Video

Attach one video mediaItem. The content becomes the caption.

{
  "content": "Quick walkthrough",
  "platforms": [{ "platform": "threads", "accountId": "abc123" }],
  "mediaItems": [
    { "type": "video", "url": "https://cdn.example.com/walkthrough.mp4" }
  ]
}

Reply restrictions

Use platformSpecificData.replyControl to limit who can reply:

{
  "platformSpecificData": {
    "replyControl": "accounts_you_follow"
  }
}

Allowed values: everyone (default), accounts_you_follow, mentioned_only.

Image accessibility — alt text

Pass alt text per image via platformSpecificData.altText, in the same order as mediaItems:

{
  "mediaItems": [
    { "type": "image", "url": "https://cdn.example.com/sunset.jpg" },
    { "type": "image", "url": "https://cdn.example.com/coast.jpg" }
  ],
  "platformSpecificData": {
    "altText": ["Pink sunset over the bay", "Rocky coast at low tide"]
  }
}

Limits

LimitValue
Text length500 characters
Images per post1 (single) or 2–10 (carousel)
Videos per post1
Mixing video + imagesNot allowed in one post
GIFsNot supported
Video size1 GB max
Video duration5 minutes max

Tokens & reconnection

Threads issues long-lived (60-day) user tokens. PostPeer auto-refreshes the token before expiry by calling th_refresh_token, extending it for another 60 days. As long as posts go through at least once every 60 days, the integration stays alive indefinitely without user intervention.

If the user revokes access in their Threads settings or the refresh window lapses, the next post returns an authentication error — surface a "reconnect Threads" prompt and have the user go through /v1/connect/threads again.

On this page