ask-search
Gives Claude Code a web search skill that queries a self-hosted SearxNG instance, aggregating Google, Bing, DuckDuckGo and Brave results. No API key is needed.
Fetch X/Twitter tweets, replies, timelines, lists, and articles, no login, no API keys.
Claude Code
$ npx -y skills add ythx-101/x-tweet-fetcher --agent claude-code
Codex
$ npx skills add ythx-101/x-tweet-fetcher -a codex
Cursor
$ npx skills add ythx-101/x-tweet-fetcher -a cursor
Gemini CLI
$ npx skills add ythx-101/x-tweet-fetcher -a gemini-cli
GitHub Copilot
$ npx skills add ythx-101/x-tweet-fetcher -a github-copilot
OpenCode
$ npx skills add ythx-101/x-tweet-fetcher -a opencode
Windsurf
$ npx skills add ythx-101/x-tweet-fetcher -a windsurf
Fetch X/Twitter tweets, replies, timelines, lists, and articles β no login, no API keys.
Three backends Β· Auto fallback Β· Unified JSON schema Β· Built for AI agents
Quick Start Β· Backends Β· Capabilities Β· Python API Β· Self-hosted Nitter Β· Migrating from v1
You: fetch that tweet / list / article for me
AI: I can't access X/Twitter. Please copy-paste the content manually.
You: ...seriously?
X has no free API. Scraping gets you blocked. Browser automation is fragile in headless environments.
x-tweet-fetcher solves this with smart backend routing: FxTwitter for single tweets (zero deps), Nitter for timelines and search (direct HTTP), a browser driver for everything else β with automatic fallback between them.
git clone https://github.com/ythx-101/x-tweet-fetcher
cd x-tweet-fetcher && pip install .
# Single tweet β works instantly, zero configuration
xtf --url https://x.com/user/status/1234567890
# User timeline (needs a Nitter instance, see below)
export XTF_NITTER=http://127.0.0.1:8788
xtf --user elonmusk --limit 20
# Search
xtf --search "openclaw" --limit 10
# Human-readable output instead of JSON
xtf --user elonmusk --text-only
Prefer not to install? python3 scripts/fetch_tweet.py --url ... works straight from the clone (same flags).
| Backend | Deps | Speed | Covers |
|---|---|---|---|
| fxtwitter | None (stdlib) | β‘β‘ | Single tweets, user profiles |
| nitter | A Nitter instance | β‘ | Timeline, search, replies, mentions |
| browser | Camofox or Playwright | π’ | Everything above + Lists + X Articles |
| auto (default) | Best available | β‘βπ’ | Nitter first, browser fallback |
xtf --user elonmusk # auto (default)
xtf --user elonmusk --backend nitter # direct HTTP only
xtf --list 1455045069516357634 # lists always use the browser
Browser driver defaults to Camofox (localhost:9377). Playwright users:
pip install ".[playwright]" # from the clone
export XTF_BROWSER=playwright # or: --browser-driver playwright
| Feature | Flag | Backend |
|---|---|---|
| Single tweet (text, stats, media, quotes) | --url | fxtwitter |
| Reply comments (threaded) | --url --replies | nitter / browser |
| User timeline (paginated) | --user | nitter / browser |
| Search | --search | nitter |
| User profile | --user-info | fxtwitter β nitter |
| X List tweets | --list | browser |
| X Article full text | --article | browser |
| Mentions monitor (incremental, cron-friendly) | --monitor | nitter / browser |
| Archive fetch results (dedupe, SQLite) | --ledger <db> | any |
| Search / stats the archive (offline) | --ledger <db> --query/--stats | offline |
Exit codes (cron-friendly): 0 success / no new mentions Β· 1 error / new mentions found Β· 2 monitor setup error.
Errors are machine-readable. Every failure carries error (human message) plus error_code β one of invalid_input, not_found, rate_limited, upstream_down, backend_unavailable, all_backends_failed β so agents can branch on it. all_backends_failed additionally includes per-backend error_causes.
--ledger <db> turns xtf into a fetch + archive + query local tweet library: every timeline / search / list / replies / single-tweet fetch is archived into a SQLite DB, deduped by tweet_id (INSERT OR IGNORE, idempotent). Schema is compatible with the tweet-ledger (OpenClaw) tweets table, so the same DB can be read by both tools.
# Fetch + archive a timeline
xtf --user YuLin807 --limit 20 --ledger ~/tweets.db
# Search the archive (offline)
xtf --ledger ~/tweets.db --query "sop"
# Stats: totals, languages, media/urls, time ranges
xtf --ledger ~/tweets.db --stats
Behavior without --ledger is unchanged (3.0.0-compatible). Archiving never breaks a successful fetch β on failure the JSON envelope carries ledger_error instead. Single-tweet (fxtwitter) dicts lack tweet_id, so the CLI injects it from the URL; --replies results are archived with is_reply=1 and in_reply_to_status_id pointing at the parent tweet.
tweets table: tweet_id (PK) Β· created_at Β· full_text Β· lang Β· source_file Β· is_reply Β· in_reply_to_status_id Β· retweeted_status_id Β· quoted_status_id Β· urls_json Β· media_json Β· raw_json Β· imported_at
End-to-end integration test record: docs/e2e-integration.md.
from xtf import Router, NotFound, RateLimited
router = Router() # backend="auto"
tweet = router.fetch_tweet("user", "1234567890") # dict, v1-compatible shape
tweets = router.fetch_timeline("user", limit=20) # list[Tweet]
replies = router.fetch_replies("user", "1234567890")
results = router.search("openclaw", limit=10)
for tw in tweets:
print(tw.author, tw.likes, tw.text)
print(tw.to_dict()) # JSON-ready
All backends normalize into one Tweet / Reply / Profile / Article schema β your downstream prompt only ever needs to describe one shape.
Everything is an environment variable (CLI flags override):
| Variable | Default | Meaning |
|---|---|---|
XTF_NITTER | http://127.0.0.1:8788 | Comma-separated Nitter instances, tried in order with failover |
XTF_BROWSER | camofox | Browser driver: camofox or playwright |
XTF_BROWSER_PORT | 9377 | Camofox HTTP port |
XTF_LANG | zh | Message language: zh or en |
XTF_CACHE_DIR | ~/.x-tweet-fetcher | Mentions-monitor cache |
NITTER_URL (the v1 name) is still honored as a fallback for XTF_NITTER.
Public Nitter instances are unreliable and frequently dead. Self-hosting is strongly recommended for timeline/search/replies:
# See https://github.com/zedeus/nitter for full setup
docker run -d -p 8788:8080 --name nitter zedeus/nitter:latest
export XTF_NITTER=http://127.0.0.1:8788
Multiple instances failover automatically:
export XTF_NITTER=http://127.0.0.1:8788,https://your-backup-instance.example
If no instance is reachable, you get a clear error (error_code: "all_backends_failed", with each backend's reason β e.g. backend_unavailable β under error_causes) telling you exactly what to set. Never a silent empty result.
src/xtf/
βββ models.py # Tweet / Reply / Profile / Article dataclasses
βββ backends/
β βββ fxtwitter.py # single tweets + profiles
β βββ nitter.py # direct HTTP, multi-instance failover
β βββ browser.py # Camofox / Playwright snapshot fetching
βββ parsers/ # pure functions, locked by fixture tests
βββ router.py # auto-fallback chain
βββ monitor.py # incremental mentions monitor
βββ cli.py # the `xtf` command
scripts/fetch_tweet.py # v1-compatible entry point (thin shim)
tests/fixtures/ # captured page structures β regression protection
python3 scripts/fetch_tweet.py still works with all v1 flags and exit codes, and JSON fields are unchanged for every mode except --search, whose per-tweet schema is now unified with --user (fields renamed, url/has_media/media_urls dropped). See MIGRATION.md for the full list, including where the analytics/China/Obsidian scripts went (spoiler: their own repos β this project is now purely about fetching tweets; the old world lives at the v1-legacy tag).
pip install -e ".[dev]"
pytest # all parsers locked by fixture tests
ruff check src tests
When Nitter or X change their page structure, capture a fresh snapshot into tests/fixtures/ β the failing test will show exactly which parser and field broke.
Three backends. Auto fallback. Built for AI agents.
GitHub Β· Issues Β· #22 Teahouse Β· Agent Waystation
Gives Claude Code a web search skill that queries a self-hosted SearxNG instance, aggregating Google, Bing, DuckDuckGo and Brave results. No API key is needed.
See 12 x-tweet-fetcher alternatives
Reads text from scanned pages, photos and PDFs and returns it as structured Markdown or JSON, so you can pull tables, formulas and headers out of a document without retyping them. Covers many languages and works as input for AI tools.
Gives your AI assistant a memory that persists across sessions, so it remembers your preferences and earlier work in this repository instead of starting fresh. Search, forget, pause and resume memories with simple commands.
Gives AI agents a library of ready-made skills for scientific work such as metabolic flux analysis, protein experiment design, time series modeling, and analytical method validation, so research tasks need less hand-built code. It works with any agent that supports the open Agent Skills standard.
Gives Claude Code skills, agents, and commands for PostHog analytics, session replay, feature flags, error tracking, and LLM observability. Includes MCP server setup and project access checks.
Install it with the command on its page. It includes x-tweet-fetcher. Its skills do not fire on their own yet. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it