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

Instagram

Publish Instagram professional-account media and use typed native Reels, Stories, hashtag, limit, and mention operations.

The direct Instagram adapter uses Instagram Login for a professional account by default. Supply an access token and the account ID on the server; the adapter does not perform OAuth, account discovery, or tenant membership checks. Set auth.flavor to "facebook-login" when using the Facebook Login Graph API host and token.

Setup

Prepare the professional account

Instagram publishing requires a professional account and the corresponding Meta app products. Account authorization and professional-account eligibility remain application responsibilities.

Store credentials server-side

Set INSTAGRAM_ACCESS_TOKEN and INSTAGRAM_ACCOUNT_ID in server configuration.

Configure the adapter

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

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

const social = createSocial({
  backend: instagram({
    auth: {
      accessToken: process.env.INSTAGRAM_ACCESS_TOKEN!,
      accountId: process.env.INSTAGRAM_ACCOUNT_ID!,
      // Use "facebook-login" for business discovery, tagged media, and hashtag search.
      flavor: "instagram-login",
    },
  }),
});

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

Examples

Publish an image

Images, videos, and carousels use public HTTPS media.

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

The adapter stores a workflow for child containers, parent creation, and continuation after asynchronous processing.

const result = await social.posts.publish({
  targets: [{ account }],
  content: {
    text: "Three-part carousel",
    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/3.mp4" } },
    ],
  },
});

Reconcile container processing

Retain the processing outcome and reconcile explicitly. The adapter does not silently crop, change account visibility, or convert unsupported media.

const outcome = result.outcomes[0];
if (outcome?.state === "processing" && outcome.delivery) {
  const delivery = await social.posts.getDelivery(outcome.delivery);
  console.log(delivery.state);
}

Read comments and reply

The adapter declares post reads, status, analytics, comment reads, and comment replies. Replies require a comment reference returned by a comment read.

// postRef is the platform-post reference returned by a publish outcome or post read.
const comments = await social.comments.list(postRef);
const commentId = comments.items[0]?.id;
if (typeof commentId !== "string") throw new Error("No comment selected");

await social.comments.reply(
  { ...postRef, kind: "comment", commentId },
  { text: "Thanks for the note!" },
);

Read profiles

The normalized graph reader returns the authorized professional account when no selector is supplied. A handle uses Meta’s business_discovery field and therefore requires Facebook Login.

const ownProfile = await social.graph.getProfile(account, {});
const publicProfile = await social.graph.getProfile(account, { handle: "bluebottle" });
console.log(ownProfile.handle, publicProfile.displayName);

The own-profile request is GET /{ig-user-id} with profile fields. Handle lookup is GET /{ig-user-id}?fields=business_discovery.username(...). An Instagram Login token raises unsupported_capability for handle lookup.

Moderate comments

Comment moderation uses the Graph API comment and media nodes. moderateComment maps to POST /{ig-comment-id}?hide=true|false, deleteComment maps to DELETE /{ig-comment-id}, and setCommentsEnabled maps to POST /{ig-media-id}?comment_enabled=true|false.

const instagramNative = social.native("default", { acknowledgeUnsafe: true });
const context = {
  backendInstance: "default",
  correlationId: "instagram-native",
  retryBudget: { maxAttempts: 1, maxElapsedMs: 10_000 },
};

await instagramNative.moderateComment({
  account,
  commentId: "17873440459141021",
  hidden: true,
  context,
});
await instagramNative.setCommentsEnabled({
  account,
  mediaId: "17873440459141020",
  enabled: true,
  context,
});
await instagramNative.deleteComment({ account, commentId: "17873440459141021", context });

These mutations accept Instagram User access tokens with Instagram Login or a Facebook User/Page token with Facebook Login. For Instagram Login, request instagram_business_basic and instagram_business_manage_comments; Facebook Login uses instagram_basic, instagram_manage_comments, and pages_read_engagement (plus any permissions Meta requires for the Page role). Meta limits deletion and hiding by media ownership and does not support live video comments.

Read replies and mentions

Use listCommentReplies for the paginated GET /{ig-comment-id}/replies edge. The result has items and an optional nextCursor; pass that cursor back as cursor. An empty provider data array returns an empty items array.

const replies = await instagramNative.listCommentReplies({
  account,
  commentId: "17873440459141021",
  limit: 25,
  context,
});
const nextReplies = replies.nextCursor
  ? await instagramNative.listCommentReplies({
      account,
      commentId: "17873440459141021",
      cursor: replies.nextCursor,
      context,
    })
  : undefined;

