TwitterTools

A free and open-source suite of Twitter tools for X (Twitter). Ten tools, no login,
no tracking, no paid X API. Most features run directly in your browser; the server
only provides a stateless media proxy when a download needs one.
TwitterTools is an independent project and is not affiliated with X Corp.
Features
Download
- Video Downloader β save videos & GIFs as MP4 in every available quality, with file-size estimates
- Image Downloader β original-resolution photos, one at a time or all at once
Reader & Research
- Thread Reader β unroll a thread into one readable page; export Markdown, TXT, HTML or PDF
- Advanced Search Builder β build search-operator queries with a GUI, then open them on X
Writing
- Tweet Splitter β break long text into a numbered 280-character thread using X's real counting rules
- Character Counter β weighted counting exactly like X: CJK counts double, links always count 23
- Font Generator β bold, italic, script and more Unicode styles for posts and bios
Create & Share
- Screenshot Generator β render a post (or a thread with its replies) into a polished PNG card, light or dark
Developer
- URL & ID Parser β convert post URLs to IDs and Snowflake timestamps, and back
For AI agents
TwitterTools MCP β a Model Context Protocol
server (npx twittertools-mcp) that lets AI assistants fetch posts and threads,
count characters, and split text with X's real rules. No login, no tracking.
Highlight: a privacy-first bookmark manager
Bookmark Manager is the most distinctive
tool in the suite β a local-first X bookmark library:
- No account required β your bookmarks never leave your device (IndexedDB)
- Snapshots public posts (text, media, author) so they survive deletion
- Tags, notes, search, a built-in media viewer, JSON & Markdown export
- Installable as a PWA β viewed items stay available offline
Demo
https://twittertools.com β every tool works without an account.
Why open source?
Twitter tools often demand accounts, OAuth permissions, or opaque black-box services.
TwitterTools is open source so you can verify the opposite: nothing is tracked,
nothing is stored, and every claim on the site can be checked against the code.
Tech Stack
Frontend:
Astro + TypeScript + Tailwind CSS (a scoped service worker powers the installable Bookmark Manager)
Backend:
Python 3.8+ (3.11+ recommended) + aiohttp. Public posts are resolved through X's
free syndication endpoint (the same one that powers embeds on publisher sites) β
no developer account, no OAuth, no per-read billing.
Tests:
vitest (frontend units) + Playwright (page smoke tests) + an offline mock suite
for the backend β all run in CI on every push.
Self Hosting
The site is a static build (dist/) plus one small API service. Any host that can
reach cdn.syndication.twimg.com, pbs.twimg.com and video.twimg.com can run it.
1. Build the frontend
npm install
npm run build # -> dist/
2. Run the API
cd server
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python downloader_server.py # serves 127.0.0.1:8787
3. Serve it
Point a web server at dist/ and reverse-proxy /api/ to 127.0.0.1:8787.
A ready-made Apache vhost example lives in deploy/.
Two rules matter:
- Point the document root at
dist/, never at the repo root β that would
expose server/ and deploy/ to the web.
video.twimg.com 403s hotlink requests that carry a non-X Referer; the
backend already sends none β don't add one back.
For production, run the API under the provided systemd unit
(server/twittertools-api.service) and create
its state directory first (install -d -o www-data -g www-data /var/lib/twittertools)
so the tweet cache can persist across restarts. If persistence fails, the backend
logs a one-time "staying memory-only" warning and keeps working.
Configuration
| Variable | Default | Purpose |
|---|
TT_CACHE_FILE | next to the script (dev) | Disk path for the 10-minute tweet cache; "" disables persistence |
TT_GLOBAL_TWEET_RATE | 300/60 | Global lookup limiter, limit/window-seconds |
TT_GLOBAL_DOWNLOAD_RATE | 120/60 | Global download limiter |
TT_THREAD_DOWNWALK | 0 | 1 enables best-effort conversation down-walk (guest GraphQL) |
TT_MEDIA_HOSTS | β | Extra allowed media hosts (for tests) |
TT_PROXY | β | Optional outbound proxy (e.g. socks5h://127.0.0.1:1080) for hosts that can't reach X directly |
Per-IP limits (60 lookups/min, 40 downloads/min) are built in. Aggregate, anonymous
counters are served at GET /api/healthz.
Updating
git pull && npm install && npm run build
# rsync dist/ to your web root; rsync server/ and restart the service when it changes
Local development
npm run dev # site on :4321, API auto-wired to :8787
cd server && .venv/bin/python mock_test.py # offline integration suite (no network)
Honest limitation: the syndication endpoint is undocumented and has no SLA. If X
changes it, only the fetch layer in
server/downloader_server.py
(fetch_syndication() / candidate_tokens()) needs updating β GET /api/healthz
shows the upstream status distribution.
License
MIT