PostZen

Threads

Connect a Threads account and publish text, single-image, and single-video posts with the PostZen API.

PostZen publishes and schedules text, single-image, and single-video posts to Threads — each Threads target can also control who is allowed to reply.

Quick reference

LimitThreads value
Text500 characters maximum
Post typesText, single image, or single video
Images per post1
Maximum image size8 MB
Videos per post1
Maximum video size1 GB
Mixed image and videoNot supported
PostZen presigned-upload image typesJPEG/JPG, PNG, WebP, GIF
PostZen presigned-upload video typesMP4, MPEG, QuickTime, AVI, WebM, M4V
SchedulingSupported

Threads text is limited to 500 characters. This is the most common Threads cross-posting failure. Use customContent on the Threads target when the shared content is longer.

Threads currently accepts one image per post through PostZen. Multi-image carousels are coming soon — they are not yet supported.

Before you start

  • Your Threads profile must be tied to an Instagram account. Sign in with the Instagram account that has Threads enabled when you complete OAuth.
  • You need the PostZen profile ID that will own the connected Threads account.
  • The free tier allows 2 connected accounts before a payment method is required. Connecting another account after that returns a 402.
  • Keep Threads content at or below 500 characters. For a cross-platform post, set customContent on the Threads target to provide a shorter version.
  • Attach either one image or one video. You cannot attach multiple images or mix an image and a video.

Media URLs must be publicly accessible direct links to the file. Google Drive, Dropbox, OneDrive, and iCloud share links return HTML pages instead of the media file. You can upload media through POST /v1/media/presign and use the returned publicUrl instead.

Connect your account

Threads uses OAuth 2.0. Request a connect URL for your PostZen profile, then send the user to the returned authUrl. PostZen manages the OAuth scopes internally. The returned state expires after 10 minutes.

const { data } = await postzen.connect.createConnectUrl({
  path: { platform: 'threads' },
  query: {
    profileId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
    redirectUrl: 'https://yourapp.com/connected',
  },
});

console.log(data.authUrl); // redirect the user here
console.log(data.state);   // expires after 10 minutes
response = client.connect.create_connect_url(
    "threads",
    profile_id="jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
    redirect_url="https://yourapp.com/connected",
)

print(response.authUrl)  # redirect the user here
print(response.state)    # expires after 10 minutes
curl "https://api.postzen.dev/v1/connect/threads?profileId=jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e&redirectUrl=https://yourapp.com/connected" \
  -H "Authorization: Bearer $POSTZEN_API_KEY"

The response is { authUrl, state }. Complete the OAuth flow at authUrl with the Instagram account that has Threads enabled.

Quick start

