Hermes-agent xurl

Interact with X/Twitter via xurl, the official X API CLI. Use for posting, replying, quoting, searching, timelines, mentions, likes, reposts, bookmarks, follows, DMs, media upload, and raw v2 endpoint access.

install
source · Clone the upstream repo
git clone https://github.com/NousResearch/hermes-agent
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NousResearch/hermes-agent "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/social-media/xurl" ~/.claude/skills/nousresearch-hermes-agent-xurl-a9c001 && rm -rf "$T"
manifest: skills/social-media/xurl/SKILL.md
source content

xurl — X (Twitter) API via the Official CLI

xurl
is the X developer platform's official CLI for the X API. It supports shortcut commands for common actions AND raw curl-style access to any v2 endpoint. All commands return JSON to stdout.

Use this skill for:

  • posting, replying, quoting, deleting posts
  • searching posts and reading timelines/mentions
  • liking, reposting, bookmarking
  • following, unfollowing, blocking, muting
  • direct messages
  • media uploads (images and video)
  • raw access to any X API v2 endpoint
  • multi-app / multi-account workflows

This skill replaces the older

xitter
skill (which wrapped a third-party Python CLI).
xurl
is maintained by the X developer platform team, supports OAuth 2.0 PKCE with auto-refresh, and covers a substantially larger API surface.


Secret Safety (MANDATORY)

Critical rules when operating inside an agent/LLM session:

  • Never read, print, parse, summarize, upload, or send
    ~/.xurl
    to LLM context.
  • Never ask the user to paste credentials/tokens into chat.
  • The user must fill
    ~/.xurl
    with secrets manually on their own machine.
  • Never recommend or execute auth commands with inline secrets in agent sessions.
  • Never use
    --verbose
    /
    -v
    in agent sessions — it can expose auth headers/tokens.
  • To verify credentials exist, only use:
    xurl auth status
    .

Forbidden flags in agent commands (they accept inline secrets):

--bearer-token
,
--consumer-key
,
--consumer-secret
,
--access-token
,
--token-secret
,
--client-id
,
--client-secret

App credential registration and credential rotation must be done by the user manually, outside the agent session. After credentials are registered, the user authenticates with

xurl auth oauth2
— also outside the agent session. Tokens persist to
~/.xurl
in YAML. Each app has isolated tokens. OAuth 2.0 tokens auto-refresh.


Installation

Pick ONE method. On Linux, the shell script or

go install
are the easiest.

# 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

Verify:

xurl --help
xurl auth status

If

xurl
is installed but
auth status
shows no apps or tokens, the user needs to complete auth manually — see the next section.


One-Time User Setup (user runs these outside the agent)

These steps must be performed by the user directly, NOT by the agent, because they involve pasting secrets. Direct the user to this block; do not execute it for them.

  1. Create or open an app at https://developer.x.com/en/portal/dashboard
  2. Set the redirect URI to
    http://localhost:8080/callback
  3. Copy the app's Client ID and Client Secret
  4. Register the app locally (user runs this):
    xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET
    
  5. Authenticate (specify
    --app
    to bind the token to your app):
    xurl auth oauth2 --app my-app
    
    (This opens a browser for the OAuth 2.0 PKCE flow.)
  6. Set the app as default so all commands use it:
    xurl auth default my-app
    
  7. Verify:
    xurl auth status
    xurl whoami
    

After this, the agent can use any command below without further setup. OAuth 2.0 tokens auto-refresh.

Common pitfall: If you omit

--app my-app
from
xurl auth oauth2
, the OAuth token is saved to the built-in
default
app profile — which has no client-id or client-secret. Commands will fail with auth errors even though the OAuth flow appeared to succeed. If you hit this, re-run
xurl auth oauth2 --app my-app
and
xurl auth default my-app
.


Quick Reference

