{"name":"Probe Uranus","version":"1.2.1","description":"Profiles, posts, and search for X. Authenticate with an API key. Plan limits are per wallet account.","auth":{"api_key":{"header":"Authorization","format":"Bearer <api_key>","alternateHeader":"X-API-Key"},"admin":{"header":"X-Admin-Secret","description":"Required for /v1/admin/* routes"}},"links":{"openapi":"/v1/openapi.json","health":"/health","commands":"/v1/commands"},"endpoints":[{"id":"health","method":"GET","path":"/health","summary":"Health check","description":"Health check.","auth":"none"},{"id":"catalog","method":"GET","path":"/v1","summary":"List available API commands","description":"Full list of available commands.","auth":"none","notes":["Also available at GET / and GET /v1/commands","Also available for API tools at GET /v1/openapi.json"]},{"id":"openapi","method":"GET","path":"/v1/openapi.json","summary":"Schema for API tools","description":"Command list in a format API tools understand.","auth":"none"},{"id":"me","method":"GET","path":"/v1/me","summary":"Current account plan and usage","description":"Returns plan limits, remaining RPM/daily/monthly quota, and reset timestamps for the wallet account that owns this API key. Quota is shared across the account's keys.","auth":"api_key","notes":["Counts as one request against your account quota","Also mirrored on every keyed response via X-RateLimit-* and X-RateLimit-Reset* headers","Response envelope: { data, meta }"]},{"id":"user_info","method":"GET","path":"/v1/user/info","summary":"Get user profile and stats","description":"Returns profile fields, follower/following counts, statuses, media counts, etc.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @ (alias: username)","example":"elonmusk"},{"name":"fresh","in":"query","required":false,"type":"boolean","description":"Bypass cache when 1/true/yes","example":"1"}],"notes":["Response envelope: { data, meta } with meta.cached / meta.fetched_at","Cache-Control: private, max-age=60 (or CACHE_TTL_MS capped); weak ETag from user id + fetched_at; 304 on If-None-Match"]},{"id":"user_tweets","method":"GET","path":"/v1/user/tweets","summary":"Get latest tweets for a user","description":"User timeline with cursor pagination. Tweets include media[] and isRetweet.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @","example":"elonmusk"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"user_tweets_and_replies","method":"GET","path":"/v1/user/tweets-and-replies","summary":"Get tweets and replies for a user","description":"Timeline including the user's replies (UserTweetsAndReplies). Cursor-paginated.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @","example":"elonmusk"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"search_tweets","method":"GET","path":"/v1/search/tweets","summary":"Search tweets","description":"Latest/Top search over X posts.","auth":"api_key","params":[{"name":"query","in":"query","required":true,"type":"string","description":"Search query (alias: q). Supports X operators.","example":"solana"},{"name":"product","in":"query","required":false,"type":"string","description":"Latest or Top (default Latest)","example":"Latest"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":5},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"search_users","method":"GET","path":"/v1/search/users","summary":"Search users","description":"People search over X accounts via SearchTimeline (product=People). Cursor-paginated.","auth":"api_key","params":[{"name":"query","in":"query","required":true,"type":"string","description":"Search query (alias: q)","example":"solana"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"user_likes","method":"GET","path":"/v1/user/likes","summary":"Get tweets a user liked","description":"Likes timeline for a public X account. X often returns an empty shell for scraper sessions even when favouritesCount > 0.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @ (alias: username)","example":"jack"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors","Empty data with meta.note means X hid likes from this session. Not necessarily a bug"]},{"id":"user_media","method":"GET","path":"/v1/user/media","summary":"Get media tweets for a user","description":"Media tab timeline (photos/videos/gifs). Cursor-paginated; tweets include media[].","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @ (alias: username)","example":"nasa"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"list_tweets","method":"GET","path":"/v1/list/{id}/tweets","summary":"Get tweets from an X list","description":"Latest tweets from a list by numeric list id. May return empty for some sessions; falls back to search list:id.","auth":"api_key","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Numeric list id","example":"1539453138322673664"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"community_tweets","method":"GET","path":"/v1/community/{id}/tweets","summary":"Get tweets from an X community","description":"Community timeline (CommunityTweetsTimeline). Cursor-paginated.","auth":"api_key","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Numeric community id","example":"1489422448332197888"},{"name":"ranking","in":"query","required":false,"type":"string","description":"Relevance (default) or Recency (alias: rankingMode)","example":"Relevance"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."},{"name":"include_replies","in":"query","required":false,"type":"boolean","description":"Include reply tweets (default false on /user/tweets, true elsewhere)","example":"0"},{"name":"include_retweets","in":"query","required":false,"type":"boolean","description":"Include retweets (default true)","example":"1"},{"name":"media_only","in":"query","required":false,"type":"boolean","description":"Only tweets that have media[] (default false)","example":"1"},{"name":"since_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly greater than this snowflake (page filter; keep paging with cursor)"},{"name":"until_id","in":"query","required":false,"type":"string","description":"Keep tweets with id strictly less than this snowflake (exclusive)"}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"tweet_by_id","method":"GET","path":"/v1/tweet/{id}","summary":"Get a tweet by id","description":"Returns a single post with author, media[], and engagement counts (likes, replies, retweets, quotes, bookmarks, views).","auth":"api_key","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Numeric tweet/status id","example":"20"}],"notes":["Response envelope: { data, meta }","Cache-Control: private, max-age=60 (or CACHE_TTL_MS capped); weak ETag from tweet id; 304 on If-None-Match"]},{"id":"tweet_by_url","method":"GET","path":"/v1/tweet/by-url","summary":"Get a tweet by X/Twitter URL","description":"Accepts an x.com or twitter.com status URL (or raw numeric id) and returns the same shape as tweet-by-id.","auth":"api_key","params":[{"name":"url","in":"query","required":true,"type":"string","description":"Status URL (alias: u)","example":"https://x.com/jack/status/20"}],"notes":["Response envelope: { data, meta }","Cache-Control: private, max-age=60 (or CACHE_TTL_MS capped); weak ETag from tweet id; 304 on If-None-Match"]},{"id":"tweet_replies","method":"GET","path":"/v1/tweet/{id}/replies","summary":"Get replies to a tweet","description":"Conversation replies for a post (commenters + engagement). Cursor-paginated.","auth":"api_key","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Numeric tweet/status id","example":"20"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors","Parent tweet (when available) is in meta.tweet"]},{"id":"tweet_retweeters","method":"GET","path":"/v1/tweet/{id}/retweeters","summary":"List users who retweeted a tweet","description":"Retweeters for a post. Cursor-paginated.","auth":"api_key","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Numeric tweet/status id","example":"20"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"tweets_batch","method":"POST","path":"/v1/tweets","summary":"Batch fetch tweets by id","description":"Fetch up to 25 tweets in one request. Partial failures return in errors[].","auth":"api_key","body":{"contentType":"application/json","fields":[{"name":"ids","in":"body","required":true,"type":"string","description":"Array of tweet ids or status URLs (alias: tweetIds), max 25","example":"20"}],"example":{"ids":["20","https://x.com/jack/status/20"]}},"notes":["Response envelope: { data, meta } with meta.errors / meta.max"]},{"id":"users_batch","method":"POST","path":"/v1/users","summary":"Batch fetch user profiles","description":"Fetch up to 25 profiles in one request. Partial failures return in errors[].","auth":"api_key","body":{"contentType":"application/json","fields":[{"name":"userNames","in":"body","required":true,"type":"string","description":"Array of handles without @ (alias: usernames), max 25","example":"jack"}],"example":{"userNames":["jack","elonmusk"]}},"notes":["Response envelope: { data, meta } with meta.errors / meta.max"]},{"id":"user_followers","method":"GET","path":"/v1/user/followers","summary":"List followers for a user","description":"Followers of an X account. Cursor-paginated.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @ (alias: username)","example":"elonmusk"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"user_following","method":"GET","path":"/v1/user/following","summary":"List accounts a user follows","description":"Following list for an X account. Cursor-paginated.","auth":"api_key","params":[{"name":"userName","in":"query","required":true,"type":"string","description":"X handle without @ (alias: username)","example":"elonmusk"},{"name":"limit","in":"query","required":false,"type":"number","description":"Page size (1-40, default 20)","example":20},{"name":"cursor","in":"query","required":false,"type":"string","description":"Opaque pagination cursor. Pass next_cursor (or cursor) from the previous response unchanged."}],"notes":["Response envelope: { data, next_cursor, cursor, meta }","meta.has_next_page mirrors next_cursor; extra context (user, filters, query) lives in meta","Pass next_cursor back as ?cursor=. Do not invent or parse cursors"]},{"id":"admin_seed_plans","method":"POST","path":"/v1/admin/seed-plans","summary":"Seed plan tiers","description":"Creates starter/pro/unlimited plans if missing.","auth":"admin"},{"id":"admin_create_key","method":"POST","path":"/v1/admin/keys","summary":"Create an API key","description":"Returns the raw API key once. Store it securely.","auth":"admin","body":{"contentType":"application/json","fields":[{"name":"name","in":"body","required":false,"type":"string","description":"Key label","example":"owner"},{"name":"planName","in":"body","required":false,"type":"string","description":"starter | pro | unlimited","example":"unlimited"}],"example":{"name":"owner","planName":"unlimited"}}},{"id":"admin_list_keys","method":"GET","path":"/v1/admin/keys","summary":"List API keys","description":"Does not return raw key secrets.","auth":"admin"},{"id":"admin_add_session","method":"POST","path":"/v1/admin/sessions","summary":"Add connection","description":"Register a connection for API capacity.","auth":"admin","body":{"contentType":"application/json","fields":[{"name":"label","in":"body","required":false,"type":"string","description":"Human label"},{"name":"authToken","in":"body","required":true,"type":"string","description":"Primary token"},{"name":"ct0","in":"body","required":true,"type":"string","description":"Secondary token"},{"name":"maxRpm","in":"body","required":false,"type":"number","description":"Per-connection requests per minute (default 25)"}]}},{"id":"admin_list_sessions","method":"GET","path":"/v1/admin/sessions","summary":"List connection health","description":"Status, cooldown, and usage only. No secrets.","auth":"admin"},{"id":"admin_patch_session","method":"PATCH","path":"/v1/admin/sessions/{id}","summary":"Update connection status","description":"Set healthy | cooling | dead | disabled.","auth":"admin","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Connection id"}],"body":{"contentType":"application/json","fields":[{"name":"status","in":"body","required":true,"type":"string","description":"healthy | cooling | dead | disabled"}]}},{"id":"admin_rotate_session","method":"PUT","path":"/v1/admin/sessions/{id}","summary":"Rotate connection","description":"Replace connection tokens and mark healthy.","auth":"admin","params":[{"name":"id","in":"path","required":true,"type":"string","description":"Connection id"}],"body":{"contentType":"application/json","fields":[{"name":"authToken","in":"body","required":true,"type":"string","description":"New primary token"},{"name":"ct0","in":"body","required":true,"type":"string","description":"New secondary token"}]}},{"id":"admin_refresh_query_ids","method":"POST","path":"/v1/admin/query-ids/refresh","summary":"Refresh operation IDs","description":"Refresh cached operation identifiers.","auth":"admin"}]}