X (Twitter) API
Read-only access to X content via five MCP tools. Use for sentiment on tickers, exec announcements, launches, event tracking, and qualitative research alongside SEC/market data.
Not for single-post URL lookups. If the user hands you a specific X post URL and just wants its text or context, use
web_fetchon the URL — no vault, no auth, no rate limit. Reach for this skill when the task is search, aggregation, or thread traversal.
Auth
Every tool requires a bearer_token. Read it once per code block from the workspace vault:
pythonfrom vault import get token = get("X_BEARER_TOKEN")
If token is None or empty, the user hasn't added it yet. For the setup walkthrough and per-error fixes, see TROUBLESHOOTING.md.
Tools at a glance
The primary use case is search — the first two tools are what you'll reach for most.
| Tool | Use for | Page size | Notes |
|---|---|---|---|
search_posts | Posts from the last ~7 days | default 10, max 100 | Default choice. Query ≤512 chars. |
search_all_posts | Posts older than 7 days (back to 2006) | default 10, max 500 | Paid-tier X plan only. Query ≤1024 chars. |
get_conversation | Reply thread to a root tweet | default 50, max 100 | Uses recent search — thread must be ≤7 days old. Root tweet not included. |
get_user_by_username | A user profile + metrics | — | Handle without @, ≤15 chars. |
get_tweet_by_id | Hydrate a single post (mainly to find its conversation_id before get_conversation) | — | For a one-off URL the user already has, prefer web_fetch. |
Examples
Recent sentiment on a ticker
pythonres = search_posts( query="$NVDA -is:retweet lang:en", bearer_token=token, max_results=100, ) posts = res["posts"] posts.sort(key=lambda p: p["public_metrics"].get("impression_count", 0), reverse=True) for p in posts[:10]: print(p["author"]["username"], p["public_metrics"].get("like_count"), p["text"][:140])
Historical reaction to a past event
pythonres = search_all_posts( query="$TSLA earnings -is:retweet lang:en", bearer_token=token, max_results=500, start_time="2020-03-13T00:00:00Z", end_time="2020-03-20T00:00:00Z", )
Full thread on a specific tweet
pythonroot = get_tweet_by_id(tweet_id="1700000000000000001", bearer_token=token) thread = get_conversation( conversation_id=root["post"]["conversation_id"], bearer_token=token, max_results=100, ) all_posts = [root["post"], *thread["posts"]]
Paginate through a large result
pythonposts, next_tok = [], None while len(posts) < 500: res = search_posts( query="from:FedSpeakers", bearer_token=token, max_results=100, next_token=next_tok, ) if "error" in res: break posts.extend(res["posts"]) next_tok = res.get("next_token") if not next_tok: break
Post shape
Each post: id, text, created_at, lang, conversation_id, author_id, edit_history_tweet_ids, public_metrics (retweet/reply/like/quote/bookmark/impression counts), author — which is {id, username, name, verified}, {id, unresolved: true} for suspended/deleted users, or None if the tweet has no author_id.
Full per-tool response schemas (including user shape and error variants): reference.md.
Query syntax
$TSLA (cashtag), #hashtag, from:elonmusk, to:@SEC_News, -is:retweet, is:verified, has:links, has:media, lang:en, "exact phrase", parentheses + OR for alternation. Full list: https://docs.x.com/x-api/posts/search/introduction.
Errors
Every tool returns {"error": "...", ...} on failure — they never raise. Always check for error before accessing posts / user / post. For the per-error playbook (including setup fixes and tier gotchas), read TROUBLESHOOTING.md.
Do / Don't
- Do read
token = get("X_BEARER_TOKEN")once and reuse it across calls. - Do cross-reference with
get_daily_pricesandget_sec_filingwhen investigating price moves or disclosures. - Don't hardcode tokens. Ever.
- Don't cache
next_tokenacross sessions — cursors can expire. - Don't assume every author is resolved — check for
{unresolved: true}before readingusername.
Related
get_daily_prices— cross-reference X sentiment with price actionget_sec_filing— pair chatter with official disclosuresscrapescrape_page/scrape_pages— fallback for public pages when the API is blockedweb_search— broader news search that also indexes X posts

