TypeScript SDK for the Xpoz social media intelligence platform. Query Twitter/X, Instagram, and Reddit data through a simple, typed interface.
Installation
npm install @xpoz/xpoz
Requires Node.js 18+.
Get an API Key
Sign up and get your token at https://xpoz.ai/get-token.
Once you have it, pass it directly or set the XPOZ_API_KEY environment variable:
export XPOZ_API_KEY=your-token-here
What is Xpoz?
Xpoz provides unified access to social media data across Twitter/X, Instagram, and Reddit. The platform indexes billions of posts, user profiles, and engagement metrics — making it possible to search, analyze, and export social media data at scale.
The SDK wraps Xpoz's MCP server, abstracting away transport, authentication, operation polling, and pagination into a clean developer-friendly API.
Features
40 data methods across Twitter, Instagram, Reddit, and TikTok
Fully async — all methods return
Promise<T>Automatic operation polling — long-running queries are abstracted away
Response types — choose between fast (immediate), paging (full pagination), or CSV export
Server-side pagination —
PaginatedResult<T>withnextPage(),getPage(n)CSV export —
exportCsv()on any paginated resultField selection — request only the fields you need
TypeScript-first — fully typed results with autocomplete support
Namespaced API —
client.twitter.*,client.instagram.*,client.reddit.*,client.tiktok.*,client.tracking.*
Quick Start
import { XpozClient, ResponseType } from "@xpoz/xpoz"; const client = new XpozClient({ apiKey: "your-api-key" }); await client.connect(); const user = await client.twitter.getUser("elonmusk"); console.log(`${user.name} — ${user.followersCount?.toLocaleString()} followers`); const results = await client.twitter.searchPosts("artificial intelligence", { startDate: "2025-01-01", }); for (const post of results.data) { console.log(post.text, post.likeCount); } await client.close();Authentication
Get your API key at https://xpoz.ai/get-token, then use it as follows:
// Pass API key directly const client = new XpozClient({ apiKey: "your-api-key" }); // Or use XPOZ_API_KEY environment variable const client = new XpozClient(); // Custom server URL (also reads XPOZ_SERVER_URL env var) const client = new XpozClient({ apiKey: "your-api-key", serverUrl: "https://xpoz.ai/mcp" }); // Custom operation timeout in milliseconds (default: 300000) const client = new XpozClient({ apiKey: "your-api-key", timeoutMs: 600_000 });Async Disposal
// Using Symbol.asyncDispose (Node.js 18.2+ with --experimental-vm-modules or TypeScript 5.2+) await using client = new XpozClient({ apiKey: "your-api-key" }); await client.connect(); const user = await client.twitter.getUser("elonmusk"); // client.close() is called automatically // Manual connect/close const client = new XpozClient({ apiKey: "your-api-key" }); await client.connect(); try { const results = await client.twitter.searchPosts("AI"); } finally { await client.close(); }Pagination
Methods that return large datasets use server-side pagination (100 items per page). These return a PaginatedResult<T> with built-in helpers:
const results = await client.twitter.searchPosts("AI"); results.data // TwitterPost[] — current page results.pagination.totalRows // total matching rows results.pagination.totalPages // total pages results.pagination.pageNumber // current page number results.pagination.pageSize // items per page (100) results.pagination.resultsCount // items on current page results.hasNextPage() // boolean // Navigate pages const page2 = await results.nextPage(); // fetch next page const page5 = await results.getPage(5); // jump to specific page // Export to CSV const csvUrl = await results.exportCsv(); // returns download URLField Selection
All methods accept a fields option. Use camelCase field names.
// Only fetch the fields you need (faster + less memory) const results = await client.twitter.searchPosts("AI", { fields: ["id", "text", "likeCount", "retweetCount", "createdAtDate"], }); const user = await client.twitter.getUser("elonmusk", { fields: ["id", "username", "name", "followersCount", "description"], });Requesting fewer fields significantly improves response time.
Response Types
Search and query methods support a responseType option that controls how results are returned. Import the ResponseType enum:
import { XpozClient, ResponseType } from "@xpoz/xpoz";Mode | Enum Value | Behavior | Best For |
Fast |
| Returns up to 300 results immediately, no async polling (default) | Quick queries, UI previews |
Paging |
| Async paginated query with full dataset access | Full analysis, large datasets |
CSV |
| Async bulk export, use | Data exports |
Fast mode (default)
The default behavior. Returns results immediately without polling. Use limit to constrain the number of results (max 300):
const results = await client.twitter.searchPosts("bitcoin", { startDate: "2025-01-01", responseType: ResponseType.Fast, limit: 50, }); console.log(results.data.length); // up to 50 results, returned immediatelyPaging mode
Returns paginated results with full totalRows, totalPages, and tableName for cursor-based navigation:
const results = await client.twitter.searchPosts("bitcoin", { startDate: "2025-01-01", responseType: ResponseType.Paging, // optional — this is the default }); console.log(results.pagination.totalRows); // total matching rows if (results.hasNextPage()) { const page2 = await results.nextPage(); }CSV mode
Initiates an async export. Call exportCsv() on the result to poll the export operation and get a download URL:
const results = await client.twitter.searchPosts("bitcoin", { startDate: "2025-01-01", responseType: ResponseType.Csv, }); const downloadUrl = await results.exportCsv(); console.log(downloadUrl); // URL to download the CSV fileMethods supporting responseType and limit
The following methods accept both responseType and limit:
twitter.getPostsByAuthor(),twitter.searchPosts(),twitter.getUsersByKeywords()instagram.getPostsByUser(),instagram.searchPosts(),instagram.getUsersByKeywords()reddit.searchPosts()tiktok.getPostsByUser(),tiktok.searchPosts(),tiktok.getUsersByKeywords()
These methods accept limit only:
twitter.searchUsers(),instagram.searchUsers(),reddit.searchUsers(),reddit.searchSubreddits()tiktok.searchUsers()
Query Syntax
The query parameter on all search* and get*ByKeywords methods supports a Lucene-style full-text syntax across Twitter, Instagram, and Reddit.
Exact phrase
Wrap in double quotes to require an exact match:
"machine learning" "climate change"
Keywords (any word)
Space-separated terms without quotes match posts containing any of the words:
AI crypto blockchain
Boolean operators
Use AND, OR, NOT (case-insensitive). A bare space is treated as OR — be explicit:
"deep learning" AND python tensorflow OR pytorch climate NOT politics
Grouping with parentheses
(AI OR "artificial intelligence") AND ethics (startup OR entrepreneur) NOT "venture capital"
Combined example
const results = await client.twitter.searchPosts( '("machine learning" OR "deep learning") AND python NOT spam', { startDate: "2025-01-01", language: "en", } );Note: Do not use from:, lang:, since:, or until: in the query string — use the dedicated parameters (authorUsername, language, startDate, endDate) instead.
Error Handling
import { XpozError, AuthenticationError, XpozConnectionError, OperationTimeoutError, OperationFailedError, OperationCancelledError, ResponseType, } from "@xpoz/xpoz"; try { const user = await client.twitter.getUser("nonexistent_user_12345"); } catch (e) { if (e instanceof OperationFailedError) { console.log(`Operation ${e.operationId} failed: ${e.operationError}`); } else if (e instanceof OperationTimeoutError) { console.log(`Timed out after ${Math.round(e.elapsedMs / 1000)}s`); } else if (e instanceof AuthenticationError) { console.log("Invalid API key"); } else if (e instanceof XpozError) { console.log(`Xpoz error: ${e.message}`); } }API Reference
Twitter — client.twitter
getUser(identifier, options?) -> Promise<TwitterUser>
Get a single Twitter user profile.
// By username (default) const user = await client.twitter.getUser("elonmusk"); // By numeric ID const user = await client.twitter.getUser("44196397", { identifierType: "id" });searchUsers(name, options?) -> Promise<TwitterUser[]>
Search users by name or username. Returns up to 10 results by default. Use limit to adjust.
const users = await client.twitter.searchUsers("elon"); const topFive = await client.twitter.searchUsers("elon", { limit: 5 });getUserConnections(username, connectionType, options?) -> Promise<PaginatedResult<TwitterUser>>
Get followers or following for a user.
const followers = await client.twitter.getUserConnections("elonmusk", "followers"); const following = await client.twitter.getUserConnections("elonmusk", "following");getUsersByKeywords(query, options?) -> Promise<PaginatedResult<TwitterUser>>
Find users who authored posts matching a keyword query. Supports responseType and limit.
const users = await client.twitter.getUsersByKeywords('"machine learning"', { fields: ["username", "name", "followersCount"], responseType: ResponseType.Fast, limit: 20, });getPostsByIds(postIds, options?) -> Promise<TwitterPost[]>
Get 1-100 posts by their IDs.
const tweets = await client.twitter.getPostsByIds(["1234567890", "0987654321"]);
getPostsByAuthor(identifier, options?) -> Promise<PaginatedResult<TwitterPost>>
Get all posts by an author with optional date filtering. Supports responseType and limit.
const results = await client.twitter.getPostsByAuthor("elonmusk", { startDate: "2025-01-01", responseType: ResponseType.Fast, limit: 100, });searchPosts(query, options?) -> Promise<PaginatedResult<TwitterPost>>
Full-text search with filters. Supports exact phrases ("machine learning"), boolean operators (AI AND python), and parentheses. Supports responseType and limit.
const results = await client.twitter.searchPosts('"artificial intelligence" AND ethics', { startDate: "2025-01-01", endDate: "2025-06-01", language: "en", fields: ["id", "text", "likeCount", "authorUsername", "createdAtDate"], responseType: ResponseType.Fast, limit: 50, });getRetweets(postId, options?) -> Promise<PaginatedResult<TwitterPost>>
Get retweets of a specific post (database only).
const retweets = await client.twitter.getRetweets("1234567890");getQuotes(postId, options?) -> Promise<PaginatedResult<TwitterPost>>
Get quote tweets of a specific post.
const quotes = await client.twitter.getQuotes("1234567890");getComments(postId, options?) -> Promise<PaginatedResult<TwitterPost>>
Get replies to a specific post.
const comments = await client.twitter.getComments("1234567890");getPostInteractingUsers(postId, interactionType, options?) -> Promise<PaginatedResult<TwitterUser>>
Get users who interacted with a post. interactionType: "commenters", "quoters", "retweeters".
const commenters = await client.twitter.getPostInteractingUsers("1234567890", "commenters");countPosts(phrase, options?) -> Promise<number>
Count tweets containing a phrase within a date range.
const count = await client.twitter.countPosts("bitcoin", { startDate: "2025-01-01" }); console.log(`${count.toLocaleString()} tweets mention bitcoin`);Instagram — client.instagram
getUser(identifier, options?) -> Promise<InstagramUser>
const user = await client.instagram.getUser("instagram"); console.log(`${user.fullName} — ${user.followerCount?.toLocaleString()} followers`);searchUsers(name, options?) -> Promise<InstagramUser[]>
Search users by name. Use limit to adjust the number of results.
const users = await client.instagram.searchUsers("nasa"); const topThree = await client.instagram.searchUsers("nasa", { limit: 3 });getUserConnections(username, connectionType, options?) -> Promise<PaginatedResult<InstagramUser>>
const followers = await client.instagram.getUserConnections("instagram", "followers");getUsersByKeywords(query, options?) -> Promise<PaginatedResult<InstagramUser>>
Find users who authored posts matching a keyword query. Supports responseType and limit.
const users = await client.instagram.getUsersByKeywords('"sustainable fashion"', { responseType: ResponseType.Fast, limit: 20, });getPostsByIds(postIds, options?) -> Promise<InstagramPost[]>
Post IDs must be in strong_id format: "media_id_user_id" (e.g. "3606450040306139062_4836333238").
const posts = await client.instagram.getPostsByIds(["3606450040306139062_4836333238"]);
getPostsByUser(identifier, options?) -> Promise<PaginatedResult<InstagramPost>>
Get all posts by a user. Supports responseType and limit.
const results = await client.instagram.getPostsByUser("nasa", { responseType: ResponseType.Fast, limit: 50, });searchPosts(query, options?) -> Promise<PaginatedResult<InstagramPost>>
Full-text search with filters. Supports responseType and limit.
const results = await client.instagram.searchPosts("travel photography", { responseType: ResponseType.Fast, limit: 30, });getComments(postId, options?) -> Promise<PaginatedResult<InstagramComment>>
const comments = await client.instagram.getComments("3606450040306139062_4836333238");getPostInteractingUsers(postId, interactionType, options?) -> Promise<PaginatedResult<InstagramUser>>
interactionType: "commenters", "likers".
const likers = await client.instagram.getPostInteractingUsers( "3606450040306139062_4836333238", "likers" );
Reddit — client.reddit
getUser(username, options?) -> Promise<RedditUser>
const user = await client.reddit.getUser("spez"); console.log(`${user.username} — ${user.totalKarma?.toLocaleString()} karma`);searchUsers(name, options?) -> Promise<RedditUser[]>
Search users by name. Use limit to adjust the number of results.
const users = await client.reddit.searchUsers("spez"); const topThree = await client.reddit.searchUsers("spez", { limit: 3 });getUsersByKeywords(query, options?) -> Promise<PaginatedResult<RedditUser>>
const users = await client.reddit.getUsersByKeywords('"machine learning"', { subreddit: "MachineLearning", });searchPosts(query, options?) -> Promise<PaginatedResult<RedditPost>>
sort: "relevance", "hot", "top", "new", "comments". time: "hour", "day", "week", "month", "year", "all". Supports responseType and limit.
const results = await client.reddit.searchPosts("python tutorial", { subreddit: "learnpython", sort: "top", time: "month", responseType: ResponseType.Fast, limit: 25, });getPostWithComments(postId, options?) -> Promise<RedditPostWithComments>
Returns an object with the post and its comments.
const result = await client.reddit.getPostWithComments("abc123"); console.log(result.post.title); for (const comment of result.comments) { console.log(` ${comment.authorUsername}: ${comment.body?.slice(0, 80)}`); }searchComments(query, options?) -> Promise<PaginatedResult<RedditComment>>
const comments = await client.reddit.searchComments("helpful tip", { subreddit: "LifeProTips", });searchSubreddits(query, options?) -> Promise<RedditSubreddit[]>
Search subreddits by name. Use limit to adjust the number of results.
const subs = await client.reddit.searchSubreddits("machine learning"); const topFive = await client.reddit.searchSubreddits("machine learning", { limit: 5 });getSubredditWithPosts(subredditName, options?) -> Promise<SubredditWithPosts>
const result = await client.reddit.getSubredditWithPosts("wallstreetbets"); console.log(`r/${result.subreddit.displayName} — ${result.subreddit.subscribersCount?.toLocaleString()} members`); for (const post of result.posts) { console.log(` ${post.title} (${post.score} points)`); }getSubredditsByKeywords(query, options?) -> Promise<PaginatedResult<RedditSubreddit>>
const subs = await client.reddit.getSubredditsByKeywords("cryptocurrency");TikTok — client.tiktok
getUser(identifier, options?) -> Promise<TiktokUser>
const user = await client.tiktok.getUser("charlidamelio"); console.log(`${user.nickname} — ${user.followerCount?.toLocaleString()} followers`); // By numeric ID const user = await client.tiktok.getUser("123456789", { identifierType: "id" });searchUsers(name, options?) -> Promise<TiktokUser[]>
Search users by name. Use limit to adjust the number of results.
const users = await client.tiktok.searchUsers("charli"); const topFive = await client.tiktok.searchUsers("charli", { limit: 5 });getUsersByKeywords(query, options?) -> Promise<PaginatedResult<TiktokUser>>
Find users who authored posts matching a keyword query. Supports responseType and limit.
const users = await client.tiktok.getUsersByKeywords('"machine learning"', { responseType: ResponseType.Fast, limit: 20, });getPostsByIds(postIds, options?) -> Promise<TiktokPost[]>
Get 1-100 posts by their IDs.
const posts = await client.tiktok.getPostsByIds(["7123456789012345678"]);
getPostsByUser(identifier, options?) -> Promise<PaginatedResult<TiktokPost>>
Get all posts by a user. Supports responseType and limit.
const results = await client.tiktok.getPostsByUser("charlidamelio", { startDate: "2025-01-01", responseType: ResponseType.Fast, limit: 50, });searchPosts(query, options?) -> Promise<PaginatedResult<TiktokPost>>
Full-text search with filters. Supports responseType and limit.
const results = await client.tiktok.searchPosts("travel vlog", { startDate: "2025-01-01", responseType: ResponseType.Fast, limit: 30, });getComments(postId, options?) -> Promise<PaginatedResult<TiktokComment>>
const comments = await client.tiktok.getComments("7123456789012345678");Tracking — client.tracking
Manage tracked items (keywords, users, subreddits) that Xpoz monitors on your behalf. Import the enums to build items:
import { XpozClient, TrackedItemType, TrackedItemPlatform } from "@xpoz/xpoz";getTrackedItems() -> Promise<TrackedItem[]>
List all currently tracked items on your account.
const items = await client.tracking.getTrackedItems(); for (const item of items) { console.log(`${item.platform} / ${item.type}: ${item.phrase}`); }addTrackedItems(items) -> Promise<AddTrackedItemsResult>
Add one or more items to track.
const result = await client.tracking.addTrackedItems([ { phrase: "bitcoin", type: TrackedItemType.Keyword, platform: TrackedItemPlatform.Twitter }, { phrase: "nasa", type: TrackedItemType.User, platform: TrackedItemPlatform.Instagram }, ]); console.log(`Added ${result.addedCount} items (${result.currentCount}/${result.maxTrackedItems} used)`);removeTrackedItems(items) -> Promise<RemoveTrackedItemsResult>
Remove one or more tracked items.
const result = await client.tracking.removeTrackedItems([ { phrase: "bitcoin", type: TrackedItemType.Keyword, platform: TrackedItemPlatform.Twitter }, ]); console.log(`Removed ${result.removedCount} items`);Type Models
All fields are optional and typed as their respective TypeScript types. Unknown fields are preserved on the object.
TwitterPost
Field | Type | Description |
|
| Post ID |
|
| Post text content |
|
| Author's user ID |
|
| Author's username |
|
| Number of likes |
|
| Number of retweets |
|
| Number of replies |
|
| Number of quotes |
|
| Number of impressions |
|
| Number of bookmarks |
|
| Language code |
|
| Hashtags in tweet |
|
| Mentioned usernames |
|
| Media attachment URLs |
|
| URLs in tweet |
|
| Country (if geo-tagged) |
|
| Creation timestamp |
|
| Creation date (YYYY-MM-DD) |
|
| Thread conversation ID |
|
| ID of quoted tweet |
|
| ID of parent tweet |
|
| Whether this is a retweet |
|
| Sensitive content flag |
TwitterUser
Field | Type | Description |
|
| User ID |
|
| Username (handle) |
|
| Display name |
|
| Bio text |
|
| Location string |
|
| Verification status |
|
| Verification type |
|
| Number of followers |
|
| Number of following |
|
| Total tweets |
|
| Total likes |
|
| Profile picture URL |
|
| Account creation timestamp |
|
| Account location |
|
| Inauthenticity flag |
|
| Inauthenticity probability |
|
| Tweeting frequency |
InstagramPost
Field | Type | Description |
|
| Post ID (strong_id format) |
|
| Post caption |
|
| Author username |
|
| Author display name |
|
| Number of likes |
|
| Number of comments |
|
| Number of reshares |
|
| Video play count |
|
| Media type |
|
| Image URL |
|
| Video URL |
|
| Creation date |
InstagramUser
Field | Type | Description |
|
| User ID |
|
| Username |
|
| Display name |
|
| Bio text |
|
| Private account |
|
| Verified status |
|
| Followers |
|
| Following |
|
| Total posts |
|
| Profile picture URL |
InstagramComment
Field | Type | Description |
|
| Comment ID |
|
| Comment text |
|
| Author username |
|
| Parent post ID |
|
| Number of likes |
|
| Reply count |
|
| Creation date |
RedditPost
Field | Type | Description |
|
| Post ID |
|
| Post title |
|
| Post body text |
|
| Author username |
|
| Subreddit name |
|
| Net score |
|
| Upvote count |
|
| Comment count |
|
| Post URL |
|
| Reddit permalink |
|
| Self post (text only) |
|
| NSFW flag |
|
| Creation date |
RedditUser
Field | Type | Description |
|
| User ID |
|
| Username |
|
| Total karma |
|
| Link karma |
|
| Comment karma |
|
| Reddit Gold status |
|
| Moderator status |
|
| Profile bio |
|
| Account creation date |
RedditComment
Field | Type | Description |
|
| Comment ID |
|
| Comment text |
|
| Author username |
|
| Parent post ID |
|
| Net score |
|
| Nesting depth |
|
| Is OP |
|
| Creation date |
RedditSubreddit
Field | Type | Description |
|
| Subreddit ID |
|
| Subreddit name |
|
| Subreddit title |
|
| Short description |
|
| Full description |
|
| Subscriber count |
|
| Active users |
|
| NSFW flag |
|
| Creation date |
TiktokPost
Field | Type | Description |
|
| Post ID |
|
| Post caption/description |
|
| Language of description |
|
| Author user ID |
|
| Author username |
|
| Author display name |
|
| Number of likes |
|
| Number of comments |
|
| Video play count |
|
| Number of collects/saves |
|
| Number of downloads |
|
| Number of forwards/shares |
|
| Thumbnail URL |
|
| Post type code |
|
| Private post flag |
|
| Creation timestamp |
|
| Creation date (YYYY-MM-DD) |
TiktokUser
Field | Type | Description |
|
| User ID |
|
| Username |
|
| Display name |
|
| Bio text |
|
| Secure user ID |
|
| Profile picture URL |
|
| Private account |
|
| Verified status |
|
| Number of followers |
|
| Number of following |
|
| Total likes received |
|
| Total posts |
|
| Profile language |
|
| Account region |
|
| Account creation date |
TiktokComment
Field | Type | Description |
|
| Comment ID |
|
| Parent post ID |
|
| Author user ID |
|
| Author username |
|
| Comment text |
|
| Number of likes |
|
| Creation timestamp |
|
| Creation date (YYYY-MM-DD) |
TrackedItem
Field | Type | Description |
|
| Keyword, username, or subreddit name to track |
|
|
|
|
|
|
AddTrackedItemsResult
Field | Type | Description |
|
| Whether the operation succeeded |
|
| Number of items added |
|
| Status message |
|
| Total tracked items after addition |
|
| Plan limit for tracked items |
|
| Current plan name |
RemoveTrackedItemsResult
Field | Type | Description |
|
| Whether the operation succeeded |
|
| Number of items removed |
|
| Status message |
Composite Types
RedditPostWithComments — returned by getPostWithComments():
post: RedditPostcomments: RedditComment[]commentsPagination: PaginationInfo | nullcommentsTableName: string | null
SubredditWithPosts — returned by getSubredditWithPosts():
subreddit: RedditSubredditposts: RedditPost[]postsPagination: PaginationInfo | nullpostsTableName: string | null
Environment Variables
Variable | Description | Default |
| API key for authentication | — |
| MCP server URL |
Testing
Tests hit the live Xpoz API and require a valid API key:
XPOZ_API_KEY=your-api-key npx vitest run
License
MIT
