A command-line tool for interacting with the X (formerly Twitter) API, supporting both OAuth 1.0a and OAuth 2.0 authentication.
- Multi-app support — register multiple X API apps with separate credentials and tokens
- OAuth 2.0 PKCE flow authentication
- OAuth 1.0a authentication
- Multiple OAuth 2.0 account support per app
- Default app and default user selection (interactive Bubble Tea picker or single command)
- Persistent token storage in YAML (
~/.xurl), auto-migrates from legacy JSON - HTTP request customization (headers, methods, body)
- Per-request app override with
--app
brew install --cask xdevplatform/tap/xurlnpm install -g @xdevplatform/xurlcurl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | bashInstalls to ~/.local/bin. If it's not in your PATH, the script will tell you what to add.
go install github.com/xdevplatform/xurl@latestYou must have a developer account and app to use this tool.
Register your X API app credentials so they're stored in ~/.xurl (no env vars needed after this):
xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRETYou can register multiple apps:
xurl auth apps add prod-app --client-id PROD_ID --client-secret PROD_SECRET
xurl auth apps add dev-app --client-id DEV_ID --client-secret DEV_SECRETLegacy / env-var flow: You can also set
CLIENT_IDandCLIENT_SECRETas environment variables. They'll be auto-saved into the active app on first use.
Note: For OAuth 2.0 authentication, you must specify the redirect URI in the X API developer portal.
- Create an app at the X API developer portal.
- Go to authentication settings and set the redirect URI to
http://localhost:8080/callback.

- Register the app (if you haven't already):
xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET- Get your access keys:
xurl auth oauth2xurl auth app --bearer-token BEARER_TOKENxurl auth oauth1 --consumer-key KEY --consumer-secret SECRET --access-token TOKEN --token-secret SECRETList registered apps:
xurl auth apps listUpdate credentials on an existing app:
xurl auth apps update my-app --client-id NEW_ID --client-secret NEW_SECRETRemove an app:
xurl auth apps remove old-appSet the default app and user — interactive picker (uses Bubble Tea):
xurl auth defaultSet the default app and user — single command:
xurl auth default my-app # set default app
xurl auth default my-app alice # set default app + default userUse a specific app for a single request:
xurl --app dev-app /2/users/meView authentication status across all apps:
xurl auth statusExample output:
▸ my-app [client_id: VUttdG9P…]
▸ oauth2: alice
oauth2: bob
oauth1: ✓
bearer: ✓
dev-app [client_id: OTHER789…]
oauth2: (none)
oauth1: –
bearer: –
▸ on the left = default app. ▸ next to a user = default user.
xurl auth clear --all # Clear all tokens
xurl auth clear --oauth1 # Clear OAuth 1.0a tokens
xurl auth clear --oauth2-username USERNAME # Clear specific OAuth 2.0 token
xurl auth clear --bearer # Clear bearer tokenBasic GET request:
xurl /2/users/meCustom HTTP method:
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'Add headers:
xurl -H "Content-Type: application/json" /2/tweetsSpecify authentication type:
xurl --auth oauth2 /2/users/me
xurl --auth oauth1 /2/tweets
xurl --auth app /2/users/meUse specific OAuth 2.0 account:
xurl --username johndoe /2/users/meStreaming endpoints (like /2/tweets/search/stream) are automatically detected and handled appropriately. The tool will automatically stream the response for these endpoints:
/2/tweets/search/stream/2/tweets/sample/stream/2/tweets/sample10/stream/2/tweets/firehose/strea/lang/en/2/tweets/firehose/stream/lang/ja/2/tweets/firehose/stream/lang/ko/2/tweets/firehose/stream/lang/pt
For example:
xurl /2/tweets/search/streamYou can also force streaming mode for any endpoint using the --stream or -s flag:
xurl -s /2/users/mexurl can help you quickly set up a temporary webhook URL to receive events from the X API. This is useful for development and testing.
-
Start the local webhook server with ngrok:
Run the
webhook startcommand. This will start a local server and use ngrok to create a public URL that forwards to your local server. You will be prompted for your ngrok authtoken if it's not already configured via theNGROK_AUTHTOKENenvironment variable.xurl webhook start # Or with a specific port and output file for POST bodies xurl webhook start -p 8081 -o webhook_events.logThe command will output an ngrok URL (e.g.,
https://your-unique-id.ngrok-free.app/webhook). Note this URL. -
Register the webhook with the X API:
Use the ngrok URL obtained in the previous step to register your webhook. You'll typically use app authentication for this.
# Replace https://your-ngrok-url.ngrok-free.app/webhook with the actual URL from the previous step xurl --auth app /2/webhooks -d '{"url": "<your ngrok url>"}' -X POST
Your local
xurl webhook startserver will then handle the CRC handshake from Twitter and log incoming POST events (and write them to a file if-owas used).
The tool supports uploading media files to the X API using the chunked upload process.
Upload a media file:
xurl media upload path/to/file.mp4With custom media type and category:
xurl media upload --media-type image/jpeg --category tweet_image path/to/image.jpgCheck media upload status:
xurl media status MEDIA_IDWait for media processing to complete:
xurl media status --wait MEDIA_IDYou can also use the main command with the -F flag for direct media uploads:
- First, initialize the upload:
xurl -X POST '/2/media/upload?command=INIT&total_bytes=FILE_SIZE&media_type=video/mp4&media_catefory=tweet_video'- Then, append the media chunks:
xurl -X POST -F path/to/file.mp4 '/2/media/upload?command=APPEND&media_id=MEDIA_ID&segment_index=0'- Finally, finalize the upload:
xurl -X POST '/2/media/upload?command=FINALIZE&media_id=MEDIA_ID'- Check the status:
xurl '/2/media/upload?command=STATUS&media_id=MEDIA_ID'Tokens and app credentials are stored in ~/.xurl in YAML format. Each registered app has its own isolated set of tokens. Example:
apps:
my-app:
client_id: abc123
client_secret: secret456
default_user: alice
oauth2_tokens:
alice:
type: oauth2
oauth2:
access_token: "..."
refresh_token: "..."
expiration_time: 1234567890
bearer_token:
type: bearer
bearer: "AAAA..."
default_app: my-appMigration: If you have an existing JSON-format
~/.xurlfile from a previous version, it will be automatically migrated to the new YAML multi-app format on first use. Your tokens are preserved in adefaultapp.
Contributions are welcome!
This project is open-sourced under the MIT License - see the LICENSE file for details.