A scriptable CLI for automating Twitter/X workflows using the v2 API.
Perfect for building bots, monitoring social media, aggregating content, or integrating Twitter into your automation pipelines. Single binary, JSON output, works with standard Unix tools.
- Built for Automation - JSON output, exit codes, environment variables - integrates seamlessly with bash, cron, systemd
- Complete API Coverage - Tweets, DMs, users, media upload, streaming, likes, retweets, bookmarks, timelines
- Single Binary - No runtime dependencies, just download and run
- Script-Friendly - Pipeable output, works with
jq,grep,awkand other Unix tools - Real-Time Processing - Monitor tweets as they happen using Twitter's filtered stream API
- Resilient - Automatic retries with exponential backoff, honors
Retry-Afterand rate-limit headers, clean exit codes and stderr logging
ctw is only as useful as your X API access, and X's pricing changed drastically over the years -- please read this before installing so there are no surprises:
| Commands | Access required (as of 2026) |
|---|---|
tweets create/delete, media upload, dms send |
Pay-per-use posting (about $0.015 per post) -- affordable for bots and cron jobs |
search, counts, timelines, users, likes, retweets, bookmarks |
Pay-per-use reads (about $0.005 per read, capped monthly) |
watch, stream (filtered stream) |
Pro or Enterprise tier only -- these are expensive legacy plans; streaming does not work on pay-per-use or Basic access |
The free tier is closed to new developers. Check the X Developer Portal for current pricing before building on any of these endpoints. If a command returns HTTP 403, your access level does not include that endpoint -- it is not a bug in ctw.
# Install (macOS)
brew tap 0dayfall/tap && brew install ctw
# Configure
export BEARER_TOKEN="your_twitter_bearer_token"
# Start automating
ctw init
ctw search recent --query "golang" | jq -r '.data[].text'
ctw users lookup --usernames "github,golang"
ctw tweets create --text "Hello from automation!"# Real-time monitoring with keyword filtering
ctw watch --keyword "@YourBrand" --auto-setup | grep "urgent"
# JSON lines output for automation
ctw watch --keyword "@YourBrand" --auto-setup --json > brand_mentions.jsonlMore copy-paste examples live in script/recipes/.
# Gather tweets for analysis
ctw search recent --query "climate change" --param "max_results=100" \
| jq -r '.data[] | [.created_at, .text] | @csv' > data.csv# Publish generated content on a schedule (cron-friendly, pay-per-use)
./generate_daily_digest.sh > digest.txt
ctw tweets create --text "$(cat digest.txt)"# Publish with media (cron job)
MEDIA_ID=$(ctw media upload --file chart.png | jq -r '.media_id_string')
ctw tweets create --text "Daily update" --media-ids "$MEDIA_ID"# Stream to Slack/Discord webhook
ctw watch --keyword "BREAKING" --auto-setup \
| while read -r line; do curl -X POST $WEBHOOK -d "$line"; doneTweets & Content
- Create, delete, and lookup tweets
- Upload media (images, videos, GIFs) with chunked upload
- Search recent tweets with filtering
- Get tweet counts and analytics
Streaming
- Real-time filtered stream with keyword monitoring
- Rule management (add, list, delete)
- Watch command for easy keyword tracking
User Operations
- Lookup users by username or ID
- Follow/unfollow users
- Block/unblock users
Engagement
- Like/unlike tweets
- Retweet/unretweet
- Add/remove bookmarks
- List liked tweets, bookmarks, retweeters
Timelines
- User tweets timeline
- Mentions timeline
- Home timeline (reverse chronological)
Direct Messages
- Send DMs
- List conversations
- Delete messages
macOS (Homebrew)
brew tap 0dayfall/tap && brew install ctwUbuntu/Debian
# Download from releases or build locally
make deb
sudo dpkg -i build/ctw_<version>_amd64.debFrom Source
git clone https://github.com/0dayfall/ctw.git
cd ctw
go build -o ctw ./cmd/ctw
sudo mv ctw /usr/local/bin/Pre-built Binaries
Download from releases for Linux, macOS, Windows (amd64/arm64).
See INSTALL.md for detailed installation instructions and docs/GORELEASER_SETUP.md for building releases.
ctw initThis verifies your credentials and writes a config file with an env reference.
Default locations:
- macOS/Linux:
~/.config/ctw/config.toml - Windows:
%APPDATA%\\ctw\\config.toml
Example:
[auth]
bearer_token = "env:BEARER_TOKEN"
[http]
user_agent = "ctw/0.2"
timeout = "15s"
retry = 3
[output]
pretty = false
[stream]
backoff_max = "2m"See config.example.toml for a ready-to-copy template.
Flags > Env vars > Config file > Defaults
# Required: Twitter API bearer token
export BEARER_TOKEN="your_twitter_bearer_token_here"
# Optional: custom user agent
export USER_AGENT="my-bot/1.0"
# Optional: config overrides
export CTW_TIMEOUT="15s"
export CTW_RETRY="3"
export CTW_PRETTY="false"
export CTW_STREAM_BACKOFF_MAX="2m"Get your bearer token from the Twitter Developer Portal.
# Monitor keywords and pipe to processing
ctw watch --keyword "golang" --auto-setup | grep "tutorial"
# Multi-keyword brand monitoring
ctw watch --keyword "@YourBrand" --keyword "YourProduct" --auto-setup --show-user
# Stream with complex rules
ctw stream rules add --value "bitcoin OR ethereum lang:en -is:retweet"
ctw stream# Search and extract with jq
ctw search recent --query "AI ethics" --param "max_results=100" \
| jq -r '.data[] | [.author_id, .text] | @csv'
# Get tweet counts over time
ctw counts recent --query "climate change" --granularity day
# Monitor user activity
ctw timelines user --user-id 123 --param "max_results=50"# Simple tweet
ctw tweets create --text "Hello from automation"
# Tweet with media
MEDIA=$(ctw media upload --file chart.png --category tweet_image | jq -r '.media_id_string')
ctw tweets create --text "Daily metrics" --media-ids "$MEDIA"
# Scheduled via cron: 0 9 * * * /usr/local/bin/post_daily_update.sh# Lookup users
ctw users lookup --usernames "github,golang,docker"
ctw users lookup --ids "123,456,789"
# Manage relationships
ctw users follow --source-id YOUR_ID --target-id 123
ctw users block --source-id YOUR_ID --target-id 456# Like or retweet a specific tweet
ctw likes add --user-id YOUR_ID --tweet-id 1234567890
ctw retweets add --user-id YOUR_ID --tweet-id 1234567890
# Curate a review queue: bookmark interesting results to act on yourself
ctw search recent --query "open source" --param "max_results=10" \
| jq -r '.data[].id' \
| xargs -I {} ctw bookmarks add --user-id YOUR_ID --tweet-id {}
ctw bookmarks list --user-id YOUR_IDNote: Bulk, unsupervised liking or retweeting violates X's automation rules and can get an account suspended. Keep engagement actions deliberate; automate collection (search, bookmarks) rather than engagement.
# Error handling with exit codes
if ctw tweets create --text "test"; then
echo "Posted successfully"
else
echo "Failed with code $?"
fi
# Loop through results
ctw search recent --query "golang" | jq -r '.data[].text' | while read -r tweet; do
echo "Processing: $tweet"
done
# Combine multiple operations
USER_ID=$(ctw users lookup --usernames "twitter" | jq -r '.data[0].id')
ctw timelines user --user-id "$USER_ID"For detailed help on any command:
ctw --help
ctw stream --help
ctw search --help
# ... etcAvailable Commands:
watch- Monitor tweets with keyword filtering (easiest)stream- Manage filtered stream rules and connectsearch- Search recent or all tweetscounts- Get tweet count aggregationstweets- Create, delete, and lookup tweetsusers- Lookup users and manage relationshipstimelines- Get user, mentions, and home timelineslikes- Like, unlike, and list liked tweetsretweets- Retweet, unretweet, and list retweetersbookmarks- Add, remove, and list bookmarksdms- Send, list, and delete direct messagesmedia- Upload images, videos, and GIFs
- AUTOMATION.md - Comprehensive automation guide with real-world examples
- STREAMING.md - Detailed guide to Twitter's filtered stream API
- KEYWORD_SCREENING.md - Quick reference for keyword filtering
- INSTALL.md - Installation instructions for all platforms
- script/sh/examples/ - Ready-to-use automation scripts
ctw is a thin CLI wrapper around the Twitter v2 API. It handles:
- Authentication - Bearer token management
- HTTP - Configurable client with rate-limit tracking and automatic retries (exponential backoff, honors
Retry-After/x-rate-limit-reset; non-idempotent requests are only replayed when the API confirms they were not processed) - JSON - Type-safe request/response handling
- Errors - Proper exit codes and error messages
- Streaming - Real-time filtered stream processing
All commands output JSON to stdout and logs/errors to stderr, making it perfect for Unix pipelines and automation scripts.
cmd/ctw/ # CLI commands (Cobra)
internal/client/ # HTTP client with auth and rate-limits
internal/tweet/ # Tweet services (publish, search, stream, likes, etc.)
internal/users/ # User services (lookup, follow, block)
internal/media/ # Media upload (chunked upload for large files)
internal/dm/ # Direct message services
script/sh/ # Shell script examples and testing utilities
Run tests:
go test ./...Tests use httptest.Server mocks - no live API calls required.
Build locally:
make build # Builds to bin/ctw
make install # Installs to /usr/local/bin
make deb # Creates .deb packageContributing:
- Follow the service pattern: accept
context.Context, return typed structs +client.RateLimitSnapshot - Keep CLI commands thin - parse flags, call service, print JSON
- Add tests using
httptest.Serverhelpers - Update documentation when adding features
Issues & Contributions:
- Report bugs or request features via GitHub Issues
- PRs welcome -- small, focused changes are easiest to review
Twitter API has rate limits. ctw tracks and reports rate-limit headers:
ctw search recent --query "test" 2>&1 | grep "Rate Limit"Rate limits vary by endpoint. See Twitter's documentation for details.
MIT -- see LICENSE.