Replies can use either login flavor when the token has the permissions required for the selected Graph host. Instagram Login uses instagram_business_basic and instagram_business_manage_comments. Facebook Login uses instagram_basic, instagram_manage_comments, and pages_read_engagement.

listMentions, listTaggedMedia, and mentions read GET /{ig-user-id}/tags and return the same cursor-paginated page shape. mentions is an alias for listMentions. All three work with either login flavor and back the mentions.read capability. Instagram Login calls graph.instagram.com and needs instagram_business_basic and instagram_business_manage_comments. Facebook Login calls graph.facebook.com and needs instagram_basic, instagram_manage_comments, and pages_read_engagement. Meta does not return private media or Story mentions.

mentionedMedia and mentionedComment query the mentioned_media and mentioned_comment field expansions for a known media or comment ID. They require Facebook Login. Meta’s Instagram Login mentions guide documents only the tags edge and replies through POST /{ig-user-id}/mentions, so an Instagram Login adapter raises unsupported_capability for these two methods before sending a request. See Meta’s mentions guides for Instagram Login and Facebook Login.

const tagged = await instagramNative.listTaggedMedia({ account, limit: 25, context });
const mentions = await instagramNative.mentions({ account, limit: 25, context });
const captionMention = await instagramNative.mentionedMedia({
  account,
  mediaId: "17873440459141021",
  context,
});
const commentMention = await instagramNative.mentionedComment({
  account,
  commentId: "17873440459141022",
  context,
});

Business discovery uses Facebook Login and maps to GET /{ig-user-id}?fields=business_discovery.username(...). The default field expansion requests profile counts and a bounded media edge; pass fields when you need a different expansion.

const profile = await instagramNative.businessDiscovery({
  account,
  username: "bluebottle",
  fields: "business_discovery.username(bluebottle){id,username,followers_count,media_count}",
  context,
});

Hashtag search is available only with Facebook Login, Instagram Public Content Access app review, and the instagram_basic permission. Call hashtagSearch to resolve a name through GET /ig_hashtag_search, then read top_media or recent_media with hashtagMedia.

const hashtag = await instagramNative.hashtagSearch({ account, hashtag: "coffee", context });
// The search response contains the resolved node in data[0].id.
const hashtagId = "17843857450040591"; // read this from hashtag.data[0].id
const recent = await instagramNative.hashtagMedia({
  account,
  hashtagId,
  kind: "recent",
  limit: 25,
  context,
});

If an Instagram Login adapter calls a Facebook Login-only operation, it raises SocialError with code: "unsupported_capability". Hashtag lookups are limited to 30 unique hashtags in a rolling seven-day period, and media returned by hashtag search cannot be commented on.

Read post metrics

Metrics are the values returned by Instagram Insights; absent metrics remain absent.

const metrics = await social.analytics.getPostMetrics(postRef);
for (const metric of metrics) console.log(metric.name, metric.value, metric.unit);

Receive webhooks

Pass the Meta app secret as webhookSecret to verify X-Hub-Signature-256 through adapter.webhooks.verify. Answer Meta’s GET verification with answerMetaWebhookChallenge from @opencoredev/social-sdk/server. Comments and live comments decode to comment.received and incoming messages to message.received; other fields stay unknown. See Process webhooks for the handler and the batching rules.

Native operations

The typed native module exposes Reels and Stories container publishing, post deletion, publishing-limit reads, comment moderation, comment replies, tagged and mentioned media reads, business discovery, and hashtag search. Product tagging and messaging are declared approval dependent and require the corresponding Meta products and app review.

Limits and requirements

  • Media must be publicly reachable over HTTPS; the adapter does not upload local bytes.
  • Published captions and media cannot be edited. The IG Media reference only documents comment_enabled as an update field, so posts.update is declared unsupported-by-platform.
  • Account authorization and Instagram professional-account eligibility remain application responsibilities.
  • Instagram Login uses graph.instagram.com; Facebook Login uses graph.facebook.com. Facebook Login operations require the Facebook token, connected Page access, and the permissions listed in Meta’s Instagram Platform documentation.
  • Meta may require Advanced Access or App Review for accounts outside your own organization, comment moderation, business discovery, and public hashtag content.

See the capability matrix and events.

Last updated on September 24, 2026