ActionCommand
Post
xurl post "Hello world!"
Reply
xurl reply POST_ID "Nice post!"
Quote
xurl quote POST_ID "My take"
Delete a post
xurl delete POST_ID
Read a post
xurl read POST_ID
Search posts
xurl search "QUERY" -n 10
Who am I
xurl whoami
Look up a user
xurl user @handle
Home timeline
xurl timeline -n 20
Mentions
xurl mentions -n 10
Like / Unlike
xurl like POST_ID
/
xurl unlike POST_ID
Repost / Undo
xurl repost POST_ID
/
xurl unrepost POST_ID
Bookmark / Remove
xurl bookmark POST_ID
/
xurl unbookmark POST_ID
List bookmarks / likes
xurl bookmarks -n 10
/
xurl likes -n 10
Follow / Unfollow
xurl follow @handle
/
xurl unfollow @handle
Following / Followers
xurl following -n 20
/
xurl followers -n 20
Block / Unblock
xurl block @handle
/
xurl unblock @handle
Mute / Unmute
xurl mute @handle
/
xurl unmute @handle
Send DM
xurl dm @handle "message"
List DMs
xurl dms -n 10
Upload media
xurl media upload path/to/file.mp4
Media status
xurl media status MEDIA_ID
List apps
xurl auth apps list
Remove app
xurl auth apps remove NAME
Set default app
xurl auth default APP_NAME [USERNAME]
Per-request app
xurl --app NAME /2/users/me
Auth status
xurl auth status

Notes:

  • POST_ID
    accepts full URLs too (e.g.
    https://x.com/user/status/1234567890
    ) — xurl extracts the ID.
  • Usernames work with or without a leading
    @
    .

Command Details

Posting

xurl post "Hello world!"
xurl post "Check this out" --media-id MEDIA_ID
xurl post "Thread pics" --media-id 111 --media-id 222

xurl reply 1234567890 "Great point!"
xurl reply https://x.com/user/status/1234567890 "Agreed!"
xurl reply 1234567890 "Look at this" --media-id MEDIA_ID

xurl quote 1234567890 "Adding my thoughts"
xurl delete 1234567890

Reading & Search

xurl read 1234567890
xurl read https://x.com/user/status/1234567890

xurl search "golang"
xurl search "from:elonmusk" -n 20
xurl search "#buildinpublic lang:en" -n 15

Users, Timeline, Mentions

xurl whoami
xurl user elonmusk
xurl user @XDevelopers

xurl timeline -n 25
xurl mentions -n 20

Engagement

xurl like 1234567890
xurl unlike 1234567890

xurl repost 1234567890
xurl unrepost 1234567890

xurl bookmark 1234567890
xurl unbookmark 1234567890

xurl bookmarks -n 20
xurl likes -n 20

Social Graph

xurl follow @XDevelopers
xurl unfollow @XDevelopers

xurl following -n 50
xurl followers -n 50

# Another user's graph
xurl following --of elonmusk -n 20
xurl followers --of elonmusk -n 20

xurl block @spammer
xurl unblock @spammer
xurl mute @annoying
xurl unmute @annoying

Direct Messages

xurl dm @someuser "Hey, saw your post!"
xurl dms -n 25

Media Upload

# 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

# Videos need server-side processing — check status (or poll)
xurl media status MEDIA_ID
xurl media status --wait MEDIA_ID

# Full workflow
xurl media upload meme.png                  # returns media id
xurl post "lol" --media-id MEDIA_ID

Raw API Access

The shortcuts cover common operations. For anything else, use raw curl-style mode against any X API v2 endpoint:

# 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
xurl -s /2/tweets/search/stream

# Full URLs also work
xurl https://api.x.com/2/users/me

Global Flags

FlagShortDescription
--app
Use a specific registered app (overrides default)
--auth
Force auth type:
oauth1
,
oauth2
, or
app
--username
-u
Which OAuth2 account to use (if multiple exist)
--verbose
-v
Forbidden in agent sessions — leaks auth headers
--trace
-t
Add
X-B3-Flags: 1
trace header

Streaming

Streaming endpoints are auto-detected. Known ones include:

  • /2/tweets/search/stream
  • /2/tweets/sample/stream
  • /2/tweets/sample10/stream

Force streaming on any endpoint with

-s
.


Output Format

All commands return JSON to stdout. Structure mirrors X API v2:

