xurl-rs
A fast, ergonomic CLI for the X (Twitter) API. OAuth1, OAuth2 PKCE, Bearer auth. Media upload. Streaming. Agent-native.
Rust port of xurl — faster, type-safe, with shell completions and machine-readable output.
Install
Homebrew
Pre-built Binary
Download from GitHub Releases for Linux, macOS, and Windows.
Cargo
From Source
# Binary at ./target/release/xr
Quick Start
# Set up OAuth2 (browser-based, 30 seconds)
# Post
# Read
# Search
# Check your profile
Commands
Most shortcut commands honor -u USERNAME to bypass the /2/users/me lookup. When set, the user-ID resolver hits
/2/users/by/username/<u> instead, which is useful when /me is temporarily failing on X. Example: xr like POST_ID -u alice.
Posting
Reading
Engagement
Social Graph
Direct Messages
Schema Discovery
Generate typed clients from schema output:
# TypeScript
|
# Python
|
Raw API Access
Media Upload
Authentication
OAuth2 (Recommended)
xr auth oauth2 accepts an optional [USERNAME] positional. If X's /2/users/me endpoint is unreliable, supplying the
handle explicitly skips that lookup and stores the resulting token under the known username so shortcut commands resolve
without /me.
OAuth1
Bearer Token (App-Only)
Multi-App Management
Register an app with a custom OAuth2 callback URL via --redirect-uri:
Update credentials or the stored redirect URI on an existing app:
The REDIRECT_URI environment variable still overrides the stored app value at runtime, so auth apps update --redirect-uri is best for setting your default per-app callback while env vars stay the temporary override path.
Inspect the effective redirect URI for an app (or the default app when NAME is omitted) — the output shows the
resolved URI, its source (env-var | app-config | built-in-default), and the stored URI when an env var is
overriding it:
Write the per-app stored redirect URI. The scheme must be https, or http with a host in {localhost, 127.0.0.1, ::1}:
If you run xr auth oauth2 without --app, the default app has no client_id set, and another registered app does
have credentials, the CLI prints a warning suggesting xr auth oauth2 --app NAME so the token lands on the right app
instead of the credential-less default.
Agent-Native Features
Built for AI agents and automation:
Response Schema Discovery
Machine-Readable Output
# Default to JSON
xr --output json auth status and xr --output json auth apps list emit a structured array with one object per
registered app. Per-app fields:
name— app name.client_id_hint— first eight characters of theclient_id, for visual identification without leaking the full ID.redirect_uri— the effective redirect URI for this app.redirect_uri_source— kebab-case provenance:env-var|app-config|built-in-default.redirect_uri_stored— only present when theREDIRECT_URIenvironment variable overrides a stored app value; carries the stored value so precedence is auditable.oauth2_users— array of usernames with OAuth2 tokens stored under this app.oauth1— boolean: OAuth1 credentials are stored for this app.bearer— boolean: a bearer token is stored for this app.default— boolean: this is the default app.oauth2_unnamed— only present whentrue; indicates an unnamed-user OAuth2 token is stored after a refresh where/2/users/mefailed and no username was supplied.
|
Quiet Mode
Non-Interactive Mode
# Exit code 2 if auth needed: "authentication required: run xr auth login"
Structured Exit Codes
| Code | Meaning | Agent Action |
|---|---|---|
| 0 | Success | Continue |
| 1 | General error | Log and handle |
| 2 | Auth required | Run xr auth oauth2 |
| 3 | Rate limited | Retry with backoff |
| 4 | Not found | Resource doesn't exist |
| 5 | Network error | Check connectivity |
NO_COLOR Support
NO_COLOR=1
Shell Completions
# Bash
# Zsh (writes to the first directory on your fpath)
# Fish
# PowerShell
# Elvish
Pre-generated scripts are also available in completions/.
Library Usage
xurl-rs is also a Rust library. Add it to your Cargo.toml:
[]
= "1"
All 29 shortcut commands return typed responses via ApiResponse<T>:
use ;
// Typed response from deserialization
let resp: = deserialize_response?;
println!;
// List responses
let resp: = deserialize_response?;
for tweet in &resp.data
Available types: Tweet, User, DmEvent, UsageData, LikedResult, FollowingResult, DeletedResult,
RetweetedResult, BookmarkedResult, BlockingResult, MutingResult, MediaUploadResponse, Includes,
ResponseMeta, ApiError.
All structs include #[serde(flatten)] extra: BTreeMap<String, Value> for forward compatibility with new API fields.
Troubleshooting
X Platform Enrollment
If OAuth succeeds but reads like xr whoami fail with an error body containing client-forbidden or
client-not-enrolled, the current X platform fix is to move the app into the Pay-per-use package and use the
Production environment in the developer console. This is an X platform enrollment issue, not a local callback-listener
issue in xr.
The working recipe in the X developer console:
- Go to
Apps->Manage apps. - Open the app.
- Use
Move to package. - Choose
Pay-per-use. - Move the app to the
Productionenvironment.
Without that enrollment step, xr whoami and other /2/* reads can fail even when the OAuth callback and tokens are
valid.
vs Go Original
| Feature | Go xurl | xurl-rs |
|---|---|---|
| Language | Go | Rust |
| Memory safety | GC | Compile-time |
| Binary size | ~15 MB | ~8 MB |
| Shell completions | Built-in (cobra) | Built-in (clap_complete) |
--output json |
❌ | ✅ |
--quiet |
❌ | ✅ |
--no-interactive |
❌ | ✅ |
| Structured exit codes | ❌ | ✅ |
NO_COLOR support |
❌ | ✅ |
XURL_OUTPUT env var |
❌ | ✅ |
| Typed response structs | ❌ | ✅ |
xr schema (JSON Schema) |
❌ | ✅ |
Contributing
See RELEASING.md for release procedures.
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.