Read posts and analytics
Read feeds, single posts, likes, and current post metrics through the normalized API, with platform tabs covering scopes, limits, and native reads.
To get post analytics from X, Instagram and LinkedIn in one place with code,
register each platform’s adapter in one Social SDK client and call
social.analytics.getPostMetrics with a reference to each post. Each call reads
that platform’s own API and returns a list of metrics with a name, a numeric
value, a unit, a period, and a source. X returns likes, reposts,
replies, quotes, impressions, and bookmarks; Instagram returns likes, comments,
saved, shares, and reach from media insights; LinkedIn returns likes and
comments. A metric the platform doesn’t return is left out, not set to zero.
npm install @opencoredev/social-sdkpnpm add @opencoredev/social-sdkyarn add @opencoredev/social-sdkbun add @opencoredev/social-sdknub add @opencoredev/social-sdkaube add @opencoredev/social-sdkimport { createSocial, platformPostRef } from "@opencoredev/social-sdk";
import { instagram } from "@opencoredev/social-sdk/instagram";
import { linkedin } from "@opencoredev/social-sdk/linkedin";
import { x } from "@opencoredev/social-sdk/x";
const social = createSocial({
backends: {
x: x({
auth: { userId: process.env.X_USER_ID!, accessToken: process.env.X_ACCESS_TOKEN! },
}),
instagram: instagram({
auth: {
accountId: process.env.IG_ACCOUNT_ID!,
accessToken: process.env.IG_ACCESS_TOKEN!,
flavor: "instagram-login",
},
}),
linkedin: linkedin({
auth: {
accessToken: process.env.LINKEDIN_ACCESS_TOKEN!,
author: "urn:li:organization:12345",
},
apiVersion: "202610",
}),
},
});
const posts = [
platformPostRef({
backend: "x",
platform: "x",
accountId: process.env.X_USER_ID!,
postId: "1850000000000000000",
}),
platformPostRef({
backend: "instagram",
platform: "instagram",
accountId: process.env.IG_ACCOUNT_ID!,
postId: "17900000000000000",
}),
platformPostRef({
backend: "linkedin",
platform: "linkedin",
accountId: "urn:li:organization:12345",
postId: "urn:li:share:7000000000000000000",
}),
];
for (const post of posts) {
const metrics = await social.analytics.getPostMetrics(post);
console.log(post.platform, metrics.map((metric) => `${metric.name}=${metric.value}`).join(" "));
}
Each request goes straight to the platform with your own app’s tokens, so
the access rules are the platform’s: X needs a user token and current X API
access, Instagram needs a professional account plus instagram_business_basic
and instagram_business_manage_insights with Instagram Login, and LinkedIn Page
posts need r_organization_social and r_organization_social_feed. The
platform tabs below and
Analytics reports cover the rest of each route’s access. Units
and periods differ by platform, so match period and source before you add
numbers from different networks. For what each platform requires before you
can post or read, see the social media APIs guide.
This page covers post and feed reads, post and account metrics, and provider-native likes lists. See Search, Social graph, Notifications, and Analytics reports for those separate surfaces.
Normalized API
The facade returns provider-shaped JsonObject items in a Page:
const page = await social.posts.list(account, { limit: 20 });
const one = await social.posts.get(platformPostRef);
Pass nextCursor back unchanged with the same account and options. Cursors
are opaque. posts.iterate(account, { maxPages, maxItems }) is lazy and
bounded. Page-size limits differ by route; 20 is valid on every direct adapter
(TikTok allows at most 20, YouTube and Instagram at most 50, the others 100).
Items are projections, not full provider objects. Most adapters copy a fixed list of scalar fields and drop nested objects and arrays, so the tabs below list what each route actually returns.
The examples assume one client and one connected account for the adapter you configure below:
const social = createSocial({ backend: adapter });
const account = connectedAccountRef({ backend: "default", platform: "x", accountId: "123" });
Post metrics
const metrics = await social.analytics.getPostMetrics(platformPostRef);
const accountMetrics = await social.analytics.getAccountMetrics(account);
Both return MetricValue[]. An omitted metric means the provider did not
return it, not zero. Bounded date-range reports are covered in
Analytics reports.
Platform support
X
Reads: posts list, get, and iterate. Metrics: account and post, plus native likes lists. Access: user scopes and your X API tier.
Use a user OAuth token. appBearerToken is for app-only operations such as
full-archive search, not user timelines.
import { x } from "@opencoredev/social-sdk/x";
const adapter = x({ auth: { userId: "123", accessToken: process.env.X_ACCESS_TOKEN! } });posts.list and posts.get return id, text, author_id, created_at,
and conversation_id. They do not include public_metrics or referenced
posts; read counts with analytics.getPostMetrics. User timeline, home timeline, mentions, liked posts,
and liking users are native operations: userPosts, homeTimeline,
mentions, likedPosts, and likingUsers. They need the matching X read
capability, user scopes, and an eligible API tier. Missing access raises
missing_permission; throttling raises rate_limited.
Bluesky
Reads: author feed through posts.list, plus native getPost and
getPostThread. Metrics: account and post, plus native likes lists.
Access: repo. Feed page size is 1 to 100.
Use a PDS service and DID with an access JWT, or an OAuth session.
import { bluesky } from "@opencoredev/social-sdk/bluesky";
const adapter = bluesky({
auth: {
service: "https://bsky.social",
did: "did:plc:example",
accessJwt: process.env.BSKY_JWT!,
},
});posts.list reads the account’s author feed. Each item is
{ post, reason? }, where post has uri, cid, the like, repost, reply,
and quote counts, indexedAt, and public author fields (did, handle,
displayName, avatar). Validated nested fields include record text, timestamps,
facets, reply references, and embeds; AppView embeds; labels; and repost reasons.
Image, video, external-link, and quoted-post embeds are retained, including quotes
with attached media. Fields outside the public allowlist are omitted. Use native
getPost({ uri }) or getPostThread({ uri }) for additional record or thread data;
neither returns a cursor. The normalized post ref can carry the native uri
and cid. Native getLikes lists users who liked a post; getActorLikes
lists posts an actor liked. An invalid page size raises invalid_input.
Threads
Reads: list and get. Metrics: account and post. Access: user token. Feed page size is 1 to 100.
Use the Threads user ID and a long-lived user access token.
import { threads } from "@opencoredev/social-sdk/threads";
const adapter = threads({ auth: { userId: "th_123", accessToken: process.env.THREADS_TOKEN! } });List items contain id, text, username, media_type, permalink, and
timestamp. The after cursor is opaque. posts.get and search.posts are
normalized; posts.get returns the post fields the native getPost reads.
Threads has post and selected account insights but no normalized likes list.
Mentions and keyword search options beyond the normalized input are native.
Reads: media list and get. Metrics: account and post insights. Access: a professional account.
Use a professional account ID and access token, and pick Instagram Login or Facebook Login. Business discovery and hashtag features need Facebook Login.
import { instagram } from "@opencoredev/social-sdk/instagram";
const adapter = instagram({
auth: {
accountId: "17840000000000000",
accessToken: process.env.IG_TOKEN!,
flavor: "instagram-login",
},
});Media includes Graph API fields such as ID, caption, media type, permalink,
timestamp, and URLs. Insights can include likes, comments, saves, shares,
and reach. Mentions, tagged media, and business discovery are native. The
tags mentions edge works with both login flavors.
Missing permissions raise missing_permission.
YouTube
Reads: uploads and get. Metrics: channel and video statistics. Access: an OAuth token and the channel ID.
Normalized posts are videos from the channel uploads feed.
import { youtube } from "@opencoredev/social-sdk/youtube";
const adapter = youtube({
auth: { accessToken: process.env.YOUTUBE_TOKEN!, channelId: "UC_example" },
});posts.get returns id, snippet (title, description, channelId,
publishedAt), and status (privacyStatus, uploadStatus,
selfDeclaredMadeForKids). It does not include statistics; read view, like,
and comment counts with analytics.getPostMetrics. Playlists and
playlist items are native. Reports need the separate
https://www.googleapis.com/auth/yt-analytics.readonly scope and a bounded
query. An inaccessible video raises an error instead of returning an empty item.
Reads: author feed and get. Metrics: post, and account for
organizations only. Access: r_member_social or r_organization_social.
Use a person or organization author URN and a monthly API version.
import { linkedin } from "@opencoredev/social-sdk/linkedin";
const adapter = linkedin({
auth: { accessToken: process.env.LINKEDIN_TOKEN!, author: "urn:li:person:abc" },
apiVersion: "202609",
});Person authors need r_member_social; organization authors need
r_organization_social. The author feed uses an opaque offset cursor.
Organization account metrics need an administrator grant such as
rw_organization_admin. Member-profile account analytics are not supported.
Invalid URNs or API versions raise invalid_config.
TikTok
Reads: video list and get. Metrics: account stats and per-video
counts. Access: Display API with user.info.stats and video.list.
TikTok reads use an access token, the creator’s Open ID, and verified media origins.
import { tiktok } from "@opencoredev/social-sdk/tiktok";
const adapter = tiktok({
auth: { accessToken: process.env.TIKTOK_TOKEN!, openId: "open-id" },
verifiedMediaOrigins: ["https://media.example.com"],
});Video items include ID, creation time, title, description, dimensions, share
URL, and like, comment, share, and view counts. getPostMetrics reads the
same four counts through the video query endpoint and needs video.list.
Account counts need user.info.stats. TikTok has no normalized likes list.
Errors and unsupported platforms
An unavailable capability raises unsupported_capability. Authentication
failures use reconnect_required or unauthorized; denied scopes use
missing_permission; bad cursors, references, or limits use invalid_input;
throttling uses rate_limited; and inaccessible resources can be not_found
or gone. Do not turn errors into empty pages or zero metrics.
The direct Facebook adapter reads Page posts through posts.list and
posts.get, comments, post and Page counts, and daily Page Insights; see the
Facebook page. Managed backends that cover Facebook
declare their own support. Inspect social.capabilities() before enabling a
feature.