Skip to main content
Before you start. Threads uses Meta’s Threads Graph API with a separate OAuth at threads.net — distinct from Facebook Login. Connecting Threads does not connect Facebook or Instagram, and vice versa.

Quick reference

Limit / capabilityValue
Character limit500 graphemes
Items per carousel2 – 20 (mixed image + video allowed)
Image formatsjpeg, png, webp
Image size8,000,000 bytes (8 MB)
Video formatsmp4, mov
Video size1,000,000,000 bytes (1 GB)
Video duration≤ 5 min (platform-enforced)
Alt text per item1000 graphemes
Post types supportedtext, single image, single video, carousel, reply
SchedulingYes (via scheduledAt)
Reply supportYes (threads.replyToId)
First commentNot supported
Inbox / DMNot supported in v1
AnalyticsNot supported in v1

Connect an account

OAuth 2.0 against threads.net. Start at POST /v1/accounts/connect/threads, send the user through the redirect, and the callback completes the handshake.

Scopes

scoperequested
threads_basicalways — required to call GET /me.
threads_content_publishalways — unlocks create-container + publish.
threads_manage_repliesonly when the caller opts into extended scopes.
threads_read_repliesonly when the caller opts into extended scopes.
The publisher uses neither replies scope — Threads supports replying via replyToId on the request body, which is a write and needs no extra scope.

Token lifecycle

Threads issues long-lived tokens (~60 days, refreshable after the first 24 h). The publisher refreshes ahead of expiry; subscribe to token.expiring to know when a user-prompt re-auth is on the horizon.

Post types

Text post

text.json
{
  "targets": [{ "accountId": "..." }],
  "text": "Threads-only thought"
}

Single image

single-image.json
{
  "targets": [{ "accountId": "..." }],
  "text": "Caption is optional",
  "media": [
    { "kind": "image", "mediaId": "med_…", "altText": "..." }
  ]
}

Single video

single-video.json
{
  "targets": [{ "accountId": "..." }],
  "text": "Demo clip",
  "media": [
    { "kind": "video", "mediaId": "med_…" }
  ]
}
Threads accepts 2 – 20 items per carousel, and unlike most platforms it allows mixed image + video in a single carousel.
carousel.json
{
  "targets": [{ "accountId": "..." }],
  "text": "Mixed carousel",
  "media": [
    { "kind": "image", "mediaId": "med_…", "altText": "first" },
    { "kind": "video", "mediaId": "med_…", "altText": "second" },
    { "kind": "image", "mediaId": "med_…", "altText": "third" }
  ]
}

Reply

Replies to an existing thread under the same account.
reply.json
{
  "targets": [
    {
      "accountId": "...",
      "options": { "platform": "threads", "replyToId": "<thread id>" }
    }
  ],
  "text": "Replying to my own post"
}

Wisdom (platform-specific things that bite)

  • Threads is a two-step container API: create-container → poll until FINISHED → publish. The publisher hides this entirely; one POST /v1/posts blocks until the publish lands. Image containers usually FINISH inside 1 – 2 s; video can take a minute.
  • Containers expire after 24 hours without a publish. If a scheduled post’s container ages out, the dispatcher creates a fresh one — see threads.container.expired.
  • Carousels allow mixed image + video — this is the only platform in letmepost where that’s true.
  • Threads has no first-comment surface; what looks like “first comment” on the app is just another reply you’d post via threads.replyToId.
  • A 700-char caption on an image post is rejected upstream with the same 500-grapheme rule as text-only — preflight catches it locally before any container is created.
  • The publish response carries the post id but not a permalink; the publisher follows up with GET /{thread-id}?fields=permalink best-effort, and falls back to the id if Threads returns nothing.

Common errors

Error ruleWhat it meansHow to fix
threads.text.max_graphemesCaption exceeded 500 graphemesTrim to ≤500 graphemes.
threads.text.requiredText-only post had no textProvide text or attach media[].
threads.media.count_maxMore than 20 items in a carouselReduce to ≤20, or split into multiple posts.
threads.media.mime_allowedUnsupported image/video mimeImages: jpeg/png/webp. Videos: mp4 or mov.
threads.media.image_size_maxImage > 8 MBRe-encode under 8,000,000 bytes.
threads.media.video_size_maxVideo > 1 GBCompress under 1,000,000,000 bytes.
threads.container.expiredContainer aged past Threads’ 24 h windowRe-create and publish in one flow (the publisher does this for scheduled posts automatically).
threads.container.errorThreads transcoder or policy filter rejected the containerInspect platformResponse.error_message for the upstream reason.

What you can’t do (yet)

  • First comment (Threads has no comment-as-author surface distinct from a reply).
  • Quote posts, polls, edits.
  • Reading replies / engagement (would require threads_read_replies + the inbox feature, neither in v1).
  • DMs — Threads has no DM API.
  • Cross-posting fan-out to Instagram in a single call; Threads and Instagram are separate connects.

API reference