Multi-platform social TUI client for Instagram and X.com / Twitter. Browse your feed, like posts, and download media without leaving the terminal.
Warning:
instagrapiandtwikitare unofficial clients and may violate platform terms of service. Use burner accounts, not your primary accounts.
Python 3.11 is recommended.
twikitdepends on Js2Py, which currently breaks on Python 3.12+.
cd <repo-dir>
python3.11 -m venv .venv && source .venv/bin/activate
pip install -e .
# for image rendering in the terminal:
sudo dnf install chafa # Fedora
# or: sudo apt install chafaplume
# or: python -m plumeOn launch, Plume opens a home screen where you can choose Instagram or X.com. After login, the session is persisted in a local SQLite database so the next launch returns directly to the feed.
Username and password, with 2FA if enabled. instagrapi emulates the mobile
app, so the first login can take 10 to 20 seconds.
The username and password flow in twikit is currently broken because X
changed their JS bundle layout. Cookie login is the reliable path:
- Log in normally on x.com in your browser.
- Export cookies to a file, for example with Cookie-Editor on Chrome or Firefox.
- In the X login screen, press
Ctrl+Kor click Cookies.... - Paste the cookie payload into the text area, or preferably paste a file path and click Load file for large JSON exports.
- Click Sign in.
auth_tokenandct0are required; everything else is optional.
Accepted formats: Cookie-Editor JSON array, JSON key/value object, or a raw
Cookie: header like auth_token=...; ct0=....
Long pastes are often truncated by terminal bracketed paste mode. Prefer a file path when importing Cookie-Editor JSON.
All local state is stored in a single SQLite database:
~/.local/share/plume/plume.db
Tables:
| table | content |
|---|---|
session |
Per-platform cookies and session settings |
proxy |
Per-platform proxy configuration |
setting |
Generic key/value settings such as last platform |
Downloads are written to ~/Downloads/plume/<platform>/.
| key | action |
|---|---|
| ↑ / ↓ + Enter | Choose platform |
Esc |
Quit |
| key | action |
|---|---|
| Tab | Move between fields |
| Enter | Submit |
Ctrl+P |
Open proxy settings |
Ctrl+K |
Open cookie login for X |
Esc |
Back to home |
| key | action |
|---|---|
j / k or arrows |
Navigate |
l |
Like or unlike |
d |
Download media |
r |
Reload |
Esc |
Back to home |
q |
Quit |
Supported schemes: HTTP, HTTPS, SOCKS4, and SOCKS5 with optional authentication.
Proxy settings are stored per platform, so Instagram can use SOCKS5 while X
uses HTTP, for example. Access proxy settings from the login screen with
Ctrl+P or the Proxy... button.
twikit expects a JS bundle structure that no longer exists on x.com. Plume
installs two runtime patches from plume/x/client.py:
_StubClientTransactionreplacesClientTransaction, which fails with "Couldn't get KEY_BYTE indices", and returns an emptyX-Client-Transaction-Id. Read endpoints accept it._install_twikit_compat_patchesmakesUser.__init__defensive by pre-filling about 30legacy.*fields that X removed or renamed.
When upstream twikit catches up, these patches should become no-ops and can
be removed.
- Instagram rate limits are aggressive. The client throttles 1 to 3 seconds, but you should still avoid spamming actions.
- X username/password login is unreliable. Use cookies.
- Not implemented yet: DMs, stories, search, profile pages, posting, retweets.
- Image rendering uses
chafaANSI symbols. For true inline images, extend the renderer for kitty or ghostty graphics protocols.
Your IP or account was flagged by Instagram. Try this:
- Log in first on instagram.com or the mobile app from the same network.
- Wait 15 to 30 minutes.
- Configure a residential proxy with
Ctrl+P. - Use an account with some real history instead of a fresh one.
This is a structural upstream issue. See the X compatibility patch section
above. Use cookie login with Ctrl+K.
Same root cause as above. The stub fixes it automatically, but if the error
returns then the patch likely was not installed. Make sure XClient() is
instantiated before any call into twikit.
The terminal likely truncated your paste. Try this instead:
- Save the cookies to a file and use Load file.
- Use the raw
Cookie:header format, which is shorter and more tolerant.
The Twitter and Instagram CDNs reject non-browser user agents.
plume/image_render.py already sends a Firefox user agent, so if it still
fails then the CDN is likely rate limiting you. Wait a few minutes.
sqlite3 ~/.local/share/plume/plume.db \
"DELETE FROM session WHERE platform = 'instagram';"
# ou platform = 'x'Then log in again.
chafa outputs real ANSI sequences, but your terminal or Textual layer is not
rendering them. The renderer already converts through
rich.text.Text.from_ansi(...). If it still breaks, update your checkout and
reinstall with pip install -e ..
Stop all activity for about 30 minutes. If it persists, the account is likely restricted and may need 24 hours to cool down.
plume/
├── app.py # boot
├── config.py # path helpers
├── storage.py # SQLite session/proxy/setting store
├── proxy.py # ProxyConfig load/save helpers
├── format.py # compact_number, truncate
├── image_render.py # chafa → rich.text.Text
├── screens/
│ ├── home.py # Instagram / X platform picker
│ └── proxy.py # generic proxy screen per platform
├── instagram/
│ ├── client.py # wrapper instagrapi
│ └── screens/
│ ├── login.py
│ └── feed.py
└── x/
├── client.py # wrapper twikit + compat patches
└── screens/
├── login.py # user/password path
├── cookies.py # recommended cookie login
└── feed.py
pip install -e .
python -m plume # run from local sources
python -m compileall plume # quick syntax check
# inspect the database:
sqlite3 ~/.local/share/plume/plume.db
sqlite> .tables
sqlite> SELECT platform, updated_at FROM session;
sqlite> SELECT platform, host, port FROM proxy;Plume is distributed under the Boost Software License 1.0.
See LICENSE for the full text.