{"id":47300051,"url":"https://github.com/brettdavies/xurl-rs","last_synced_at":"2026-06-06T00:01:24.884Z","repository":{"id":344890499,"uuid":"1181433972","full_name":"brettdavies/xurl-rs","owner":"brettdavies","description":"Fast, ergonomic CLI for the X (Twitter) API. Rust port of xurl.","archived":false,"fork":false,"pushed_at":"2026-03-23T16:33:01.000Z","size":207,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-24T13:59:13.052Z","etag":null,"topics":["api-client","cli","oauth","rust","twitter","x-api"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/xurl-rs","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brettdavies.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-14T06:05:08.000Z","updated_at":"2026-03-21T05:00:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brettdavies/xurl-rs","commit_stats":null,"previous_names":["brettdavies/xurl-rs"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/brettdavies/xurl-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fxurl-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fxurl-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fxurl-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fxurl-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brettdavies","download_url":"https://codeload.github.com/brettdavies/xurl-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brettdavies%2Fxurl-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31317834,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T21:35:00.834Z","status":"ssl_error","status_checked_at":"2026-04-02T21:34:59.806Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-client","cli","oauth","rust","twitter","x-api"],"created_at":"2026-03-17T00:02:31.593Z","updated_at":"2026-06-06T00:01:24.874Z","avatar_url":"https://github.com/brettdavies.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# xurl-rs\n\nA fast, ergonomic CLI for the X (Twitter) API. OAuth1, OAuth2 PKCE, Bearer auth. Media upload. Streaming. Agent-native.\n\nRust port of [xurl](https://github.com/xdevplatform/xurl) — faster, type-safe, with shell completions and\nmachine-readable output.\n\n## Install\n\n### Homebrew\n\n```bash\nbrew tap brettdavies/tap\nbrew install xurl-rs\n```\n\n### Pre-built Binary\n\nDownload from [GitHub Releases](https://github.com/brettdavies/xurl-rs/releases) for Linux, macOS, and Windows.\n\n### Cargo\n\n```bash\ncargo install xurl-rs\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/brettdavies/xurl-rs\ncd xurl-rs\ncargo build --release\n# Binary at ./target/release/xr\n```\n\n## Quick Start\n\n```bash\n# Set up OAuth2 (browser-based, 30 seconds)\nxr auth apps add myapp --client-id YOUR_ID --client-secret YOUR_SECRET\nxr auth oauth2\n\n# Post\nxr post \"Hello from xurl-rs!\"\n\n# Read\nxr read 1234567890\n\n# Search\nxr search \"rust programming\" -n 20\n\n# Check your profile\nxr whoami\n```\n\n## Commands\n\nMost shortcut commands honor `-u USERNAME` to bypass the `/2/users/me` lookup. When set, the user-ID resolver hits\n`/2/users/by/username/\u003cu\u003e` instead, which is useful when `/me` is temporarily failing on X. Example: `xr like POST_ID -u\nalice`.\n\n### Posting\n\n```bash\nxr post \"Hello world!\"                        # Post\nxr post \"With media\" --media-id 12345          # Post with media\nxr reply 1234567890 \"Nice!\"                    # Reply\nxr reply https://x.com/user/status/123 \"Nice!\" # Reply by URL\nxr quote 1234567890 \"My take\"                  # Quote\nxr delete 1234567890                           # Delete\n```\n\n### Reading\n\n```bash\nxr read 1234567890                             # Read a post\nxr search \"golang\" -n 20                       # Search (10-100 results)\nxr whoami                                      # Your profile\nxr user @elonmusk                              # Look up user\nxr timeline                                    # Home timeline\nxr mentions                                    # Your mentions\n```\n\n### Engagement\n\n```bash\nxr like 1234567890                             # Like\nxr unlike 1234567890                           # Unlike\nxr repost 1234567890                           # Repost\nxr bookmark 1234567890                         # Bookmark\nxr bookmarks                                   # List bookmarks\nxr likes                                       # List likes\n```\n\n### Social Graph\n\n```bash\nxr follow @user                                # Follow\nxr unfollow @user                              # Unfollow\nxr following                                   # Who you follow\nxr followers                                   # Your followers\nxr mute @user                                  # Mute\n```\n\n### Direct Messages\n\n```bash\nxr dm @user \"Hey!\"                             # Send DM\nxr dms                                         # List DMs\n```\n\n### Schema Discovery\n\n```bash\nxr schema post                                 # JSON Schema for post response\nxr schema whoami                               # JSON Schema for whoami response\nxr schema --list                               # All commands and response types\nxr schema --all                                # All schemas as one JSON document\n```\n\nGenerate typed clients from schema output:\n\n```bash\n# TypeScript\nxr schema post | bunx json-schema-to-typescript \u003e types.ts\n\n# Python\nxr schema post | uvx --from datamodel-code-generator datamodel-codegen --output models.py\n```\n\n### Raw API Access\n\n```bash\nxr /2/users/me                                 # GET request\nxr -X POST /2/tweets -d '{\"text\":\"Hello!\"}'    # POST with JSON body\nxr --auth oauth1 /2/users/me                   # Explicit auth type\nxr -s /2/tweets/search/stream                  # Streaming\n```\n\n### Media Upload\n\n```bash\nxr media upload video.mp4                      # Upload media\nxr media status 1234567890                     # Check status\n```\n\n## Authentication\n\n### OAuth2 (Recommended)\n\n```bash\nxr auth apps add myapp --client-id ID --client-secret SECRET\nxr auth oauth2                                 # Opens browser\nxr auth oauth2 alice                           # Skip /2/users/me; save under \"alice\"\nxr auth oauth2 --app myapp alice               # Same, against a specific app\n```\n\n`xr auth oauth2` accepts an optional `[USERNAME]` positional. If X's `/2/users/me` endpoint is unreliable, supplying the\nhandle explicitly skips that lookup and stores the resulting token under the known username so shortcut commands resolve\nwithout `/me`.\n\n### OAuth1\n\n```bash\nxr auth oauth1 \\\n  --consumer-key CK \\\n  --consumer-secret CS \\\n  --access-token AT \\\n  --token-secret TS\n```\n\n### Bearer Token (App-Only)\n\n```bash\nxr auth app --bearer-token YOUR_TOKEN\n```\n\n### Multi-App Management\n\n```bash\nxr auth apps add prod --client-id ... --client-secret ...\nxr auth apps add dev --client-id ... --client-secret ...\nxr auth apps list\nxr auth default prod                           # Set default\nxr --app dev whoami                             # Per-request override\n```\n\nRegister an app with a custom OAuth2 callback URL via `--redirect-uri`:\n\n```bash\nxr auth apps add prod \\\n  --client-id ID \\\n  --client-secret SECRET \\\n  --redirect-uri http://localhost:8080/callback\n```\n\nUpdate credentials or the stored redirect URI on an existing app:\n\n```bash\nxr auth apps update prod --client-id NEW_ID --client-secret NEW_SECRET\nxr auth apps update prod --redirect-uri http://localhost:8080/callback\n```\n\nThe `REDIRECT_URI` environment variable still overrides the stored app value at runtime, so `auth apps update\n--redirect-uri` is best for setting your default per-app callback while env vars stay the temporary override path.\n\nInspect the effective redirect URI for an app (or the default app when `NAME` is omitted) — the output shows the\nresolved URI, its source (`env-var` | `app-config` | `built-in-default`), and the stored URI when an env var is\noverriding it:\n\n```bash\nxr auth apps redirect-uri get               # Default app\nxr auth apps redirect-uri get prod          # Named app\n```\n\nWrite the per-app stored redirect URI. The scheme must be `https`, or `http` with a host in `{localhost, 127.0.0.1,\n::1}`:\n\n```bash\nxr auth apps redirect-uri set prod http://localhost:8080/callback\n```\n\nIf you run `xr auth oauth2` without `--app`, the default app has no `client_id` set, and another registered app does\nhave credentials, the CLI prints a warning suggesting `xr auth oauth2 --app NAME` so the token lands on the right app\ninstead of the credential-less default.\n\n## Agent-Native Features\n\nBuilt for AI agents and automation:\n\n### Response Schema Discovery\n\n```bash\nxr schema --list                               # Discover all commands + response types\nxr schema post                                 # Get JSON Schema for any command's output\nxr schema --all                                # All schemas for MCP tool definitions\n```\n\n### Machine-Readable Output\n\n```bash\nxr --output json whoami                        # Raw JSON, no color\nxr --output jsonl search \"topic\"               # JSON Lines for streaming\nexport XURL_OUTPUT=json                          # Default to JSON\n```\n\n`xr --output json auth status` and `xr --output json auth apps list` emit a structured array with one object per\nregistered app. Per-app fields:\n\n- `name` — app name.\n- `client_id_hint` — first eight characters of the `client_id`, for visual identification without leaking the full ID.\n- `redirect_uri` — the effective redirect URI for this app.\n- `redirect_uri_source` — kebab-case provenance: `env-var` | `app-config` | `built-in-default`.\n- `redirect_uri_stored` — only present when the `REDIRECT_URI` environment variable overrides a stored app value;\n  carries the stored value so precedence is auditable.\n- `oauth2_users` — array of usernames with OAuth2 tokens stored under this app.\n- `oauth1` — boolean: OAuth1 credentials are stored for this app.\n- `bearer` — boolean: a bearer token is stored for this app.\n- `default` — boolean: this is the default app.\n- `oauth2_unnamed` — only present when `true`; indicates an unnamed-user OAuth2 token is stored after a refresh where\n  `/2/users/me` failed and no username was supplied.\n\n```bash\nxr --output json auth status | jq '.[] | select(.default) | .name'\n```\n\n### Quiet Mode\n\n```bash\nxr --quiet post \"Hello\"                        # No progress indicators\nxr -q search \"topic\"                           # Short form\n```\n\n### Non-Interactive Mode\n\n```bash\nxr --no-interactive whoami                     # Error instead of prompt\n# Exit code 2 if auth needed: \"authentication required: run xr auth login\"\n```\n\n### Structured Exit Codes\n\n| Code | Meaning       | Agent Action           |\n| ---- | ------------- | ---------------------- |\n| 0    | Success       | Continue               |\n| 1    | General error | Log and handle         |\n| 2    | Auth required | Run `xr auth oauth2`   |\n| 3    | Rate limited  | Retry with backoff     |\n| 4    | Not found     | Resource doesn't exist |\n| 5    | Network error | Check connectivity     |\n\n### NO_COLOR Support\n\n```bash\nNO_COLOR=1 xr whoami                           # Disable color (no-color.org)\n```\n\n## Shell Completions\n\n```bash\n# Bash\nxr completions bash \u003e ~/.local/share/bash-completion/completions/xr\n\n# Zsh (writes to the first directory on your fpath)\nxr completions zsh \u003e \"${fpath[1]}/_xr\"\n\n# Fish\nxr completions fish \u003e ~/.config/fish/completions/xr.fish\n\n# PowerShell\nxr completions powershell \u003e xr.ps1\n\n# Elvish\nxr completions elvish \u003e xr.elv\n```\n\nPre-generated scripts are also available in `completions/`.\n\n## Library Usage\n\nxurl-rs is also a Rust library. Add it to your `Cargo.toml`:\n\n```toml\n[dependencies]\nxurl-rs = \"1\"\n```\n\nAll 29 shortcut commands return typed responses via `ApiResponse\u003cT\u003e`:\n\n```rust\nuse xurl::api::{ApiResponse, Tweet, User, LikedResult, deserialize_response};\n\n// Typed response from deserialization\nlet resp: ApiResponse\u003cTweet\u003e = deserialize_response(json_value)?;\nprintln!(\"{}\", resp.data.text);\n\n// List responses\nlet resp: ApiResponse\u003cVec\u003cTweet\u003e\u003e = deserialize_response(json_value)?;\nfor tweet in \u0026resp.data {\n    println!(\"{}: {}\", tweet.id, tweet.text);\n}\n```\n\nAvailable types: `Tweet`, `User`, `DmEvent`, `UsageData`, `LikedResult`, `FollowingResult`, `DeletedResult`,\n`RetweetedResult`, `BookmarkedResult`, `BlockingResult`, `MutingResult`, `MediaUploadResponse`, `Includes`,\n`ResponseMeta`, `ApiError`.\n\nAll structs include `#[serde(flatten)] extra: BTreeMap\u003cString, Value\u003e` for forward compatibility with new API fields.\n\n## Troubleshooting\n\n### X Platform Enrollment\n\nIf OAuth succeeds but reads like `xr whoami` fail with an error body containing `client-forbidden` or\n`client-not-enrolled`, the current X platform fix is to move the app into the `Pay-per-use` package and use the\n`Production` environment in the developer console. This is an X platform enrollment issue, not a local callback-listener\nissue in `xr`.\n\nThe working recipe in the X developer console:\n\n1. Go to `Apps` -\u003e `Manage apps`.\n2. Open the app.\n3. Use `Move to package`.\n4. Choose `Pay-per-use`.\n5. Move the app to the `Production` environment.\n\nWithout that enrollment step, `xr whoami` and other `/2/*` reads can fail even when the OAuth callback and tokens are\nvalid.\n\n## vs Go Original\n\n| Feature                   | Go xurl          | xurl-rs                  |\n| ------------------------- | ---------------- | ------------------------ |\n| Language                  | Go               | Rust                     |\n| Memory safety             | GC               | Compile-time             |\n| Binary size               | ~15 MB           | ~8 MB                    |\n| Shell completions         | Built-in (cobra) | Built-in (clap_complete) |\n| `--output json`           | ❌                | ✅                        |\n| `--quiet`                 | ❌                | ✅                        |\n| `--no-interactive`        | ❌                | ✅                        |\n| Structured exit codes     | ❌                | ✅                        |\n| `NO_COLOR` support        | ❌                | ✅                        |\n| `XURL_OUTPUT` env var     | ❌                | ✅                        |\n| Typed response structs    | ❌                | ✅                        |\n| `xr schema` (JSON Schema) | ❌                | ✅                        |\n\n## Contributing\n\n```bash\ngit clone https://github.com/brettdavies/xurl-rs\ncd xurl-rs\ncargo test\ncargo clippy\n```\n\nSee [RELEASING.md](RELEASING.md) for release procedures.\n\n## License\n\nLicensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT) at your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2Fxurl-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrettdavies%2Fxurl-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrettdavies%2Fxurl-rs/lists"}