Skip to content
Social SDK megaphone markSocial SDK
Esc
navigateopen⌘Jpreview
On this page

Threads

Publish Threads containers, carousels, and media while preserving processing outcomes for explicit reconciliation.

The direct Threads adapter uses an existing user access token and Threads user ID. OAuth and account selection are application responsibilities. Set THREADS_GRAPH_VERSION deliberately when a version other than the adapter default (v1.0) is required.

Setup

Complete Meta app setup

Create a Meta app with Threads API access and complete the app review your use case requires. Token, user ID, scopes, and account eligibility remain server-owned setup.

Store credentials server-side

Set THREADS_USER_ID and THREADS_ACCESS_TOKEN in server configuration, and THREADS_GRAPH_VERSION only when you need a non-default version.

Configure the adapter

Construct the backend and select the account through a connected-account reference.

import { createSocial, connectedAccountRef } from "@opencoredev/social-sdk";
import { threads } from "@opencoredev/social-sdk/threads";

const social = createSocial({
  backend: threads({
    graphVersion: process.env.THREADS_GRAPH_VERSION,
    auth: {
      userId: process.env.THREADS_USER_ID!,
      accessToken: process.env.THREADS_ACCESS_TOKEN!,
    },
  }),
});

const account = connectedAccountRef({
  backend: "default",
  platform: "threads",
  accountId: process.env.THREADS_USER_ID!,
});

Examples

Publish a text post

const result = await social.posts.publish({
  targets: [{ account }],
  content: { text: "Hello Threads" },
});

Publish a single image or video

The adapter requires caller-provided HTTPS URLs; it does not upload local bytes.

const result = await social.posts.publish({
  targets: [{ account }],
  content: {
    text: "One image",
    media: [
      {
        kind: "image",
        source: { kind: "https-url", url: "https://cdn.example.com/photo.jpg" },
      },
    ],
  },
});

Carousel children are created before the parent container. The adapter accepts at most ten media items.

const result = await social.posts.publish({
  targets: [{ account }],
  content: {
    text: "A short gallery",
    media: [
      { kind: "image", source: { kind: "https-url", url: "https://cdn.example.com/1.jpg" } },
      { kind: "image", source: { kind: "https-url", url: "https://cdn.example.com/2.jpg" } },
      {
        kind: "video",
        source: { kind: "https-url", url: "https://cdn.example.com/clip.mp4" },
      },
    ],
  },
});

Reconcile a processing container

Threads publishing has two upstream phases: container creation and threads_publish. If the publish call does not return a native post ID, the result is processing; retain the delivery reference and reconcile explicitly. A returned ID produces published. Unknown upstream responses remain unknown. There is no automatic polling or cross-provider fallback.

const outcome = result.outcomes[0];
if (outcome?.state === "processing" && outcome.delivery) {
  // Later, from your own job runner:
  const delivery = await social.posts.getDelivery(outcome.delivery);
  console.log(delivery.state); // published | processing | failed | unknown
}

Beyond publishing

Reads, lifetime metrics, replies, quote posts, reposts, deletion, keyword search, and mentions are exposed through the typed adapter and native module. Normalized search is social.search.posts(account, { query, cursor, limit, startTime, endTime, scope }); it calls GET /keyword_search with the documented post fields and returns a page with an optional next cursor. Threads supports the recent search horizon only, so scope: "all" is rejected with invalid_input. The native search method additionally accepts searchType (TOP or RECENT), searchMode (KEYWORD or TAG), mediaType, since, until, and authorUsername. Public keyword search requires Meta’s threads_keyword_search approval; without it, results may be limited to the authorized account’s own posts. The adapter sends q and preserves empty result pages as { items: [] }.

Profile reads use GET /{threads-user-id}?fields=... for the authorized app-scoped user. A public username can use GET /profile_lookup?username=..., which requires threads_profile_discovery and is subject to Meta’s discovery limits. Profile responses are mapped from profile_picture_url and biography; if discovery omits an ID, the adapter preserves that limitation in the native result instead of claiming the username is a provider ID. Threads does not expose follow, block, or mute APIs, so those graph operations are not advertised.

Two operations are declared unsupported-by-platform in the capability manifest. profiles.search is unsupported because profile discovery only matches an exact username and Threads has no query-based profile search. graph.read is unsupported because the Threads user node has no followers or following edge. The authorized user’s total follower count is still available as the followers_count metric from social.analytics.getAccountMetrics, which requires threads_manage_insights.

Reply moderation is available through native methods: hideReply calls POST /{reply-id}/manage_reply with hide=true|false; listConversation calls GET /{media-id}/conversation; listPendingReplies calls GET /{media-id}/pending_replies; and managePendingReply calls POST /{reply-id}/manage_pending_reply with approve=true|false. These operations require threads_manage_replies (and threads_read_replies for reads). Mentions call GET /{threads-user-id}/mentions and require threads_manage_mentions approval.

deleteComment({ account, commentId, context }) deletes a reply with DELETE /{threads-media-id}, where the ID is the reply media ID returned when the reply was published. Meta only allows deleting media the authenticated user created, requires threads_basic and threads_delete, and limits each profile to 100 deletions per 24 hours. Use hideReply for replies from other users. See Meta’s delete posts guide.

Limits and requirements

  • Text, one HTTPS image or video, and carousels are supported.
  • The token, user ID, scopes, app review, and account eligibility remain server-owned setup.
  • Existing repository evidence covers adapter contract behavior and mocked transports; no live Threads account has been verified.

Consult Meta’s Threads API collection for current fields, permissions, media processing, and version changes.

Last updated on September 24, 2026