Publish a text post immediately by targeting the connected account with the threads platform value.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'Hello from Threads!',
    publishNow: true,
    platforms: [
      {
        platform: 'threads',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="Hello from Threads!",
    publish_now=True,
    platforms=[
        {
            "platform": "threads",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from Threads!",
    "publishNow": true,
    "platforms": [
      {
        "platform": "threads",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e"
      }
    ]
  }'

Set exactly one of publishNow, scheduledFor, or isDraft when you create a post. To schedule it, replace publishNow with an ISO-8601 scheduledFor value at least 60 seconds in the future.

Content types

Text posts

Threads text posts accept up to 500 characters. Set replyControl in the Threads target's settings object to choose who can reply.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'What are you building this week?',
    publishNow: true,
    platforms: [
      {
        platform: 'threads',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
        settings: {
          replyControl: 'accountsYouFollow',
        },
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="What are you building this week?",
    publish_now=True,
    platforms=[
        {
            "platform": "threads",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
            "settings": {
                "reply_control": "accountsYouFollow",
            },
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "What are you building this week?",
    "publishNow": true,
    "platforms": [
      {
        "platform": "threads",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        "settings": {
          "replyControl": "accountsYouFollow"
        }
      }
    ]
  }'

For a cross-platform post whose shared content exceeds 500 characters, add a shorter customContent value to the Threads target.

Single-image posts

Add one image in mediaItems. Threads supports one image only through PostZen; adding another image is not yet supported.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'A first look at the new design.',
    publishNow: true,
    mediaItems: [
      { url: 'https://cdn.example.com/design.jpg' },
    ],
    platforms: [
      {
        platform: 'threads',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="A first look at the new design.",
    publish_now=True,
    media_items=[
        {"url": "https://cdn.example.com/design.jpg"},
    ],
    platforms=[
        {
            "platform": "threads",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "A first look at the new design.",
    "publishNow": true,
    "mediaItems": [
      { "url": "https://cdn.example.com/design.jpg" }
    ],
    "platforms": [
      {
        "platform": "threads",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e"
      }
    ]
  }'

The image must be 8 MB or smaller. PostZen warns when an image exceeds this limit.

Single-video posts

Add one video in mediaItems. A Threads video must be 1 GB or smaller, and the post cannot also contain an image.

const { data } = await postzen.posts.createPost({
  body: {
    content: 'Watch the new workflow in action.',
    publishNow: true,
    mediaItems: [
      { url: 'https://cdn.example.com/workflow.mp4' },
    ],
    platforms: [
      {
        platform: 'threads',
        accountId: 'jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e',
      },
    ],
  },
});

console.log(data.post._id);
response = client.posts.create_post(
    content="Watch the new workflow in action.",
    publish_now=True,
    media_items=[
        {"url": "https://cdn.example.com/workflow.mp4"},
    ],
    platforms=[
        {
            "platform": "threads",
            "account_id": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e",
        },
    ],
)

print(response.post.field_id)
curl -X POST https://api.postzen.dev/v1/posts \
  -H "Authorization: Bearer $POSTZEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Watch the new workflow in action.",
    "publishNow": true,
    "mediaItems": [
      { "url": "https://cdn.example.com/workflow.mp4" }
    ],
    "platforms": [
      {
        "platform": "threads",
        "accountId": "jh72r5nqk9wx3v8m1t4cz6bs0fy5dg3e"
      }
    ]
  }'

Media requirements

You can pass an external URL or upload media through /v1/media/presign. PostZen downloads and re-hosts external URLs up to 100 MB. Presigned uploads support files up to 5 GB, but the stricter Threads limits below still apply.

Images

RequirementValue
Images per post1
Maximum file size8 MB
PostZen presigned-upload typesJPEG/JPG, PNG, WebP, GIF
Can be combined with videoNo

Videos

RequirementValue
Videos per post1
Maximum file size1 GB
PostZen presigned-upload typesMP4, MPEG, QuickTime, AVI, WebM, M4V
Can be combined with an imageNo

Platform settings

Add Threads settings to the target object inside platforms, as shown in the text post example.

KeyTypeNotes
replyControl"everyone" | "accountsYouFollow" | "mentionedOnly"Controls who can reply to the post.

PostZen analytics is live for Threads, including post metrics, follower counts, and best-time-to-post suggestions. Reposts of other people's threads report no metrics of their own.

What you can't do

  • Publish multi-image carousels. Carousels are coming soon; PostZen currently accepts one image per Threads post.
  • Publish multi-post thread chains. PostZen does not yet support them.
  • Create polls or quote posts, or use GIF search.
  • Mix images and video in one post.
  • Edit a published post.
  • Read or manage DMs, inbox messages, comments, or replies.
  • Create ads or receive engagement webhooks.

Common errors

ErrorMeaningFix
Text over 500 charactersThe Threads target exceeds its text limit. This is the most common cross-posting failure.Shorten content, or set a Threads-specific customContent value of 500 characters or fewer.
More than one imagePostZen currently supports one image per Threads post.Keep one image. Multi-image carousels are coming soon.
Image over 8 MBThe image exceeds the Threads limit and PostZen issues a warning.Use an image no larger than 8 MB.
Image and video in one postThreads posts through PostZen cannot mix media types.Keep either one image or one video.
Video over 1 GBThe video exceeds the Threads file-size limit.Use a video no larger than 1 GB.
402 while connectingThe profile is over the free-tier allowance of 2 connected accounts without a payment method.Add a payment method, then start the connection again.
OAuth state expiredThe connect URL's state is more than 10 minutes old.Request a new Threads connect URL and complete OAuth within 10 minutes.

On this page