Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Read-only Twitter/X data: search tweets, user profiles, followers, replies, and retweets via twitterapi.io.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
client.py
1"""2Twitter API client — wraps twitterapi.io endpoints.34Read-only: search tweets, user profiles, followers, replies, thread context,5quote tweets, article, and trends.6Auth: X-API-Key header from TWITTER_API_KEY env var.7"""89import logging10import os11from typing import Any, List1213from core.http_client import proxied_get1415CALLER_ID = "chat:twitter-skill"1617logger = logging.getLogger(__name__)1819TWITTERAPI_BASE_URL = "https://api.twitterapi.io"202122class TwitterApiClient:23"""24Sync twitterapi.io client using proxied_get.2526All endpoints are public GET requests authenticated via X-API-Key header.27"""2829def __init__(self):30self.base_url = TWITTERAPI_BASE_URL31self.api_key = os.environ.get("TWITTER_API_KEY", "")3233def _get(self, path: str, params: dict = None) -> Any:34"""GET a twitterapi.io endpoint."""35url = f"{self.base_url}{path}"3637headers = {"SC-CALLER-ID": CALLER_ID}38if self.api_key:39headers["X-API-Key"] = self.api_key4041response = proxied_get(url, headers=headers, params=params, timeout=15)42if response.status_code >= 400:43raise Exception(f"twitterapi.io {response.status_code}: {response.text}")44return response.json()4546# ── Tweet Endpoints ──────────────────────────────────────────────────4748def search_tweets(self, query: str, cursor: str = None) -> dict:49"""Advanced tweet search."""50params = {"query": query}51if cursor:52params["cursor"] = cursor53return self._get("/twitter/tweet/advanced_search", params)5455def get_tweets(self, tweet_ids: List[str]) -> dict:56"""Get tweets by IDs."""57params = {"tweet_ids": ",".join(tweet_ids)}58return self._get("/twitter/tweets", params)5960def get_tweet_replies(self, tweet_id: str, cursor: str = None) -> dict:61"""Get replies to a tweet."""62params = {"tweetId": tweet_id}63if cursor:64params["cursor"] = cursor65return self._get("/twitter/tweet/replies", params)6667def get_tweet_retweeters(self, tweet_id: str, cursor: str = None) -> dict:68"""Get users who retweeted a tweet."""69params = {"tweetId": tweet_id}70if cursor:71params["cursor"] = cursor72return self._get("/twitter/tweet/retweeters", params)7374def get_tweet_thread_context(self, tweet_id: str) -> dict:75"""Get complete thread context for a tweet (parents + direct replies)."""76return self._get("/twitter/tweet/thread_context", {"tweetId": tweet_id})7778def get_tweet_quote(self, tweet_id: str, cursor: str = None) -> dict:79"""Get quote tweets for a tweet."""80params = {"tweetId": tweet_id}81if cursor:82params["cursor"] = cursor83return self._get("/twitter/tweet/quotes", params)8485def get_article(self, tweet_id: str) -> dict:86"""Get article content by tweet ID (long-form X article)."""87return self._get("/twitter/article", {"tweet_id": tweet_id})8889def get_trends(self, woeid: str = None, country: str = None, category: str = None, limit: int = None) -> dict:90"""Get trending topics."""91params = {}92if woeid:93params["woeid"] = woeid94if country:95params["country"] = country96if category:97params["category"] = category98if limit is not None:99params["limit"] = limit100return self._get("/twitter/trends", params)101102# ── User Endpoints ───────────────────────────────────────────────────103104def get_user_info(self, username: str) -> dict:105"""Get user profile info."""106return self._get("/twitter/user/info", {"userName": username})107108def get_user_tweets(self, username: str, cursor: str = None) -> dict:109"""Get user's recent tweets."""110params = {"userName": username}111if cursor:112params["cursor"] = cursor113return self._get("/twitter/user/last_tweets", params)114115def get_user_followers(self, username: str, cursor: str = None) -> dict:116"""Get user's followers."""117params = {"userName": username}118if cursor:119params["cursor"] = cursor120return self._get("/twitter/user/followers", params)121122def get_user_followings(self, username: str, cursor: str = None) -> dict:123"""Get accounts the user follows."""124params = {"userName": username}125if cursor:126params["cursor"] = cursor127return self._get("/twitter/user/followings", params)128129def search_users(self, query: str, cursor: str = None) -> dict:130"""Search for users."""131params = {"query": query}132if cursor:133params["cursor"] = cursor134return self._get("/twitter/user/search", params)135