birdnode
v1.9.5
Published
Cross-platform CLI for X (Twitter) — reads Chrome cookies and calls X's GraphQL APIs directly. No API key needed.
Downloads
1,975
Maintainers
Readme
birdnode
Node.js CLI for X (Twitter) — reads Chrome cookies and directly calls X's internal GraphQL APIs. Supports macOS / Linux / Windows.
How it Works
- Reads Chrome Cookies: Extracts
ct0andauth_tokenusing OS-specific decryption methods (see below). - Dynamic QueryId Discovery: Automatically fetches and parses X.com's JS Bundles to dynamically find the latest GraphQL QueryIds (preventing hardcoded IDs from breaking).
- Calls X's GraphQL APIs: Uses the extracted cookies and QueryIds to send POST requests directly to X's internal GraphQL endpoints.
Cookie Decryption Methods
| Platform | Cookie Path | Algorithm | Key Source |
|------|------------|---------|---------|
| macOS | ~/Library/.../Chrome/Default/Cookies | AES-128-CBC | macOS Keychain (Chrome Safe Storage) |
| Linux | ~/.config/google-chrome/Default/Cookies | AES-128-CBC | Hardcoded "peanuts", or GNOME Keyring / KWallet |
| Windows | %LOCALAPPDATA%\...\Chrome\User Data\Default\Cookies | AES-256-GCM | Windows DPAPI decrypts Master Key in Local State |
Note: For Chrome 127+, X prepends a 32-byte SHA256 domain hash to the decrypted data, which the tool automatically detects and strips.
Installation & Usage
You can run it directly via npx without installing globally:
npx birdnode helpOr install it globally:
npm install -g birdnodeRequires Node.js 18+ (uses the built-in fetch).
Commands
# Show help
birdnode help
# View currently logged-in account
birdnode whoami
birdnode whoami --json
# Search tweets
birdnode search "bitcoin" -n 20
birdnode search "solana memecoin" -n 5 --product Top
birdnode search "bitcoin" -n 10 --json
# Deep Token Analysis (DexScreener + Official + Global Tweets)
birdnode analyze 0x514910771af9ca656af840dff83e8264ecf986ca
birdnode analyze 0x5149... --json
# View user profile
birdnode user elonmusk
birdnode user @vitalikbuterin
birdnode user elonmusk --json
# Check who is mentioning/talking AT a user
birdnode mentions elonmusk
birdnode mentions @vitalikbuterin -n 50
birdnode mentions elonmusk --json
# Check credentials status
birdnode check
# Post a tweet
birdnode tweet "Hello World 🚀"
# Reply to a tweet
birdnode reply 1234567890987654321 "This is a reply to your post!"
# Fetch main tweet only (ignores comments)
birdnode read 1234567890987654321
# You can also pass full X/Twitter URLs instead of raw IDs!
birdnode read https://x.com/elonmusk/status/1234 --json
# Fetch ONLY the replies/comments left by others
birdnode replies 1234567890987654321
birdnode replies 1234567890987654321 --json
# Fetch the author's self-thread conversation
birdnode thread 1234567890987654321
birdnode thread 1234567890987654321 --json
# Get followers list
birdnode followers <user_id> -n 50
# Get following list
birdnode following <user_id> -n 50Options
| Option | Description |
|------|------|
| -n <count> | Number of results to return (default: 20) |
| --product <type> | Search criteria: Latest | Top (default: Latest) |
| --chrome-profile <name>| Specific Chrome Profile to extract cookies from (default: auto-detect) |
| --auth-token <token> | Manually override the auth_token cookie |
| --ct0 <token> | Manually override the ct0 cookie |
| --json | Return raw JSON output |
Environment Variables
You can completely bypass the local Chrome database lookup by passing credentials via environment variables:
export CT0="your_ct0_token"
export AUTH_TOKEN="your_auth_token"
birdnode search "bitcoin"Credential Priority
- CLI flags (
--ct0and--auth-token) - Environment variables (
CT0/TWITTER_CT0,AUTH_TOKEN/TWITTER_AUTH_TOKEN) - Chromium / Chrome cookie database fallback (requires permission to read)
Notes
macOS
- Chrome must be logged into x.com.
- The first time it runs, macOS may prompt for Keychain Access. Click "Allow" or "Always Allow".
- If permission is denied, open "Keychain Access" and manually grant access to "Chrome Safe Storage".
Linux
- Supports both Google Chrome and Chromium.
- If GNOME Keyring is installed and unlocked, its password will be prioritized.
- On headless/server setups without a desktop keyring, Chrome automatically falls back to an internal hardcoded password (
"peanuts"), which is fully supported. - Snap Chromium path is also supported:
~/snap/chromium/current/.config/chromium.
Windows
- Chrome 80+ uses Windows DPAPI to protect an AES-256 master key (stored in the
Local StateJSON file). - You must run the command as the same Windows user that runs Chrome (DPAPI is tied to the active Windows user account).
- A temporary PowerShell process is spawned (
ProtectedData.Unprotect) to decrypt the key, requiring basic PowerShell execution permissions. - Older Chrome (< 80) cookies encrypted completely via DPAPI are also seamlessly supported.
General
- X's GraphQL QueryIds change frequently.
birdnodeautomatically parses modern X JS bundles at runtime to discover the latestqueryIdidentifiers. - Please respect X's Terms of Service and avoid aggressive or severely rate-limiting requests.
Architecture
birdnode/
├── src/
│ ├── index.js # CLI entry point, argument parsing and printing
│ ├── token-analyzer.js # Complex multi-step analysis orchestration
│ ├── query-id-resolver.js # Dynamic GraphQL query engine and extraction
│ ├── cookie-extractor.js # Cross-platform cookie decrypter (macOS/Linux/Win)
│ └── twitter-api.js # X GraphQL and REST API logic
├── test/
│ └── test-new-api.js # Live API automated testing scripts
└── package.jsonAdvanced Library Usage (Node.js API)
In addition to the CLI commands, you can seamlessly import birdnode directly as an NPM dependency into your bots/projects. It exposes powerful native capabilities that bypass normal GraphQL blocks:
const { getXCredentials } = require('birdnode/src/cookie-extractor');
const { postTweet, getTweetThread } = require('birdnode/src/twitter-api');
(async () => {
const creds = await getXCredentials();
// 1. Post a new tweet natively
const newTweet = await postTweet({
text: 'Automated post testing!',
ct0: creds.ct0,
authToken: creds.authToken
});
// 2. Fetch an entire Thread (Main Tweet + Thread + All user Replies)
const threadData = await getTweetThread({
tweetId: '1234567890987654321', // Any active tweet ID
ct0: creds.ct0,
authToken: creds.authToken
});
console.log(threadData.thread);
})();