| name | twitter-x-hub |
| description | Skill for reading/writing Twitter/X data using Python + UV with zero third-party dependencies (pure standard library). Authentication is done by passing auth_token + ct0 Cookies directly. In Minis environment, cookies can be automatically retrieved by navigating to x.com using browser_use tool and get_cookies action, no manual copying needed. Supports fetching home timeline, following list, bookmarks (including bookmark folders), search, user profile, user tweets, likes, tweet details (single/with replies), List timeline, followers/following list, as well as write operations: posting, deleting, liking, retweeting, bookmarking. Trigger when user mentions "fetch Twitter data", "get X tweets", "Twitter timeline", "X bookmarks", "search tweets", "twitter-x-hub", "request Twitter with cookies", "Twitter GraphQL", or any scenario needing programmatic read/write of Twitter/X data.
|
twitter-x-hub
Forked from: public-clis/twitter-cli (original jackwener/twitter-cli)
This skill simplifies the original repo: removed browser-cookie3/rich/click/PyYAML/curl_cffi/
xclienttransaction/beautifulsoup4 dependencies, using pure standard library;
authentication changed to direct Cookie passing without browser auto-extraction;
removed Twitter Article rendering and image upload functionality.
File Structure
/var/minis/skills/twitter-x-hub/
├── SKILL.md
├── pyproject.toml # UV project config (zero third-party deps)
└── scripts/
├── __init__.py
├── models.py # Data models (Tweet, Author, Metrics, UserProfile, BookmarkFolder)
├── parser.py # GraphQL response parser (split from client.py, synced from upstream v0.8.6)
├── client.py # GraphQL client (core logic)
└── cli.py # CLI entry point (argparse)
Authentication
Twitter/X internal GraphQL API uses two cookies for authentication:
| Cookie | Description |
|---|
auth_token | User login credential (OAuth Session Token) |
ct0 | CSRF Token, also used as X-Csrf-Token request header |
Method 1: browser_use tool auto-fetch (recommended, Minis preferred)
In Minis, use browser_use tool to navigate to x.com, then use get_cookies
action to read cookies without manual copying. Save to environment variables
immediately after fetching to avoid plaintext in conversation context.
Steps:
browser_use navigate open https://x.com, confirm logged in
browser_use get_cookies fetch all cookies
- Tool returns offload env file path (e.g.
/var/minis/offloads/env_cookies_xxx.sh)
- Raw cookie values never appear in conversation
- After loading, use:
. /var/minis/offloads/env_cookies_xxx.sh
export TWITTER_AUTH_TOKEN="$COOKIE_AUTH_TOKEN"
export TWITTER_CT0="$COOKIE_CT0"
Method 2: Manual environment variable setup
From browser DevTools -> Application -> Cookies -> https://x.com copy auth_token
and ct0, store in Minis environment variables (Settings -> Environments):
TWITTER_AUTH_TOKEN + TWITTER_CT0
Passing methods (three ways, priority high to low)
- Environment variables:
TWITTER_AUTH_TOKEN + TWITTER_CT0 (recommended)
- CLI args:
--auth-token <value> --ct0 <value>
- Direct code:
TwitterClient(auth_token=..., ct0=...)
Quick Start
Environment Setup
which uv || pip install uv
cd /var/minis/skills/twitter-x-hub
CLI Usage
uv run python -m scripts.cli feed
uv run python -m scripts.cli feed --type following --max 30 --json
uv run python -m scripts.cli search "Claude Code" --tab Latest --max 20
uv run python -m scripts.cli bookmarks --max 50
uv run python -m scripts.cli bookmark-folders
uv run python -m scripts.cli user elonmusk
uv run python -m scripts.cli user-posts elonmusk --max 20
uv run python -m scripts.cli user-likes elonmusk --max 20
uv run python -m scripts.cli tweet 1234567890
uv run python -m scripts.cli tweet-by-id 1234567890
uv run python -m scripts.cli list 1539453138322673664
uv run python -m scripts.cli followers <user_id> --max 50
uv run python -m scripts.cli following <user_id> --max 50
uv run python -m scripts.cli post "Hello from twitter-x-hub!"
uv run python -m scripts.cli post "reply text" --reply-to 1234567890
uv run python -m scripts.cli like 1234567890
uv run python -m scripts.cli retweet 1234567890
uv run python -m scripts.cli bookmark 1234567890
Use environment variables to avoid re-passing
export TWITTER_AUTH_TOKEN="xxxx"
export TWITTER_CT0="yyyy"
uv run python -m scripts.cli feed --max 30 --json
Use as Python Library
import os, json, dataclasses
from scripts.client import TwitterClient
client = TwitterClient(
auth_token=os.environ["TWITTER_AUTH_TOKEN"],
ct0=os.environ["TWITTER_CT0"],
)
tweets = client.fetch_home_timeline(count=20)
for t in tweets:
print(f"@{t.author.screen_name}: {t.text[:80]}")
print(f" {t.metrics.likes} likes {t.metrics.retweets} RTs {t.metrics.views} views {t.metrics.bookmarks} bookmarks")
results = client.fetch_search("AI agent", count=10, product="Latest")
tweet = client.fetch_tweet_by_id("1234567890")
folders = client.fetch_bookmark_folders()
user = client.fetch_user("elonmusk")
print(user.id, user.followers_count)
data = [dataclasses.asdict(t) for t in tweets]
print(json.dumps(data, ensure_ascii=False, indent=2))
Core Implementation
Authentication
Uses browser cookies (auth_token + ct0) + hardcoded public Bearer Token,
masquerading as Chrome browser requesting Twitter internal GraphQL API.
QueryId Three-Level Resolution (auto-handles API changes)
1. In-memory cache (fastest)
2. Hardcoded FALLBACK_QUERY_IDS (constant fallback)
-> If 404, queryId expired, go to next level
3. Fetch latest queryId from github.com/fa0311/twitter-openapi
-> If still not found, scan x.com JS Bundle with regex extraction
URL Optimization (synced from upstream v0.8)
- Keys with
False value in features dict are not sent, avoiding overly long URLs (414 error)
Pagination & Rate Limiting
- Each response carries
cursor, auto-paginate until reaching count limit
- Default request interval 1.5s + -30% random jitter, HTTP 429 triggers exponential backoff
- Write operations have 1.5~4s random delay
Parser Split (synced from upstream v0.7+)
parser.py extracted from client.py, contains parse_tweet_result, parse_timeline_response,
parse_user_result and other standalone functions for easier unit testing and reuse
CLI Subcommand Reference
| Subcommand | Description | Key Args |
|---|
feed | Home timeline | `--type for-you |
bookmarks | Bookmarks | --max, --json |
bookmark-folders | Bookmark folders list (new) | --json |
search | Search | query, `--tab Top |
user | User profile | screen_name, --json |
user-posts | User tweets | screen_name, --max, --json |
user-likes | User likes | screen_name, --max, --json |
tweet | Tweet details + replies | tweet_id, --max, --json |
tweet-by-id | Single tweet (fast, new) | tweet_id, --json |
list | List timeline | list_id, --max, --json |
followers | Followers list | user_id, --max, --json |
following | Following list | user_id, --max, --json |
post | Post tweet | text, --reply-to |
delete | Delete tweet | tweet_id |
like / unlike | Like / Unlike | tweet_id |
retweet / unretweet | Retweet / Undo | tweet_id |
bookmark / unbookmark | Bookmark / Undo | tweet_id |
All subcommands support --auth-token / --ct0 args, also settable via env vars.
Changelog (synced from upstream)
v0.8.6 sync (2026-04-08)
- QueryId full update: Live-scanned from x.com JS bundle (main.0e98bc8a.js), updated all IDs:
HomeTimeline, HomeLatestTimeline, UserTweets, SearchTimeline, Likes, TweetDetail,
TweetResultByRestId, ListLatestTweetsTimeline, Followers, Following, CreateTweet, etc.
- New QueryIds:
TweetResultByRestId, BookmarkFoldersSlice, BookmarkFolderTimeline
- New commands:
tweet-by-id (single tweet fast fetch), bookmark-folders (bookmark folders)
- models.py:
Metrics added bookmarks field; Tweet added article_title,
article_text, is_subscriber_only fields; added BookmarkFolder dataclass
- parser.py: Extracted from
client.py as standalone module; fixed new API structure
(core.name/core.screen_name); parse_tweet_result supports note_tweet full text
(long tweet "Show more"); added _unwrap_visibility for TweetWithVisibilityResults;
parse_user_result fixed joined date read from core.created_at
- URL optimization: False values in features not sent, avoiding 414 errors
- SearchTimeline limitation: X started requiring
x-client-transaction-id header
from late 2025; this header is generated by xclienttransaction (C extension),
which cannot be installed on iSH/Alpine environments, so search command is
temporarily unavailable in this environment; workaround: use browser_use
to navigate to search page and extract DOM
Notes
- Cookie validity is typically weeks to months; re-fetch from browser when expired
- Recommend using a dedicated alt account to avoid main account risk flags
- Write operations (posting, liking, etc.) carry higher risk than reads; use with caution
max_count hard cap at 500 to prevent accidental heavy requests
- Upstream uses
curl_cffi for TLS fingerprint spoofing; this skill uses stdlib urllib
instead; when facing rate limiting, try passing full Cookie string via cookie_string param to enhance fingerprint