RelayAPI

Threads API

Schedule and automate Threads posts with RelayAPI — text posts, images, videos, carousels, and thread sequences.

Quick Reference

PropertyValue
Platform keythreads
Auth methodOAuth 2.0 (via Instagram/Facebook)
Character limit500
Carousel items2–20 mixed images/videos
Image formatsJPEG, PNG
Image max size8 MiB
Video formatsMP4, MOV
Video max size100 MiB
Video max duration5 minutes
Post typesText, Image, Video, Carousel, Thread sequence
SchedulingYes
AnalyticsLimited (impressions, likes, comments, shares, views)

Client optionsTypeScript · Python REST/OpenAPI · Go · Java · REST API

Before You Start

Threads has a 500-character limit — the #1 failure cause when cross-posting from LinkedIn (3,000) or Facebook (63,000). Always use target_options.threads.content to provide a shorter version. Threads is connected via Instagram — losing Instagram access means losing Threads access. Requires an Instagram Business or Creator account with Threads enabled. Threads enforces a 250 API-published posts per 24-hour window limit. RelayAPI accepts JPEG and PNG images for Threads; WebP and GIF attachments are rejected by preflight.

Location tags require the threads_location_tagging grant. Existing Threads connections must be reauthorized before using location_id; refreshing an old token cannot add a scope that was never granted.

Quick Start

Post to Threads:

import Relay from '@relayapi/sdk';

const client = new Relay();
const post = await client.posts.create({
  content: 'Hot take: the best API is the one with the best docs.',
  targets: ['threads'],
  scheduled_at: 'now',
});

console.log(post.id); // post_abc123

Content Types

Text Post

One of the few platforms that supports text-only posts. Up to 500 characters.

const post = await client.posts.create({
  content: 'Hot take: the best API is the one with the best docs.',
  targets: ['threads'],
  scheduled_at: 'now',
});

Image Post

const post = await client.posts.create({
  content: 'New office setup!',
  targets: ['threads'],
  media: [
    { url: 'https://cdn.example.com/office.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
});

Video Post

Maximum 100 MiB, up to 5 minutes.

const post = await client.posts.create({
  content: 'Behind the scenes',
  targets: ['threads'],
  media: [
    { url: 'https://cdn.example.com/video.mp4', type: 'video' },
  ],
  scheduled_at: 'now',
});

Swipeable carousel containing 2–20 images, videos, or a mix of both.

const post = await client.posts.create({
  content: 'Product launch day!',
  targets: ['threads'],
  media: [
    { url: 'https://cdn.example.com/feature1.jpg', type: 'image' },
    { url: 'https://cdn.example.com/feature2.jpg', type: 'image' },
    { url: 'https://cdn.example.com/feature3.jpg', type: 'image' },
  ],
  scheduled_at: 'now',
});

Thread Sequence

Create a connected sequence of posts. The first item is the root post, and each subsequent item is a reply. Each item can have its own content and media.

const post = await client.posts.create({
  targets: ['threads'],
  scheduled_at: 'now',
  target_options: {
    threads: {
      thread: [
        {
          content: "Here's a thread about API design",
          media: [{ url: 'https://cdn.example.com/cover.jpg', type: 'image' }],
        },
        { content: '1/ First, REST principles...' },
        { content: '2/ Authentication is crucial...' },
        { content: '3/ Always version your API! /end' },
      ],
    },
  },
});

Each item in a thread sequence has its own 500-character limit. The root post is published first, and each subsequent item becomes a reply to the previous one.

Media Requirements

Images

PropertyRequirement
Max per post20 (subject to 20 total carousel items)
FormatsJPEG, PNG
Max file size8 MiB
Recommended1080 x 1350 px (4:5 portrait)
Aspect ratios4:5, 1:1, 16:9

Videos

PropertyRequirement
Max per post20 (subject to 20 total carousel items)
FormatsMP4, MOV
Max file size100 MiB
Max duration5 minutes
Recommended1080p, H.264, AAC, 30fps

Carousels require 2–20 total items and may mix supported images and videos. A single image or video is published as a normal media post rather than a carousel.

target_options Fields

All fields go inside target_options.threads on your post request.

FieldTypeDescription
contentstringOverride content for Threads specifically. Use this to provide a version under 500 characters when cross-posting.
mediaobject[]Override media for Threads specifically
threadobject[]Array of {content, media?} for thread sequences. Each item has its own 500-char limit.
pollobjectText-only poll containing 2–4 unique options, each 1–25 characters; cannot be combined with a Relay thread sequence
quote_post_idstringNumeric Threads media ID to quote
location_idstringNumeric Meta location ID; requires threads_location_tagging
topic_tagstringTopic tag attached to the post
reply_controlstringeveryone, accounts_you_follow, or mentioned_only
link_attachmentURLExplicit link attachment for a text post
await client.posts.create({
  content: 'Which guide should we ship next?',
  targets: ['threads'],
  scheduled_at: 'now',
  target_options: {
    threads: {
      poll: { options: ['Media', 'Webhooks', 'Analytics'] },
      location_id: '123456789012345',
      reply_control: 'everyone',
    },
  },
});

Common Errors

ErrorCauseFix
Text must be at most 500 charactersContent exceeds the 500-character limitUse target_options.threads.content to provide a shorter version.
Unsupported image typeMedia is WebP, GIF, or another unsupported image formatUse JPEG or PNG. RelayAPI rejects unsupported Threads media before provider I/O.
Media download failed (2207052)URL returns HTML instead of media bytesUse a direct CDN URL that returns the media bytes.
Instagram account restricted (2207050)Linked Instagram account has policy violationsResolve any policy violations on the Instagram account first.
Publishing failedMax retries exhaustedUsually temporary. Wait and retry manually.

Known Quirks

  • 500-character limit — the #1 failure cause when cross-posting from platforms with higher limits like LinkedIn (3,000) or Facebook (63,000).
  • Connected via Instagram — losing Instagram access means losing Threads access. Requires Instagram Business or Creator account.
  • JPEG and PNG only — RelayAPI rejects channel-neutral GIF and WebP attachments for Threads.
  • Cannot post new top-level comments — only replies are supported.
  • Cannot like or unlike comments via the API.
  • Cannot edit posts after publishing.
  • No DMs available via the Threads API.
  • 250 API-published posts per 24-hour window — this limit applies to all content types.
  • Posts cannot be edited after publishing.

Automations

Threads automation uses its own Graph base (graph.threads.net/v1.0). Replies use the 2-step container → publish flow like public Threads posts.

Triggers

TypeFires on
threads_replyReply to your thread
threads_mention@-mention
threads_publishYour thread publishes (useful for chained automations)

Send nodes

NodeEndpointRequired fields
threads_reply_to_post2-step: POST /{user}/threads then POST /{user}/threads_publishtext, reply_to_id
threads_hide_replyPOST /{reply-id}/manage_reply body { hide: true }reply_id

Found something wrong? Help us improve this page.

On this page