Python SDK for the Xpoz social media intelligence platform. Query Twitter/X, Instagram, Reddit, and TikTok data through a simple, Pythonic interface.
pip install xpozRequires Python 3.10+.
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-hereXpoz provides unified access to social media data across Twitter/X, Instagram, Reddit, and TikTok. 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.
- 42 data methods across Twitter, Instagram, Reddit, and TikTok
- Sync and async clients —
XpozClientandAsyncXpozClient - Automatic operation polling — long-running queries are abstracted away
- Response modes —
ResponseType.FASTfor quick limited results,PAGINGfor full pagination,CSVfor export - Server-side pagination —
PaginatedResultwithnext_page(),get_page(n) - CSV export —
export_csv()on any paginated result - Field selection — request only the fields you need in Pythonic snake_case
- Pydantic v2 models — fully typed results with autocomplete support
- Namespaced API —
client.twitter.*,client.instagram.*,client.reddit.*,client.tiktok.*
from xpoz import XpozClient
client = XpozClient("your-api-key")
user = client.twitter.get_user("elonmusk")
print(f"{user.name} — {user.followers_count:,} followers")
results = client.twitter.search_posts("artificial intelligence", start_date="2025-01-01")
for tweet in results.data:
print(tweet.text, tweet.like_count)
client.close()Get your API key at https://xpoz.ai/get-token, then use it as follows:
# Pass API key directly
client = XpozClient("your-api-key")
# Or use XPOZ_API_KEY environment variable
import os
os.environ["XPOZ_API_KEY"] = "your-api-key"
client = XpozClient()
# Custom server URL (also reads XPOZ_SERVER_URL env var)
client = XpozClient("your-api-key", server_url="https://xpoz.ai/mcp")
# Custom operation timeout (default: 300 seconds)
client = XpozClient("your-api-key", timeout=600)Want to try the SDK before signing up? Mint a free trial token (no account needed, valid for 5 days):
curl -X POST https://api.xpoz.ai/api/trial/token \
-H "Content-Type: application/json" \
-d '{"source": "sdk"}'
# -> { "success": true, "data": { "accessKey": "TRIAL...", "expiresInSeconds": 432000 }, ... }The source field is required — it identifies where the trial token request came from (e.g. skills, a specific page, sdk, cli).
Then use the returned token (it starts with TRIAL) as your API key:
# Optional: try without your own account
client = XpozClient("TRIAL...") # the token from the curl response above
user = client.twitter.get_user("elonmusk")The trial token is rate-limited and intentionally restricted:
- Read-only data tools only — search and lookup methods across Twitter, Instagram, Reddit, and TikTok. Account, tracking, and operation-management methods are not available and return an upgrade prompt.
- Up to 5 results per call — every response is capped at 5 items.
response_typeis forced tofast, so pagination (PAGING) and CSV export (CSV) are unavailable. - Cached data only — trial reads from the database and does not trigger live on-demand crawling, so the very latest posts may not appear.
For full result limits, pagination, CSV export, and live data, get your own API key.
# Sync — auto-closes on exit
with XpozClient("your-api-key") as client:
user = client.twitter.get_user("elonmusk")
# Async
import asyncio
from xpoz import AsyncXpozClient
async def main():
async with AsyncXpozClient("your-api-key") as client:
user = await client.twitter.get_user("elonmusk")
results = await client.twitter.search_posts("AI")
page2 = await results.next_page()
asyncio.run(main())Methods that return large datasets use server-side pagination (100 items per page). These return a PaginatedResult[T] with built-in helpers:
results = client.twitter.search_posts("AI")
results.data # list[TwitterPost] — current page
results.pagination.total_rows # total matching rows
results.pagination.total_pages # total pages
results.pagination.page_number # current page number
results.pagination.page_size # items per page (100)
results.pagination.results_count # items on current page
results.has_next_page() # bool
# Navigate pages
page2 = results.next_page() # fetch next page
page5 = results.get_page(5) # jump to specific page
# Export to CSV
csv_url = results.export_csv() # returns download URLInstagram live methods bypass the database and fetch straight from the crawler API, so results are always current. They page with an opaque cursor rather than page numbers, and return a CursorResult[T]:
page = client.instagram_live.search_posts("travel", fields=["id", "caption"])
page.data # list[InstagramPost] — this page
page.has_more # bool — another page is available upstream
page.next_page_cursor # opaque token for the next call
page.has_next_page() # bool
page = page.next_page() # fetch the next page
# Walk every page
for post in page.iter_items():
...Cursor paging is forward-only: there is no get_page(n), total_pages, or total_rows, because the upstream API does not report them. Drive iteration off has_more and the cursor — never off the item count, since a page can be short or empty while has_more is still true.
These routes always trigger a live fetch, so they are not available on trial access and raise AuthenticationError (HTTP 403).
| Method | Returns |
|---|---|
search_posts(query) |
CursorResult[InstagramPost] |
get_posts_by_user(identifier) |
CursorResult[InstagramPost] |
get_post(post_id) |
InstagramPost | None |
get_comments(post_id) |
CursorResult[InstagramComment] |
get_post_interacting_users(post_id, interaction_type) |
CursorResult[InstagramUser] |
search_users(name) |
CursorResult[InstagramUser] |
get_user(identifier) |
InstagramUser | None |
get_user_connections(identifier, connection_type) |
CursorResult[InstagramUser] |
interaction_type is "commenters" or "likers"; connection_type is "followers" or "following".
Live methods talk to the Xpoz REST API rather than the MCP server. Override the base URL with XpozClient(api_url=...) or the XPOZ_API_URL environment variable.
Methods that return PaginatedResult support a response_type parameter to control how results are fetched. Import the ResponseType enum:
from xpoz import XpozClient, ResponseTypeReturns up to limit results directly — no polling, no pagination. This is the default behavior when response_type is not specified.
results = client.twitter.search_posts(
"bitcoin",
limit=10,
fields=["id", "text", "like_count"]
)
# Equivalent to response_type=ResponseType.FAST
for tweet in results.data:
print(tweet.text)Returns full paginated results (100 items per page). Use this when you need to iterate through all results.
results = client.twitter.search_posts(
"bitcoin",
response_type=ResponseType.PAGING,
)Triggers a server-side CSV export. The result contains no inline data — call export_csv() to get the download URL.
results = client.twitter.search_posts(
"bitcoin",
response_type=ResponseType.CSV,
)
csv_url = results.export_csv()response_type and limit are available on:
| Platform | Method |
|---|---|
search_posts |
|
get_posts_by_author |
|
get_users_by_keywords |
|
search_posts |
|
get_posts_by_user |
|
get_users_by_keywords |
|
search_posts |
|
| TikTok | search_posts |
| TikTok | get_posts_by_user |
| TikTok | get_users_by_keywords |
| TikTok | get_posts_by_hashtags |
| TikTok | get_users_by_hashtags |
| TikTok | get_posts_by_sound |
All methods accept a fields parameter. Use snake_case — the SDK translates to camelCase automatically.
# Only fetch the fields you need (faster + less memory)
results = client.twitter.search_posts(
"AI",
fields=["id", "text", "like_count", "retweet_count", "created_at_date"]
)
user = client.twitter.get_user(
"elonmusk",
fields=["id", "username", "name", "followers_count", "description"]
)Requesting fewer fields significantly improves response time.
The query parameter on all search_* and get_*_by_keywords methods supports a Lucene-style full-text syntax across Twitter, Instagram, and Reddit.
Wrap in double quotes to require an exact match:
"machine learning"
"climate change"
Space-separated terms without quotes match posts containing any of the words:
AI crypto blockchain
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
(AI OR "artificial intelligence") AND ethics
(startup OR entrepreneur) NOT "venture capital"
results = client.twitter.search_posts(
'("machine learning" OR "deep learning") AND python NOT spam',
start_date="2025-01-01",
language="en",
)Note: Do not use
from:,lang:,since:, oruntil:in the query string — use the dedicated parameters (author_username,language,start_date,end_date) instead.
from xpoz import (
XpozError,
AuthenticationError,
ConnectionError,
OperationTimeoutError,
OperationFailedError,
OperationCancelledError,
NotFoundError,
ValidationError,
)
try:
user = client.twitter.get_user("nonexistent_user_12345")
except OperationFailedError as e:
print(f"Operation {e.operation_id} failed: {e.error}")
except OperationTimeoutError as e:
print(f"Timed out after {e.elapsed_seconds}s")
except AuthenticationError:
print("Invalid API key")
except XpozError as e:
print(f"Xpoz error: {e}")Get a single Twitter user profile.
# By username (default)
user = client.twitter.get_user("elonmusk")
# By numeric ID
user = client.twitter.get_user("44196397", identifier_type="id")Search users by name or username. Returns up to 10 results.
users = client.twitter.search_users("elon")get_user_connections(username, connection_type, *, fields, force_latest) -> PaginatedResult[TwitterUser]
Get followers or following for a user.
followers = client.twitter.get_user_connections("elonmusk", "followers")
following = client.twitter.get_user_connections("elonmusk", "following")get_users_by_keywords(query, *, fields, start_date, end_date, language, force_latest, response_type, limit) -> PaginatedResult[TwitterUser]
Find users who authored posts matching a keyword query. Includes aggregation fields like relevant_tweets_count, relevant_tweets_likes_sum.
users = client.twitter.get_users_by_keywords(
'"machine learning"',
fields=["username", "name", "followers_count", "relevant_tweets_count", "relevant_tweets_likes_sum"]
)Get 1-100 posts by their IDs.
tweets = client.twitter.get_posts_by_ids(["1234567890", "0987654321"])get_posts_by_author(identifier, identifier_type="username", *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TwitterPost]
Get all posts by an author with optional date filtering.
results = client.twitter.get_posts_by_author("elonmusk", start_date="2025-01-01")search_posts(query, *, fields, start_date, end_date, author_username, author_id, language, force_latest, response_type, limit) -> PaginatedResult[TwitterPost]
Full-text search with filters. Supports exact phrases ("machine learning"), boolean operators (AI AND python), and parentheses.
results = client.twitter.search_posts(
'"artificial intelligence" AND ethics',
start_date="2025-01-01",
end_date="2025-06-01",
language="en",
fields=["id", "text", "like_count", "author_username", "created_at_date"]
)Get retweets of a specific post (database only).
retweets = client.twitter.get_retweets("1234567890")Get quote tweets of a specific post.
quotes = client.twitter.get_quotes("1234567890")Get replies to a specific post.
comments = client.twitter.get_comments("1234567890")get_post_interacting_users(post_id, interaction_type, *, fields, force_latest) -> PaginatedResult[TwitterUser]
Get users who interacted with a post. interaction_type: "commenters", "quoters", "retweeters".
commenters = client.twitter.get_post_interacting_users("1234567890", "commenters")Count tweets containing a phrase within a date range.
count = client.twitter.count_posts("bitcoin", start_date="2025-01-01")
print(f"{count:,} tweets mention bitcoin")user = client.instagram.get_user("instagram")
print(f"{user.full_name} — {user.follower_count:,} followers")users = client.instagram.search_users("nasa")get_user_connections(username, connection_type, *, fields, force_latest) -> PaginatedResult[InstagramUser]
followers = client.instagram.get_user_connections("instagram", "followers")get_users_by_keywords(query, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[InstagramUser]
users = client.instagram.get_users_by_keywords('"sustainable fashion"')Post IDs must be in strong_id format: "media_id_user_id" (e.g. "3606450040306139062_4836333238").
posts = client.instagram.get_posts_by_ids(["3606450040306139062_4836333238"])get_posts_by_user(identifier, identifier_type="username", *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[InstagramPost]
results = client.instagram.get_posts_by_user("nasa")search_posts(query, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[InstagramPost]
results = client.instagram.search_posts("travel photography")get_comments(post_id, *, fields, start_date, end_date, force_latest) -> PaginatedResult[InstagramComment]
comments = client.instagram.get_comments("3606450040306139062_4836333238")get_post_interacting_users(post_id, interaction_type, *, fields, force_latest) -> PaginatedResult[InstagramUser]
interaction_type: "commenters", "likers".
likers = client.instagram.get_post_interacting_users("3606450040306139062_4836333238", "likers")user = client.reddit.get_user("spez")
print(f"{user.username} — {user.total_karma:,} karma")users = client.reddit.search_users("spez")get_users_by_keywords(query, *, fields, start_date, end_date, subreddit, force_latest) -> PaginatedResult[RedditUser]
users = client.reddit.get_users_by_keywords('"machine learning"', subreddit="MachineLearning")search_posts(query, *, fields, start_date, end_date, sort, time, subreddit, force_latest, response_type, limit) -> PaginatedResult[RedditPost]
sort: "relevance", "hot", "top", "new", "comments". time: "hour", "day", "week", "month", "year", "all".
results = client.reddit.search_posts(
"python tutorial",
subreddit="learnpython",
sort="top",
time="month"
)get_post_with_comments(post_id, *, post_fields, comment_fields, force_latest) -> RedditPostWithComments
Returns a composite object with the post and its paginated comments.
result = client.reddit.get_post_with_comments("abc123")
print(result.post.title)
for comment in result.comments:
print(f" {comment.author_username}: {comment.body[:80]}")search_comments(query, *, fields, start_date, end_date, subreddit) -> PaginatedResult[RedditComment]
comments = client.reddit.search_comments("helpful tip", subreddit="LifeProTips")Fetch a single Reddit comment by its id (bare base36 or t1_-prefixed). Database-first with live API fallback when the comment is missing or stale.
comment = client.reddit.get_comment_by_id("laz1ytq", fields=["id", "body", "rank", "removal"])subs = client.reddit.search_subreddits("machine learning")get_subreddit_with_posts(subreddit_name, *, subreddit_fields, post_fields, force_latest) -> SubredditWithPosts
result = client.reddit.get_subreddit_with_posts("wallstreetbets")
print(f"r/{result.subreddit.display_name} — {result.subreddit.subscribers_count:,} members")
for post in result.posts:
print(f" {post.title} ({post.score} points)")get_subreddits_by_keywords(query, *, fields, start_date, end_date, force_latest) -> PaginatedResult[RedditSubreddit]
subs = client.reddit.get_subreddits_by_keywords("cryptocurrency")user = client.tiktok.get_user("charlidamelio")
print(f"{user.nickname} — {user.follower_count:,} followers")
# By numeric ID
user = client.tiktok.get_user("123456789", identifier_type="id")users = client.tiktok.search_users("charli")
top_five = client.tiktok.search_users("charli", limit=5)get_users_by_keywords(query, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokUser]
users = client.tiktok.get_users_by_keywords(
'"machine learning"',
response_type=ResponseType.FAST,
limit=20,
)posts = client.tiktok.get_posts_by_ids(["7123456789012345678"])get_posts_by_user(identifier, identifier_type="username", *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokPost]
results = client.tiktok.get_posts_by_user("charlidamelio", start_date="2025-01-01")search_posts(query, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokPost]
results = client.tiktok.search_posts(
"travel vlog",
start_date="2025-01-01",
response_type=ResponseType.FAST,
limit=30,
)get_posts_by_hashtags(hashtags, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokPost]
Search posts by hashtags via the indexed hashtags column. Pass bare alphanumeric tags (no leading #). Max 5 hashtags per request; OR semantics across the list.
results = client.tiktok.get_posts_by_hashtags(
["dance", "fyp"],
response_type=ResponseType.FAST,
limit=50,
)get_users_by_hashtags(hashtags, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokUser]
Find users who authored posts tagged with the given hashtags. Same input rules as get_posts_by_hashtags.
users = client.tiktok.get_users_by_hashtags(
["sustainable_fashion"],
response_type=ResponseType.FAST,
limit=20,
)Search sound/music objects by keyword (title or artist). Pass the returned id to get_posts_by_sound.
sounds = client.tiktok.search_sounds("dance monkey")
sound_id = sounds[0].idget_posts_by_sound(sound_id, *, fields, start_date, end_date, force_latest, response_type, limit) -> PaginatedResult[TiktokPost]
Find posts that use a specific sound via the indexed music_id column. Pass a single numeric sound_id from search_sounds.
results = client.tiktok.get_posts_by_sound(
"7016548364456789012",
response_type=ResponseType.FAST,
limit=50,
)get_comments(post_id, *, fields, start_date, end_date, force_latest) -> PaginatedResult[TiktokComment]
comments = client.tiktok.get_comments("7123456789012345678")Read-only account, plan, and usage information for the authenticated user.
Returns the plan (name + feature limits), billing (period + next renewal; billing is None on the Free plan), and current usage (remaining subscription/extra credits, extra tracked items).
details = client.account.get_account_details()
print(details.plan.name, details.usage.subscription_credits_remaining)Returns time-series usage for credits and export rows. range is one of "today", "7d", "current_month" (default), "lifetime"; granularity is "hour" or "day" (default). For current remaining balances, use get_account_details().
history = client.account.get_credits_usage_history(range="7d", granularity="day")
for bucket in history.credits:
print(bucket.bucket, bucket.total_used)All models are Pydantic v2 BaseModel subclasses with extra="allow" (unknown fields are preserved, not rejected). All fields are optional and default to None.
| Field | Type | Description |
|---|---|---|
id |
str |
Post ID |
text |
str |
Post text content |
author_id |
str |
Author's user ID |
author_username |
str |
Author's username |
like_count |
int |
Number of likes |
retweet_count |
int |
Number of retweets |
reply_count |
int |
Number of replies |
quote_count |
int |
Number of quotes |
impression_count |
int |
Number of impressions |
bookmark_count |
int |
Number of bookmarks |
lang |
str |
Language code |
hashtags |
list[str] |
Hashtags in tweet |
mentions |
list[str] |
Mentioned usernames |
media_urls |
list[str] |
Media attachment URLs |
urls |
list[str] |
URLs in tweet text |
place_name |
str |
Tagged place name |
place_country |
str |
Tagged place country |
place_country_code |
str |
Tagged place country code (ISO 3166-1 alpha-2) |
place_bounding_box_coordinates |
object |
Tagged place bounding box |
place_centroid |
object |
Tagged place centroid |
created_at |
str |
Creation timestamp |
created_at_date |
str |
Creation date (YYYY-MM-DD) |
conversation_id |
str |
Thread conversation ID |
quoted_tweet_id |
str |
ID of quoted tweet |
reply_to_tweet_id |
str |
ID of parent tweet |
possibly_sensitive |
bool |
Sensitive content flag |
is_retweet |
bool |
Whether this is a retweet |
has_birdwatch_notes |
bool |
Has community notes |
birdwatch_notes_id |
str |
Birdwatch note ID |
birdwatch_notes_text |
str |
Birdwatch note text |
birdwatch_notes_url |
str |
Birdwatch note URL |
status |
str |
Tweet status |
| Field | Type | Description |
|---|---|---|
id |
str |
User ID |
username |
str |
Username (handle) |
name |
str |
Display name |
description |
str |
Bio text |
location |
str |
Location string |
verified |
bool |
Verification status |
verified_type |
str |
Verification type |
followers_count |
int |
Number of followers |
following_count |
int |
Number of following |
tweet_count |
int |
Total tweets |
likes_count |
int |
Total likes |
profile_image_url |
str |
Profile picture URL |
created_at |
str |
Account creation timestamp |
account_based_in |
str |
Account location |
| Field | Type | Description |
|---|---|---|
id |
str |
Post ID (strong_id format) |
caption |
str |
Post caption |
username |
str |
Author username |
full_name |
str |
Author display name |
like_count |
int |
Number of likes |
comment_count |
int |
Number of comments |
reshare_count |
int |
Number of reshares |
video_play_count |
int |
Video play count |
media_type |
str |
Media type |
image_url |
str |
Image URL |
video_url |
str |
Video URL |
created_at_date |
str |
Creation date |
gen_ai_chat_with_ai_cta_info |
str |
Gen AI chat CTA info |
has_high_risk_gen_ai_inform_treatment |
bool |
High risk Gen AI treatment flag |
| Field | Type | Description |
|---|---|---|
id |
str |
User ID |
username |
str |
Username |
full_name |
str |
Display name |
biography |
str |
Bio text |
is_private |
bool |
Private account |
is_verified |
bool |
Verified status |
follower_count |
int |
Followers |
following_count |
int |
Following |
media_count |
int |
Total posts |
profile_pic_url |
str |
Profile picture URL |
| Field | Type | Description |
|---|---|---|
id |
str |
Comment ID |
text |
str |
Comment text |
username |
str |
Author username |
parent_post_id |
str |
Parent post ID |
like_count |
int |
Number of likes |
child_comment_count |
int |
Reply count |
created_at_date |
str |
Creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
Post ID |
title |
str |
Post title |
selftext |
str |
Post body text |
author_username |
str |
Author username |
subreddit_name |
str |
Subreddit name |
score |
int |
Net score |
upvotes |
int |
Upvote count |
comments_count |
int |
Comment count |
url |
str |
Post URL |
permalink |
str |
Reddit permalink |
is_self |
bool |
Self post (text only) |
over18 |
bool |
NSFW flag |
created_at_date |
str |
Creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
User ID |
username |
str |
Username |
total_karma |
int |
Total karma |
link_karma |
int |
Link karma |
comment_karma |
int |
Comment karma |
is_gold |
bool |
Reddit Gold status |
is_mod |
bool |
Moderator status |
profile_description |
str |
Profile bio |
created_at_date |
str |
Account creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
Comment ID |
body |
str |
Comment text |
author_username |
str |
Author username |
parent_post_id |
str |
Parent post ID |
score |
int |
Net score |
depth |
int |
Nesting depth |
is_submitter |
bool |
Is OP |
created_at_date |
str |
Creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
Subreddit ID |
display_name |
str |
Subreddit name |
title |
str |
Subreddit title |
public_description |
str |
Short description |
description |
str |
Full description |
subscribers_count |
int |
Subscriber count |
active_user_count |
int |
Active users |
over18 |
bool |
NSFW flag |
created_at_date |
str |
Creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
Post ID |
description |
str |
Post caption/description |
description_language |
str |
Language of description |
user_id |
str |
Author user ID |
username |
str |
Author username |
nickname |
str |
Author display name |
like_count |
int |
Number of likes |
comment_count |
int |
Number of comments |
play_count |
int |
Video play count |
collect_count |
int |
Number of collects/saves |
download_count |
int |
Number of downloads |
forward_count |
int |
Number of forwards/shares |
video_thumbnail |
str |
Thumbnail URL |
video_url |
list[str] |
Array of video URLs |
duration |
int |
Video duration in seconds |
hashtags |
list[str] |
Hashtags in the post |
post_type |
int |
Post type code |
is_private |
bool |
Private post flag |
created_at |
str |
Creation timestamp |
created_at_date |
str |
Creation date (YYYY-MM-DD) |
| Field | Type | Description |
|---|---|---|
id |
str |
User ID |
username |
str |
Username |
nickname |
str |
Display name |
signature |
str |
Bio text |
sec_uid |
str |
Secure user ID |
avatar |
str |
Profile picture URL |
is_private |
bool |
Private account |
is_verified |
bool |
Verified status |
follower_count |
int |
Number of followers |
following_count |
int |
Number of following |
like_count |
int |
Total likes received |
post_count |
int |
Total posts |
language |
str |
Profile language |
region |
str |
Account region |
created_at |
str |
Account creation date |
| Field | Type | Description |
|---|---|---|
id |
str |
Comment ID |
post_id |
str |
Parent post ID |
user_id |
str |
Author user ID |
username |
str |
Author username |
text |
str |
Comment text |
like_count |
int |
Number of likes |
created_at |
str |
Creation timestamp |
created_at_date |
str |
Creation date (YYYY-MM-DD) |
| Field | Type | Description |
|---|---|---|
id |
str |
Sound/music ID |
title |
str |
Sound title |
author |
str |
Sound artist/author |
album |
str |
Album name |
duration |
int |
Duration in seconds |
user_count |
int |
Number of posts using the sound |
is_original |
bool |
Whether the sound is original |
is_commerce_music |
bool |
Whether the sound is commercial |
is_original_sound |
bool |
Whether it is an original sound |
Returned by get_account_details(). All fields are nested models and may be None.
plan: AccountPlan | Nonebilling: AccountBilling | None(Noneon the Free plan)usage: AccountUsage | None
| Field | Type | Description |
|---|---|---|
name |
str |
Plan name |
features |
PlanFeatures | None |
Plan feature limits |
| Field | Type | Description |
|---|---|---|
credits |
int |
Subscription credits per period |
credit_reset_frequency |
str |
How often credits reset |
extra_credit_price |
float |
Price per extra credit |
tracked_items |
int |
Tracked-item allowance |
csv_row_export_limit |
int |
CSV row export limit |
extra_csv_row_price |
float |
Price per extra CSV row |
extra_tracked_item_price |
float |
Price per extra tracked item |
max_rows_per_export |
int |
Max rows per single export |
| Field | Type | Description |
|---|---|---|
billing_period |
str |
"monthly" or "annual" |
next_renewal_date |
str |
Next renewal date |
| Field | Type | Description |
|---|---|---|
subscription_credits_remaining |
int |
Subscription credits remaining |
extra_credits_remaining |
int |
Extra credits remaining |
extra_tracked_items |
int |
Extra tracked items purchased |
Returned by get_credits_usage_history().
| Field | Type | Description |
|---|---|---|
range |
str |
Requested range |
granularity |
str |
Requested granularity |
generated_at |
str |
Timestamp the report was generated |
credits |
list[UsageHistoryBucket] |
Credit usage buckets over time |
export_rows |
list[UsageHistoryBucket] |
Export-rows usage buckets over time |
| Field | Type | Description |
|---|---|---|
bucket |
str |
Bucket timestamp (hour or day) |
subscription_used |
int |
Subscription units used in the bucket |
extra_used |
int |
Extra units used in the bucket |
total_used |
int |
Total units used in the bucket |
extra_purchased |
int |
Extra units purchased in the bucket |
RedditPostWithComments — returned by get_post_with_comments():
post: RedditPostcomments: list[RedditComment]comments_pagination: PaginationInfo | None
SubredditWithPosts — returned by get_subreddit_with_posts():
subreddit: RedditSubredditposts: list[RedditPost]posts_pagination: PaginationInfo | None
| Variable | Description | Default |
|---|---|---|
XPOZ_API_KEY |
API key for authentication | — |
XPOZ_SERVER_URL |
MCP server URL | https://mcp.xpoz.ai/mcp |
Tests hit the live Xpoz API and require a valid API key:
XPOZ_API_KEY=your-api-key pytest tests/ -vTests must run sequentially in a single process to avoid API rate limiting. Do not run multiple pytest processes in parallel.
MIT