Xurl: Official X/Twitter API CLI for Hermes Agent
X/Twitter via xurl CLI: raw post search, posting, DM, media.
Written by Neura Market from the official Hermes Agent documentation for Xurl. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationxurl: X API v2 CLI Reference
xurl is the official command-line tool for the X (Twitter) API v2. It provides shortcut commands for common actions and raw curl-style access to any endpoint. All commands output JSON to stdout. Errors also return JSON with a non-zero exit code.
Setup
Prerequisites
Before using xurl, you must complete a one-time setup manually on your own machine. The tool stores credentials in ~/.xurl in YAML format. Never read, print, parse, summarize, upload, or send ~/.xurl to any LLM context.
Installation
Choose one of these methods:
# Shell script (installs to ~/.local/bin, no sudo, works on Linux + macOS)
curl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | bash
# Homebrew (macOS)
brew install --cask xdevplatform/tap/xurl
# npm
npm install -g @xdevplatform/xurl
# Go
go install github.com/xdevplatform/xurl@latest
Docker Considerations
In Docker, the HOME environment variable must be /opt/data/home for Hermes tool subprocesses. The file ~/.xurl resolves to /opt/data/home/.xurl, not /opt/data/.xurl. If HOME=/opt/data xurl auth status succeeds but HOME=/opt/data/home xurl auth status shows no apps or tokens, Hermes tool calls will not see credentials. Use the following commands for Docker setup:
HOME=/opt/data/home xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
HOME=/opt/data/home xurl auth oauth2 --app my-app YOUR_USERNAME
HOME=/opt/data/home xurl auth default my-app YOUR_USERNAME
HOME=/opt/data/home xurl auth status
One-Time User Setup
This setup must be done manually, outside any agent session.
- Create or open an app at https://developer.x.com/en/portal/dashboard.
- Set the redirect URI to
http://localhost:8080/callback. - Copy the Client ID and Client Secret from the dashboard. Note: if you see two "Client Secret" values, the first is actually the Client ID. Confirm on the "Keys and tokens" page; a Client ID ends in
MTpjaQ. - Register the app locally:
xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET - Authenticate with OAuth 2.0 PKCE:
This opens a browser for the OAuth flow. If you getxurl auth oauth2 --app my-appUsernameNotFoundor a 403 error, use the explicit username form (v1.1.0+):xurl auth oauth2 --app my-app YOUR_USERNAME - Set the app as default:
xurl auth default my-app - Verify everything works:
xurl auth status xurl whoami
Authentication
Authentication Management
Multiple Apps
Each registered app has isolated credentials and tokens. Switch between apps with xurl auth default or the --app flag.
xurl auth default prod alice # prod app, alice user
xurl --app staging /2/users/me # one-off against staging
Multiple Accounts Per App
Use -u or --username to select a specific OAuth2 account when multiple exist for an app. Set a default user with xurl auth default APP USER.
Auth Status
xurl auth status
List and Remove Apps
xurl auth apps list
xurl auth apps remove NAME
Auth Type Override
Use the --auth flag to force a specific auth type: oauth1, oauth2, or app.
Agent Workflow
When using xurl in an automated agent session, follow these rules:
- Verify prerequisites with
xurl --helpandxurl auth status. - Before using
xurl search, confirm the task needs actual post objects, authenticated context, or leads to a write action. - Check the default app has credentials by parsing
auth statusoutput. The default app is marked with ▸. If the default app showsoauth2: (none)but another app has a valid oauth2 user, tell the user to runxurl auth default <app>. - If authentication is missing entirely, stop and direct the user to the One-Time User Setup. Do not attempt to register apps or pass secrets.
- Start with a cheap read command (
xurl whoami,xurl user @handle,xurl search ... -n 3) to confirm reachability. - Confirm the target post/user and the user's intent before any write action (post, reply, like, repost, DM, follow, block, delete).
- Only
xurlcommand output (or raw X API response) proves a state-changing X action happened. Never report a write as done based on search results, summaries, or prior context. - Use JSON output directly.
- Never paste
~/.xurlcontents into the conversation.
Forbidden Flags in Agent Sessions
Never use --verbose or -v in agent sessions, as they can leak auth headers or tokens. The following flags that accept inline secrets are also forbidden: --bearer-token, --consumer-key, --consumer-secret, --access-token, --token-secret, --client-id, --client-secret.
Post Operations
Create a Post
xurl post "Hello world!"
xurl post "Check this out" --media-id MEDIA_ID
xurl post "Thread pics" --media-id 111 --media-id 222
Reply to a Post
xurl reply 1234567890 "Great point!"
xurl reply https://x.com/user/status/1234567890 "Agreed!"
xurl reply 1234567890 "Look at this" --media-id MEDIA_ID
The shorthand xurl reply POST_ID "Nice post!" also works.
Quote a Post
xurl quote 1234567890 "Adding my thoughts"
The shorthand xurl quote POST_ID "My take" also works.
Delete a Post
xurl delete 1234567890
The shorthand xurl delete POST_ID also works.
Read a Post
Accepts either a post ID or a full URL like https://x.com/user/status/1234567890. The tool extracts the ID automatically.
xurl read 1234567890
xurl read https://x.com/user/status/1234567890
The shorthand xurl read POST_ID also works.
Search Posts
xurl search "golang"
xurl search "from:elonmusk" -n 20
xurl search "#buildinpublic lang:en" -n 15
The shorthand xurl search "QUERY" -n 10 also works.
X Articles
For X Articles, use raw API mode instead of the read shortcut. Request the article tweet field and ingest data.article.plain_text.
xurl --app APP_NAME '/2/tweets/2057909493250539891?expansions=author_id,attachments.media_keys,referenced_tweets.id&tweet.fields=created_at,lang,public_metrics,context_annotations,entities,possibly_sensitive,conversation_id,in_reply_to_user_id,referenced_tweets,article'
User Operations
Whoami (Current User)
xurl whoami
User Lookup
Usernames work with or without the leading @.
xurl user elonmusk
xurl user @XDevelopers
Timeline and Mentions
xurl timeline -n 25
xurl mentions -n 20
The shorthand xurl mentions -n 10 also works.
Engagement Operations
Like and Unlike
xurl like 1234567890
xurl unlike 1234567890
The shorthand xurl unlike POST_ID also works.
Repost and Undo Repost
xurl repost 1234567890
xurl unrepost 1234567890
The shorthand xurl repost POST_ID and xurl unrepost POST_ID also work.
Bookmark and Unbookmark
xurl bookmark 1234567890
xurl unbookmark 1234567890
The shorthand xurl bookmark POST_ID and xurl unbookmark POST_ID also work.
List Bookmarks and Likes
xurl bookmarks -n 20
xurl likes -n 20
The shorthand xurl bookmarks -n 10 and xurl likes -n 10 also work.
Social Graph Operations
Follow and Unfollow
xurl follow @XDevelopers
xurl unfollow @XDevelopers
The shorthand xurl follow @handle and xurl unfollow @handle also work.
Following and Followers Lists
Use --of to specify another user.
xurl following -n 50
xurl followers -n 50
# Another user's graph
xurl following --of elonmusk -n 20
xurl followers --of elonmusk -n 20
The shorthand xurl following -n 20 and xurl followers -n 20 also work.
Block and Unblock
xurl block @spammer
xurl unblock @spammer
The shorthand xurl block @handle and xurl unblock @handle also work.
Mute and Unmute
xurl mute @annoying
xurl unmute @annoying
The shorthand xurl mute @handle and xurl unmute @handle also work.
Direct Messages
xurl dm @someuser "Hey, saw your post!"
xurl dms -n 25
The shorthand xurl dm @handle "message" and xurl dms -n 10 also work.
Media Upload
Upload Media
Auto-detects type from file extension. Use --media-type and --category for explicit control.
# Auto-detect type
xurl media upload photo.jpg
xurl media upload video.mp4
# Explicit type/category
xurl media upload --media-type image/jpeg --category tweet_image photo.jpg
The shorthand xurl media upload path/to/file.mp4 also works.
Check Media Status
Videos need server-side processing. Check status with or without polling.
xurl media status MEDIA_ID
xurl media status --wait MEDIA_ID
Raw API Access
Access any X API v2 endpoint directly. Supports GET, POST, DELETE, PUT, and PATCH methods.
GET
xurl /2/users/me
POST with JSON Body
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'
DELETE, PUT, PATCH
xurl -X DELETE /2/tweets/1234567890
Custom Headers
xurl -H "Content-Type: application/json" /2/some/endpoint
Force Streaming Mode
Use -s for streaming endpoints like /2/tweets/search/stream, /2/tweets/sample/stream, or /2/tweets/sample10/stream.
xurl -s /2/tweets/search/stream
Full URLs
Full URLs also work:
xurl https://api.x.com/2/users/me
Workflows
Full Workflow: Post with Image
xurl media upload photo.jpg
xurl post "Check out this photo!" --media-id MEDIA_ID
Full Workflow: Reply to a Conversation
xurl read https://x.com/user/status/1234567890
xurl reply 1234567890 "Here are my thoughts..."
Full Workflow: Search and Engage
xurl search "topic of interest" -n 10
xurl like POST_ID_FROM_RESULTS
xurl reply POST_ID_FROM_RESULTS "Great point!"
Full Workflow: Check Your Activity
xurl whoami
xurl mentions -n 20
xurl timeline -n 20
Parameters Reference
| Parameter | Meaning | Required |
|---|---|---|
--app | Use a specific registered app (overrides default) | No |
--auth | Force auth type: oauth1, oauth2, or app | No |
--username / -u | Which OAuth2 account to use (if multiple exist) | No |
--verbose / -v | Forbidden in agent sessions (leaks auth headers) | No |
--trace / -t | Add X-B3-Flags: 1 trace header | No |
-n | Number of results (search, timeline, mentions, bookmarks, likes, following, followers, dms) | No |
--media-id | Media ID to attach to a post or reply (can be repeated) | No |
--media-type | Explicit media type (e.g., image/jpeg) for upload | No |
--category | Media category (e.g., tweet_image, amplify_video) for upload | No |
--wait | Poll media status until processing complete | No |
--of | Specify another user for following/followers list | No |
-X | HTTP method for raw API (GET, POST, DELETE, PUT, PATCH) | No |
-d | JSON body for raw API POST | No |
-H | Custom header for raw API | No |
-s | Force streaming mode for raw API endpoint | No |
Output Format
Successful responses look like this:
{ "data": { "id": "1234567890", "text": "Hello world!" } }
Error responses look like this:
{ "errors": [ { "message": "Not authorized", "code": 403 } ] }
Failure Modes and Troubleshooting
Auth Errors After Successful OAuth Flow
The token may have been saved to the default app (which has no client-id/secret) instead of the named app. Fix: run xurl auth oauth2 --app my-app then xurl auth default my-app.
unauthorized_client During OAuth
The app type is set to "Native App" in the X dashboard. Change it to "Web app, automated app or bot" in User Authentication Settings.
UsernameNotFound or 403 on /2/users/me After OAuth
X does not reliably return the username. Re-run with explicit username: xurl auth oauth2 --app my-app YOUR_USERNAME (v1.1.0+).
401 on Every Request
The token may be expired or the wrong default app is selected. Check xurl auth status and verify the ▸ points to an app with oauth2 tokens.
client-forbidden or client-not-enrolled
This is an X platform enrollment issue. Go to Dashboard, Apps, Manage, and move to the "Pay-per-use" package in the Production environment.
CreditsDepleted
Your X API account has a $0 balance. Buy credits (minimum $5) in Developer Console under Billing.
Media Processing Failed on Image Upload
The default category is amplify_video. Add --category tweet_image --media-type image/png for images.
Rate Limits
X enforces per-endpoint rate limits. A 429 response means wait and retry. Write endpoints have tighter limits than reads.
Scopes
OAuth 2.0 tokens use broad scopes. A 403 on a specific action usually means a missing scope. Have the user re-run xurl auth oauth2.
Token Refresh
OAuth 2.0 tokens auto-refresh. No action needed.
Cost
X API access is typically paid for meaningful usage. Many failures are plan or permission problems, not code problems.
Important Constraints
- Never read, print, parse, upload, or send
~/.xurlto any LLM context. - Never ask the user to paste credentials or tokens into chat.
- App credential registration and rotation must be done by the user manually, outside an agent session.
- After credentials are registered, the user authenticates with
xurl auth oauth2, also outside an agent session. - Tokens persist to
~/.xurlin YAML. Each app has isolated tokens. - OAuth 2.0 tokens auto-refresh.
POST_IDaccepts full URLs (e.g.,https://x.com/user/status/1234567890) andxurlextracts the ID.- Usernames work with or without the leading
@. - All commands return JSON to stdout. Errors also return JSON with a non-zero exit code.
- Commands needing the caller's user ID (like, repost, bookmark, follow, etc.) auto-fetch via
/2/users/me. An auth failure there surfaces as an auth error. - Never paste
~/.xurlcontents back into the conversation. - The environment variable
HERMES_HOMEis used by the Hermes tool subprocesses; ensure it is set correctly for Docker deployments. - The
xittercommand is an alias forxurlin some contexts; usexurlfor consistency. - The raw API path
/2/tweets/...is used for direct tweet operations. - The
xurl auth default APP_NAME [USERNAME]command sets the default app and optionally the default user.