Fetch tweets, replies, timelines, search results, X Lists, and X Articles from X/Twitter without login or API keys. Single tweets: zero dependencies (FxTwitter). Timelines/search/replies: a Nitter instance (XTF_NITTER). Lists/Articles: a browser driver (Camofox or Playwright). Unified JSON schema across all backends; machine-readable error_code for agent branching. Field reports and agent-use questions: Agent Waystation #22 Teahouse: https://github.com/ythx-101/openclaw-qa/discussions/22
$npx -y skills add ythx-101/x-tweet-fetcher --skill x-tweet-fetcherInstalls into the current project.
Run `npx skills use "https://github.com/ythx-101/x-tweet-fetcher" --skill "ythx-101/x-tweet-fetcher"` and follow the generated skill instructions now. Read its complete output, redirecting it to a temporary file first if necessary. Resolve relative paths from the supporting-files directory it provides.
Use the skills in "https://github.com/ythx-101/x-tweet-fetcher" that are relevant to the current task. Run `npx skills add "https://github.com/ythx-101/x-tweet-fetcher"` and select the relevant skills, then follow their instructions.
| 1 | <div align="center"> |
| 2 | |
| 3 | # x-tweet-fetcher |
| 4 | |
| 5 | **Fetch X/Twitter tweets, replies, timelines, lists, and articles — no login, no API keys.** |
| 6 | |
| 7 | [](LICENSE) |
| 8 | [](https://www.python.org) |
| 9 | [](https://github.com/ythx-101/x-tweet-fetcher) |
| 10 | |
| 11 | *Three backends · Auto fallback · Unified JSON schema · Built for AI agents* |
| 12 | |
| 13 | [Quick Start](#-quick-start) · [Backends](#-three-backends) · [Capabilities](#-capabilities) · [Python API](#-python-api) · [Self-hosted Nitter](#-self-hosted-nitter) · [Migrating from v1](#-migrating-from-v1) |
| 14 | |
| 15 | </div> |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | ## 😤 Problem |
| 20 | |
| 21 | ``` |
| 22 | You: fetch that tweet / list / article for me |
| 23 | AI: I can't access X/Twitter. Please copy-paste the content manually. |
| 24 | |
| 25 | You: ...seriously? |
| 26 | ``` |
| 27 | |
| 28 | X has no free API. Scraping gets you blocked. Browser automation is fragile in headless environments. |
| 29 | |
| 30 | **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. |
| 31 | |
| 32 | ## 🚀 Quick Start |
| 33 | |
| 34 | ```bash |
| 35 | git clone https://github.com/ythx-101/x-tweet-fetcher |
| 36 | cd x-tweet-fetcher && pip install . |
| 37 | |
| 38 | # Single tweet — works instantly, zero configuration |
| 39 | xtf --url https://x.com/user/status/1234567890 |
| 40 | |
| 41 | # User timeline (needs a Nitter instance, see below) |
| 42 | export XTF_NITTER=http://127.0.0.1:8788 |
| 43 | xtf --user elonmusk --limit 20 |
| 44 | |
| 45 | # Search |
| 46 | xtf --search "openclaw" --limit 10 |
| 47 | |
| 48 | # Human-readable output instead of JSON |
| 49 | xtf --user elonmusk --text-only |
| 50 | ``` |
| 51 | |
| 52 | Prefer not to install? `python3 scripts/fetch_tweet.py --url ...` works straight from the clone (same flags). |
| 53 | |
| 54 | ## 🔀 Three Backends |
| 55 | |
| 56 | | Backend | Deps | Speed | Covers | |
| 57 | |---------|------|-------|--------| |
| 58 | | **fxtwitter** | None (stdlib) | ⚡⚡ | Single tweets, user profiles | |
| 59 | | **nitter** | A Nitter instance | ⚡ | Timeline, search, replies, mentions | |
| 60 | | **browser** | Camofox *or* Playwright | 🐢 | Everything above + **Lists** + **X Articles** | |
| 61 | | **auto** (default) | Best available | ⚡→🐢 | Nitter first, browser fallback | |
| 62 | |
| 63 | ```bash |
| 64 | xtf --user elonmusk # auto (default) |
| 65 | xtf --user elonmusk --backend nitter # direct HTTP only |
| 66 | xtf --list 1455045069516357634 # lists always use the browser |
| 67 | ``` |
| 68 | |
| 69 | **Browser driver** defaults to Camofox (`localhost:9377`). Playwright users: |
| 70 | |
| 71 | ```bash |
| 72 | pip install ".[playwright]" # from the clone |
| 73 | export XTF_BROWSER=playwright # or: --browser-driver playwright |
| 74 | ``` |
| 75 | |
| 76 | ## 📊 Capabilities |
| 77 | |
| 78 | | Feature | Flag | Backend | |
| 79 | |---------|------|---------| |
| 80 | | Single tweet (text, stats, media, quotes) | `--url` | fxtwitter | |
| 81 | | Reply comments (threaded) | `--url --replies` | nitter / browser | |
| 82 | | User timeline (paginated) | `--user` | nitter / browser | |
| 83 | | Search | `--search` | nitter | |
| 84 | | User profile | `--user-info` | fxtwitter → nitter | |
| 85 | | X List tweets | `--list` | browser | |
| 86 | | X Article full text | `--article` | browser | |
| 87 | | Mentions monitor (incremental, cron-friendly) | `--monitor` | nitter / browser | |
| 88 | |
| 89 | **Exit codes** (cron-friendly): `0` success / no new mentions · `1` error / new mentions found · `2` monitor setup error. |
| 90 | |
| 91 | **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`. |
| 92 | |
| 93 | ## 🐍 Python API |
| 94 | |
| 95 | ```python |
| 96 | from xtf import Router, NotFound, RateLimited |
| 97 | |
| 98 | router = Router() # backend="auto" |
| 99 | tweet = router.fetch_tweet("user", "1234567890") # dict, v1-compatible shape |
| 100 | tweets = router.fetch_timeline("user", limit=20) # list[Tweet] |
| 101 | replies = router.fetch_replies("user", "1234567890") |
| 102 | results = router.search("opencl |