> ## Documentation Index
> Fetch the complete documentation index at: https://docs.letmepost.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Threads

> Meta's Threads via the standalone Threads Graph API. Two-step container publish.

<Warning>
  **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.
</Warning>

## Quick reference

| Limit / capability   | Value                                             |
| -------------------- | ------------------------------------------------- |
| Character limit      | 500 graphemes                                     |
| Items per carousel   | 2 – 20 (mixed image + video allowed)              |
| Image formats        | jpeg, png, webp                                   |
| Image size           | 8,000,000 bytes (8 MB)                            |
| Video formats        | mp4, mov                                          |
| Video size           | 1,000,000,000 bytes (1 GB)                        |
| Video duration       | ≤ 5 min (platform-enforced)                       |
| Alt text per item    | 1000 graphemes                                    |
| Post types supported | text, single image, single video, carousel, reply |
| Scheduling           | Yes (via `scheduledAt`)                           |
| Reply support        | Yes (`threads.replyToId`)                         |
| First comment        | Not supported                                     |
| Inbox / DM           | Not supported in v1                               |
| Analytics            | Not 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

| scope                     | requested                                         |
| ------------------------- | ------------------------------------------------- |
| `threads_basic`           | always — required to call `GET /me`.              |
| `threads_content_publish` | always — unlocks create-container + publish.      |
| `threads_manage_replies`  | only when the caller opts into `extended` scopes. |
| `threads_read_replies`    | only 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`](/webhooks/token-expiring) to know when a user-prompt re-auth is on the horizon.

## Post types

### Text post

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

### Single image

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

### Single video

```json single-video.json theme={"system"}
{
  "targets": [{ "accountId": "..." }],
  "text": "Demo clip",
  "media": [
    { "kind": "video", "mediaId": "med_…" }
  ]
}
```

### Carousel (mixed)

Threads accepts 2 – 20 items per carousel, and unlike most platforms it allows **mixed image + video** in a single carousel.

```json carousel.json theme={"system"}
{
  "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.

```json reply.json theme={"system"}
{
  "targets": [
    {
      "accountId": "...",
      "options": { "platform": "threads", "replyToId": "<thread id>" }
    }
  ],
  "text": "Replying to my own post"
}
```

## Wisdom (platform-specific things that bite)

<Tip>
  * 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`](/preflight/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.
</Tip>

## Common errors

| Error rule                                                                | What it means                                              | How to fix                                                                                     |
| ------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [`threads.text.max_graphemes`](/preflight/threads-text-max_graphemes)     | Caption exceeded 500 graphemes                             | Trim to ≤500 graphemes.                                                                        |
| [`threads.text.required`](/preflight/threads-text-required)               | Text-only post had no text                                 | Provide text or attach `media[]`.                                                              |
| [`threads.media.count_max`](/preflight/threads-media-count_max)           | More than 20 items in a carousel                           | Reduce to ≤20, or split into multiple posts.                                                   |
| [`threads.media.mime_allowed`](/preflight/threads-media-mime_allowed)     | Unsupported image/video mime                               | Images: jpeg/png/webp. Videos: mp4 or mov.                                                     |
| [`threads.media.image_size_max`](/preflight/threads-media-image_size_max) | Image > 8 MB                                               | Re-encode under 8,000,000 bytes.                                                               |
| [`threads.media.video_size_max`](/preflight/threads-media-video_size_max) | Video > 1 GB                                               | Compress under 1,000,000,000 bytes.                                                            |
| [`threads.container.expired`](/preflight/threads-container-expired)       | Container aged past Threads' 24 h window                   | Re-create and publish in one flow (the publisher does this for scheduled posts automatically). |
| [`threads.container.error`](/preflight/threads-container-error)           | Threads transcoder or policy filter rejected the container | Inspect `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

* [`POST /v1/posts`](/api-reference) — primary publish.
* [`POST /v1/media`](/api-reference) — upload images/videos for `mediaId` references.
* [`POST /v1/accounts/connect/threads`](/api-reference) — start a Threads OAuth flow.
