effort: high name: xurl description: | Interact with X (Twitter) via xurl, the official X API CLI: post, reply, quote, delete, search, read timelines/mentions, like, repost, bookmark, follow, DM, upload media, raw v2 endpoints. Triggers on "post to X", "tweet this", "reply to [X post]", "search X for", any x.com/status URL, or "xurl". Do NOT trigger on bare "X" meaning the letter, a variable, or an unknown. user-invocable: true allowed-tools: Bash(xurl:*)
xurl — X (Twitter) API via the Official CLI
xurl is the X developer platform's official CLI for the X API. It has 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
Secret Safety (MANDATORY)
Critical rules when operating inside an agent/LLM session:
- Never read, print, parse, summarize, upload, or send
~/.xurlto LLM context. - Never ask the user to paste credentials/tokens into chat.
- The user fills
~/.xurlwith secrets manually on their own machine. - Never recommend or execute auth commands with inline secrets in agent sessions.
- Never use
--verbose/-vin 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 rotation must be done by the user manually, outside the agent session. Tokens persist to ~/.xurl in YAML. Each app has isolated tokens. OAuth 2.0 tokens auto-refresh.
Installation
Installed via Homebrew on this machine:
brew install --cask xdevplatform/tap/xurl
Other options: curl -fsSL https://raw.githubusercontent.com/xdevplatform/xurl/main/install.sh | bash, npm install -g @xdevplatform/xurl, or go install github.com/xdevplatform/xurl@latest.
Verify:
xurl --help
xurl auth status
One-Time User Setup (user runs these outside the agent)
These steps involve pasting secrets and must be done by the user directly. Direct the user to this block; do not execute it for them.
- Open an app at https://developer.x.com/en/portal/dashboard (redirect URI
http://localhost:8080/callbackif using OAuth 2.0). - Register the app locally (user runs this):
xurl auth apps add my-app --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET - Authenticate with one of:
xurl auth oauth2— browser PKCE flow (recommended; auto-refreshing tokens)xurl auth app --bearer-token ...— app-only readsxurl auth oauth1 --consumer-key ... --consumer-secret ... --access-token ... --token-secret ...— user-context v1.1
- Verify:
xurl auth statusandxurl whoami.
Quick Reference
| Action | Command |
|---|---|
| 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_IDaccepts 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
Reading X long-form articles. When a post contains a long-form X article (the body is just a t.co link to x.com/i/article/...), xurl read returns only the title and a 500-status link — the article body isn't in the default response. Use raw mode and request the article tweet field:
xurl "/2/tweets/POST_ID?tweet.fields=article"
The response includes data.article with:
title— article headlinepreview_text— short snippetplain_text— full article body (this is whatreadis missing)cover_media,media_entities— embedded image media keys; resolve via?expansions=attachments.media_keys&media.fields=url
Not gated — don't mistake the thin response for a permissions block. The
status: 500on the t.co link and the title-onlyxurl readoutput look like the article is gated/blocked, but they are not. The body is simply absent from the default tweet fields; requestingtweet.fields=articlereturnsplain_textin full with the same auth (no extra scope, no paid tier). Ifxurl readgives you only a title for anx.com/i/article/...link, always retry with the raw?tweet.fields=articlecall before concluding it can't be read.
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
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
For anything not covered by shortcuts, use raw curl-style mode against any X API v2 endpoint:
xurl /2/users/me
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'
xurl -X DELETE /2/tweets/1234567890
xurl -H "Content-Type: application/json" /2/some/endpoint
xurl -s /2/tweets/search/stream
xurl https://api.x.com/2/users/me
Global Flags
| Flag | Short | Description |
|---|---|---|
--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 } ] }
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(or the relevant auth subcommand) 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
- Verify prerequisites:
xurl --helpandxurl auth status. - If auth is missing, stop and direct the user to the "One-Time User Setup" section — do NOT attempt to register apps or pass secrets yourself.
- Start with a cheap read (
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).
- Use JSON output directly — every response is already structured.
- Never paste
~/.xurlcontents back into the conversation.
Notes
- Rate limits: X enforces per-endpoint rate limits. A 429 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 the token is missing a scope — have the user re-run
xurl auth oauth2. - Token refresh: OAuth 2.0 tokens auto-refresh.
- Multiple apps: Each app has isolated credentials/tokens. Switch with
xurl auth defaultor--app. - Multiple accounts per app: Select with
-u / --username, or set a default withxurl auth default APP USER. - Token storage:
~/.xurlis 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
- Upstream CLI: https://github.com/xdevplatform/xurl (X developer platform)
- Upstream agent skill: https://github.com/NousResearch/hermes-agent/blob/main/skills/social-media/xurl/SKILL.md (Hermes Agent / openclaw)
- Adapted for this dotfiles repo: frontmatter swapped to local conventions; safety guardrails preserved.