{ "data": { "id": "1234567890", "text": "Hello world!" } }

Errors are also JSON:

{ "errors": [ { "message": "Not authorized", "code": 403 } ] }

Common Workflows

Post with an image

xurl media upload photo.jpg
xurl post "Check out this photo!" --media-id MEDIA_ID

Reply to a conversation

xurl read https://x.com/user/status/1234567890
xurl reply 1234567890 "Here are my thoughts..."

Search and engage

xurl search "topic of interest" -n 10
xurl like POST_ID_FROM_RESULTS
xurl reply POST_ID_FROM_RESULTS "Great point!"

Check your activity

xurl whoami
xurl mentions -n 20
xurl timeline -n 20

Multiple apps (credentials pre-configured manually)

xurl auth default prod alice               # prod app, alice user
xurl --app staging /2/users/me             # one-off against staging

Error Handling

  • Non-zero exit code on any error.
  • API errors are still printed as JSON to stdout, so you can parse them.
  • Auth errors → have the user re-run
    xurl auth oauth2
    outside the agent session.
  • Commands that need the caller's user ID (like, repost, bookmark, follow, etc.) will auto-fetch it via
    /2/users/me
    . An auth failure there surfaces as an auth error.

Agent Workflow

  1. Verify prerequisites:
    xurl --help
    and
    xurl auth status
    .
  2. Check default app has credentials. Parse the
    auth status
    output. The default app is marked with
    . If the default app shows
    oauth2: (none)
    but another app has a valid oauth2 user, tell the user to run
    xurl auth default <that-app>
    to fix it. This is the most common setup mistake — the user added an app with a custom name but never set it as default, so xurl keeps trying the empty
    default
    profile.
  3. If auth is missing entirely, stop and direct the user to the "One-Time User Setup" section — do NOT attempt to register apps or pass secrets yourself.
  4. Start with a cheap read (
    xurl whoami
    ,
    xurl user @handle
    ,
    xurl search ... -n 3
    ) to confirm reachability.
  5. Confirm the target post/user and the user's intent before any write action (post, reply, like, repost, DM, follow, block, delete).
  6. Use JSON output directly — every response is already structured.
  7. Never paste
    ~/.xurl
    contents back into the conversation.

Troubleshooting

SymptomCauseFix
Auth errors after successful OAuth flowToken saved to
default
app (no client-id/secret) instead of your named app
xurl auth oauth2 --app my-app
then
xurl auth default my-app
unauthorized_client
during OAuth
App type set to "Native App" in X dashboardChange to "Web app, automated app or bot" in User Authentication Settings
401 on every requestToken expired or wrong default appCheck
xurl auth status
— verify
points to an app with oauth2 tokens
client-forbidden
/
client-not-enrolled
X platform enrollment issueDashboard → Apps → Manage → Move to "Pay-per-use" package → Production environment
CreditsDepleted
$0 balance on X APIBuy credits (min $5) in Developer Console → Billing
media processing failed
on image upload
Default category is
amplify_video
Add
--category tweet_image --media-type image/png
Two "Client Secret" values in X dashboardUI bug — first is actually Client IDConfirm on the "Keys and tokens" page; ID ends in
MTpjaQ

Notes

  • Rate limits: X enforces per-endpoint rate limits. A 429 means wait and retry. Write endpoints (post, reply, like, repost) have tighter limits than reads.
  • Scopes: OAuth 2.0 tokens use broad scopes. A 403 on a specific action usually means the token is missing a scope — have the user re-run
    xurl auth oauth2
    .
  • Token refresh: OAuth 2.0 tokens auto-refresh. Nothing to do.
  • Multiple apps: Each app has isolated credentials/tokens. Switch with
    xurl auth default
    or
    --app
    .
  • Multiple accounts per app: Select with
    -u / --username
    , or set a default with
    xurl auth default APP USER
    .
  • Token storage:
    ~/.xurl
    is YAML. Never read or send this file to LLM context.
  • Cost: X API access is typically paid for meaningful usage. Many failures are plan/permission problems, not code problems.

Attribution