Search posts and actors
Search posts and actors across provider indexes, with platform-specific scopes, query limits, result fields, and cursor pagination for each supported platform.
Use social.search.posts(account, input, options?) for normalized post search.
It returns Page<JsonObject>:
const page = await social.search.posts(account, {
query: "open source",
scope: "recent",
limit: 25,
});
The input takes query, and optionally scope ("recent" or "all"),
cursor, limit, startTime, and endTime. Items keep their provider fields.
Reuse nextCursor only with the same account, query, scope, time bounds, and
page size. Cursors are bound to the query, not to an authorization grant.
iteratePosts(account, input, options) follows cursors lazily within
maxPages, maxItems, and signal bounds.
The examples assume one social client built with the platform’s adapter and an
account from connectedAccountRef. Native calls take a native handle from
social.native("default", { acknowledgeUnsafe: true }) and, where the method
requires it, the operation context your application passes to adapter calls.
Platform support
X
Normalized search.posts with recent and all scopes, plus native
searchRecentPosts and searchAllPosts. Scopes: tweet.read and
users.read.
Recent search (scope: "recent", the default) covers the last 7 days and
accepts a limit of 10 to 100. Full-archive search (scope: "all") needs
eligible pay-per-use or Enterprise access and accepts 10 to 500.
Both scopes use auth.accessToken when it is configured, and fall back to
appBearerToken only when there is no user token. With neither configured,
the call fails before any request.
const page = await social.search.posts(account, {
query: "distributed systems -is:retweet",
limit: 25,
});By default the adapter requests IDs, text, author, timestamps, metrics,
language, entities, attachments, and referenced tweets. Reposts are included
unless the query excludes them with -is:retweet. The SDK rejects queries
longer than 4096 characters; X applies its own query-length and operator
limits by access level, so a shorter query can still be rejected upstream. See
X’s query guide.
Invalid bounds or timestamps raise invalid_input; missing access raises
missing_permission.
The native methods expose X tweet fields, expansions, user fields, and media
fields:
const page = await native.searchRecentPosts({
account,
search: { query: "open source", limit: 10 },
context,
});For posts as they are published, the native stream method reads X’s filtered
stream, and addStreamRules, listStreamRules, and deleteStreamRules manage
the matching rules. Both use appBearerToken and need pay-per-use or
Enterprise access. The stream opens one connection per iteration and never
reconnects on its own. See X filtered stream.
Bluesky
Normalized search.posts, plus native post and actor search. No declared
scope; the adapter calls public app.bsky endpoints.
The adapter takes service, did, and an optional accessJwt. Post results
are provider records and may include metadata.hitsTotal. Native post search
adds sort, author, mentions, language, domain, URL, and tags.
const page = await social.search.posts(account, { query: "distributed systems", limit: 25 });Actor and profile search is native only and returns actor page records:
const actors = await native.searchActors({ account, query: "opencore", limit: 10 });Invalid or missing sessions raise unauthorized or missing_permission.
Threads
Normalized search.posts, plus native keyword and tag search. Scopes:
threads_basic and threads_keyword_search.
Search uses the authorized user ID and access token. Without
threads_keyword_search approval, results are limited to the authorized user’s
posts. The normalized page holds provider post objects and a continuation
cursor.
Normalized search maps startTime and endTime to Threads’ since and
until, and accepts a limit of 1 to 100. It rejects scope: "all". It
sends no ordering, so Threads uses its default TOP ranking; that is ranking,
not a recent-only time horizon.
Use native search for RECENT ordering, tag mode, media type, or author
filters:
const result = await native.search({
account,
query: "open source",
searchType: "RECENT",
searchMode: "KEYWORD",
context,
});Provider permission failures raise missing_permission.
Threads has no profile search, so the manifest declares profiles.search as
unsupported-by-platform. Profile discovery only matches an exact username;
use social.graph.getProfile(account, { handle }) for that.
YouTube
Normalized search.posts with recent only, plus native search.
Scope: https://www.googleapis.com/auth/youtube.readonly.
YouTube uses an OAuth accessToken and a channelId. Search is global: it
queries all public YouTube videos, not only the configured channel. Results are
video search records with provider IDs and snippet.
const page = await social.search.posts(account, { query: "distributed systems", limit: 50 });scope: "all" raises invalid_input. The default limit is 25 and the maximum
is 50. Time bounds map to publishedAfter and publishedBefore.
search.list has its own quota bucket, separate from the 10,000 daily units
the other endpoints share. By default a project gets 100 search calls per day,
each costing 1 unit, and every additional page is another call. A missing scope
raises missing_permission. Source: YouTube quota costs,
accessed 2026-10-05.
Native hashtag lookup and hashtag media only. There is no normalized
search.posts. Requires Facebook Login (auth.flavor: "facebook-login").
Look up the hashtag ID first, then read its media with kind: "recent" or
"top":
import type { JsonObject, JsonValue } from "@opencoredev/social-sdk";
const isList = (value: JsonValue | undefined): value is readonly JsonValue[] =>
Array.isArray(value);
const isObject = (value: JsonValue | undefined): value is JsonObject =>
typeof value === "object" && value !== null && !Array.isArray(value);
const found = await native.hashtagSearch({ account, hashtag: "opensource", context });
const first = isList(found["data"]) ? found["data"][0] : undefined;
const hashtagId = isObject(first) ? first["id"] : undefined;
if (typeof hashtagId === "string") {
const media = await native.hashtagMedia({
account,
hashtagId,
kind: "recent",
limit: 25,
context,
});
}hashtagSearch returns { data: [{ id }] }. The media page uses
paging.cursors.after. With Instagram Login, both methods raise
unsupported_capability before any request.
Meta limits each Instagram account to 30 unique hashtags in a rolling 7-day window, and recent media covers only the last 24 hours. See Meta’s hashtag search reference.
Unsupported platforms and errors
TikTok, LinkedIn, and Facebook do not declare bundled normalized
search.posts; the facade raises unsupported_capability. Handle
invalid_input for query, scope, limit, timestamps, or cursors,
missing_permission or unauthorized for credentials,
rate_limited for provider throttling, and upstream_failure for provider
errors. Do not log tokens or private result data.