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" }
]
}Image carousel (2–10)
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
| Limit | Value |
|---|---|
| Text length | 500 characters |
| Images per post | 1 (single) or 2–10 (carousel) |
| Videos per post | 1 |
| Mixing video + images | Not allowed in one post |
| GIFs | Not supported |
| Video size | 1 GB max |
| Video duration | 5 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.