
XURL
FreeA CLI tool for authenticated X (Twitter) API requests.
Free · Opens the source repo
What XURL does
xurl is a command-line interface (CLI) tool designed for seamless interaction with the X (formerly Twitter) API. It facilitates authenticated requests, allowing users to perform a wide range of actions such as posting tweets, managing followers, sending direct messages, and more. The tool supports both shortcut commands for ease of use and raw curl-style commands for advanced users, ensuring flexibility in how you interact with the API. All commands return responses in JSON format, making it easy to integrate into scripts or other applications.
To use xurl, users must first authenticate their app credentials, which are stored in a YAML file. The tool supports multiple authentication methods, including OAuth 2.0 and OAuth 1.0a, allowing for secure access to the API. It's important to note that all credential management should be handled outside of agent sessions to maintain security. The skill emphasizes secret safety, providing clear guidelines on how to manage sensitive information without exposing it in logs or prompts.
This skill is particularly useful for developers and designers who need to automate interactions with the X API or integrate X functionalities into their applications. Whether you're building a bot, analyzing social media trends, or managing user interactions, xurl provides the necessary commands to streamline these processes. The ability to switch between multiple apps and manage various endpoints makes it a versatile tool for anyone working with the X platform.
When to use it
Use `xurl` when you need to automate interactions with the X API, such as posting tweets, managing followers, or sending direct messages.
When not to use it
This tool is not suitable for users who are unfamiliar with command-line interfaces or require a graphical user interface for interacting with the X API.
What you can build with it
Automating Tweet Posting
Use `xurl` to schedule and post tweets automatically from your application.
Managing Followers Programmatically
Leverage `xurl` to follow or unfollow users based on specific criteria in your bot.
Sending Direct Messages
Utilize `xurl` to send DMs to users as part of your engagement strategy.
How to install XURL
View source1. Install with the skills CLI
npx skills add xdevplatform/xurl --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by xdevplatformxurl — Agent Skill Reference
xurl is a CLI tool for the X API. It supports both shortcut commands (human/agent‑friendly one‑liners) and raw curl‑style access to any v2 endpoint. All commands return JSON to stdout.
Prerequisites
This skill requires the xurl CLI utility: https://github.com/xdevplatform/xurl.
Before using any command you must be authenticated. Run xurl auth status to check.
Secret Safety (Mandatory)
- Never read, print, parse, summarize, upload, or send anything under
~/.xurl/(or copies of it) to the LLM context. ~/.xurl/keys.ymlcontains XChat private encryption keys — the strictest no-read rule applies.- Never pass
--pininline in agent/LLM sessions (xurl chat keys restore --pin ...leaks the recovery PIN to context and shell history). Runxurl chat keys restorewithout the flag so the PIN is prompted without echo, or have the user run it manually. - Never ask the user to paste credentials/tokens into chat.
- The user must fill
~/.xurl/auth.ymlwith required secrets manually on their own machine. - Do not recommend or execute auth commands with inline secrets in agent/LLM sessions.
- Warn that using CLI secret options in agent sessions can leak credentials (prompt/context, logs, shell history).
- Never use
--verbose/-vin agent/LLM sessions; it can expose sensitive headers/tokens in output. - Never run
xurl tokenin agent/LLM sessions: it prints a live OAuth2 access token to stdout, which is a credential and must not enter the LLM context. xurl mcpis for configuring an MCP client (it bridges stdio↔HTTP and injects the bearer token); it is not something to invoke directly from an agent/LLM session.- Sensitive flags that must never be used in agent commands:
--bearer-token,--consumer-key,--consumer-secret,--access-token,--token-secret,--client-id,--client-secret. - To verify whether at least one app with credentials is already registered, run:
xurl auth status.
Register an app (recommended)
App credential registration must be done manually by the user outside the agent/LLM session. After credentials are registered, authenticate against the app that holds those credentials:
xurl auth oauth2 --app APP_NAME
You can also run xurl auth default APP_NAME first and then use xurl auth oauth2.
On a remote/headless machine (no reachable browser callback), add --headless: xurl auth oauth2 --app APP_NAME --headless prints the authorization URL and reads the pasted redirect URL (or code) back, so no localhost callback is needed.
For multiple pre-configured apps, switch between them:
xurl auth default prod-app # set default app
xurl auth default prod-app alice # set default app + user
xurl --app dev-app /2/users/me # one-off override
xurl auth apps redirect-uri get prod-app
xurl auth apps redirect-uri set prod-app http://localhost:8080/callback
Other auth methods
Examples with inline secret flags are intentionally omitted. If OAuth1 or app-only auth is needed, the user must run those commands manually outside agent/LLM context.
Tokens are persisted to ~/.xurl/auth.yml in YAML format (a legacy single-file ~/.xurl is migrated automatically). Each app has its own isolated tokens and may also store a redirect_uri. REDIRECT_URI in the environment still takes precedence over the stored app value. Do not read this file (or anything under ~/.xurl/) through the agent/LLM. Once authenticated, every command below will auto‑attach the right Authorization header.
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 |
| List a user's posts | xurl posts @handle -n 10 |
| Home timeline | xurl timeline -n 20 |
| Mentions | xurl mentions -n 10 |
| Like | xurl like POST_ID |
| Unlike | xurl unlike POST_ID |
| Repost | xurl repost POST_ID |
| Undo repost | xurl unrepost POST_ID |
| Bookmark | xurl bookmark POST_ID |
| Remove bookmark | xurl unbookmark POST_ID |
| List bookmarks | xurl bookmarks -n 10 |
| List likes | xurl likes -n 10 |
| Follow | xurl follow @handle |
| Unfollow | xurl unfollow @handle |
| List following | xurl following -n 20 |
| List followers | xurl followers -n 20 |
| Block | xurl block @handle |
| Unblock | xurl unblock @handle |
| Mute | xurl mute @handle |
| Unmute | 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 |
| Encrypted Chat (XChat) | |
| Chat key status | xurl chat keys status |
| Restore chat keys | xurl chat keys restore (PIN prompted; never pass --pin in agent sessions) |
| Import chat keys | xurl chat keys import (blob prompted; avoid passing it as an argument) |
| List chat inbox | xurl chat conversations |
| Read a conversation | xurl chat read @handle -n 50 |
| Send encrypted message | xurl chat send @handle "message" |
| Listen for new messages | xurl chat listen @handle |
| Rotate a conversation key | xurl chat rotate CONV --yes (write op — see notes) |
| Send with an attachment | xurl chat send CONV "text" --file path/to/img.png |
| Reply to a message | xurl chat send CONV "text" --reply-to SEQUENCE_ID |
| Download an attachment | xurl chat download CONV MEDIA_HASH_KEY -o out.png |
| Add group members | xurl chat add-members GROUP @user --yes (write op) |
| Mark read (explicit) | xurl chat mark-read CONV |
| Typing indicator (explicit) | xurl chat typing CONV |
| App Management | |
| Register app | Manual, outside agent (do not pass secrets via agent) |
| List apps | xurl auth apps list |
| Update app config | Manual, outside agent (do not pass secrets via agent) |
| View app redirect URI | xurl auth apps redirect-uri get [NAME] |
| Set app redirect URI | xurl auth apps redirect-uri set NAME URI |
| Remove app | xurl auth apps remove NAME |
| Set default (interactive) | xurl auth default |
| Set default (command) | xurl auth default APP_NAME [USERNAME] |
| Use app per-request | xurl --app NAME /2/users/me |
| Auth status | xurl auth status |
Post IDs vs URLs: Anywhere
POST_IDappears above you can also paste a full post URL (e.g.https://x.com/user/status/1234567890) — xurl extracts the ID automatically.
Usernames: Leading
@is optional.@elonmuskandelonmuskboth work.
Command Details
Posting
# Simple post
xurl post "Hello world!"
# Post with media (upload first, then attach)
xurl media upload photo.jpg # → note the media_id from response
xurl post "Check this out" --media-id MEDIA_ID
# Multiple media
xurl post "Thread pics" --media-id 111 --media-id 222
# Reply to a post (by ID or URL)
xurl reply 1234567890 "Great point!"
xurl reply https://x.com/user/status/1234567890 "Agreed!"
# Reply with media
xurl reply 1234567890 "Look at this" --media-id MEDIA_ID
# Quote a post
xurl quote 1234567890 "Adding my thoughts"
# Delete your own post
xurl delete 1234567890
Reading
# Read a single post (returns author, text, metrics, entities)
xurl read 1234567890
xurl read https://x.com/user/status/1234567890
# Search recent posts (default 10 results)
xurl search "golang"
xurl search "from:elonmusk" -n 20
xurl search "#buildinpublic lang:en" -n 15
User Info
# Your own profile
xurl whoami
# Look up any user
xurl user elonmusk
xurl user @XDevelopers
# List a user's recent posts (by @username)
xurl posts elonmusk
xurl posts @XDevelopers -n 25
Timelines & Mentions
# Home timeline (reverse chronological)
xurl timeline
xurl timeline -n 25
# Your mentions
xurl mentions
xurl mentions -n 20
Engagement
# Like / unlike
xurl like 1234567890
xurl unlike 1234567890
# Repost / undo
xurl repost 1234567890
xurl unrepost 1234567890
# Bookmark / remove
xurl bookmark 1234567890
xurl unbookmark 1234567890
# List your bookmarks / likes
xurl bookmarks -n 20
xurl likes -n 20
Social Graph
# Follow / unfollow
xurl follow @XDevelopers
xurl unfollow @XDevelopers
# List who you follow / your followers
xurl following -n 50
xurl followers -n 50
# List another user's following/followers
xurl following --of elonmusk -n 20
xurl followers --of elonmusk -n 20
# Block / unblock
xurl block @spammer
xurl unblock @spammer
# Mute / unmute
xurl mute @annoying
xurl unmute @annoying
Direct Messages
# Send a DM
xurl dm @someuser "Hey, saw your post!"
# List recent DM events
xurl dms
xurl dms -n 25
Encrypted Chat (XChat)
xurl chat is an end-to-end encrypted XChat client: encryption and decryption happen locally via the chat-xdk crypto library, so the server only sees ciphertext. Requires OAuth2 user auth with dm.read + dm.write scopes, and is available on macOS (Intel/Apple Silicon) and Linux amd64 — release binaries include it there; elsewhere (Windows, Linux arm64/i386) xurl chat prints a stub that says so.
Keys come from another XChat client — xurl never generates or registers encryption keys. The account must already have keys (e.g. from the X app); bring them to this machine once with restore (Juicebox PIN recovery) or import (an exported key blob). Private keys are stored in ~/.xurl/keys.yml (mode 600) — never read that file into LLM context.
A conversation is addressed by @username, a bare user id, or a conversation id (1:1 ids look like 123-456; group ids look like g123). Every command accepts -u USERNAME to act as a specific authenticated account.
# 1. Keys — one-time setup (xurl never generates/registers keys)
xurl chat keys status # local key presence/fingerprint + registered versions
xurl chat keys restore # recover from Juicebox; prompts for the PIN (no echo)
xurl chat keys import # paste an exported private-key blob (no echo)
# 2. Browse the inbox
xurl chat conversations # pretty list; decrypts group names when keys are present
xurl chat conversations --json # raw JSON
# 3. Read history (oldest first; auto-marks the conversation read)
xurl chat read @someuser
xurl chat read g1234567890 -n 50 # -n = how many events to fetch (max 100)
xurl chat read @someuser --json # decrypted events as JSON (each has id, sequence_id, content)
xurl chat read @someuser --no-mark-read # read without sending a read receipt
# 4. Send (a new 1:1 sets up its key automatically; both sides need keys)
xurl chat send @someuser "hey, encrypted!"
xurl chat send @someuser "look" --file ./photo.png # attach an encrypted file
xurl chat send @someuser "agreed" --reply-to SEQUENCE_ID # threaded reply (id from `read --json`)
# send auto-sends a typing indicator first and marks read after;
# suppress with --no-typing / --no-mark-read
# 5. Attachments — inbound messages show "📎 attachment <media_hash_key>"
xurl chat download @someuser MEDIA_HASH_KEY -o out.png # download + decrypt
# 6. Live tail (poll loop; Ctrl-C to stop; auto-marks new messages read)
xurl chat listen @someuser
xurl chat listen g1234567890 --interval 5
# 7. Read receipts / typing (also happen automatically on read/send)
xurl chat mark-read @someuser # mark read up to the newest message
xurl chat typing @someuser # send a typing indicator
# 8. Group key management (writes visible to all participants)
xurl chat add-members g123 @newuser # add a member (rotates the key; prompts, or --yes)
xurl chat rotate g123 # rotate the conversation key; prompts, or --yes
# Rotate when a key may be exposed, or to grant a member whose keys were
# registered after the last rotation access going forward. Future messages
# only — old history stays readable only to holders of the old key versions.
Notes for agents:
- Messages whose authorship signature cannot be verified are rejected by default and surface as stderr decrypt warnings; unsigned messages that still render carry a red
[unverified]marker — treat those with suspicion. - Messages with attachments render a
📎 attachment <media_hash_key>marker; pass that hash key toxurl chat download CONV <media_hash_key>to fetch and decrypt the file. Replies show a↩prefix. readandlistenmark the conversation read automatically (a read receipt visible to other participants);sendalso marks read and sends a typing indicator first. These are writes — pass--no-mark-read/--no-typingto suppress them (e.g. to read without signaling). The standalonemark-readandtypingcommands remain for scripted/explicit use.- Decrypt warnings for individual events go to stderr and are non-fatal; the rest of the conversation still renders.
- If a command reports missing keys, do not attempt to generate or register any — tell the user to run
xurl chat keys restore(orimport) themselves. chat rotateandchat add-membersare writes visible to every participant's clients; never run them without explicit user intent, and prefer letting the user confirm the prompt over passing--yes.
Media Upload
# Upload a file (auto‑detects type for images/videos)
xurl media upload photo.jpg
xurl media upload video.mp4
# Specify type and category explicitly
xurl media upload --media-type image/jpeg --category tweet_image photo.jpg
# Check processing status (videos need server‑side processing)
xurl media status MEDIA_ID
xurl media status --wait MEDIA_ID # poll until done
# Full workflow: upload then post
xurl media upload meme.png # response includes media id
xurl post "lol" --media-id MEDIA_ID
Global Flags
These flags work on every command:
| Flag | Short | Description |
|---|---|---|
--app | Use a specific registered app for this request (overrides default) | |
--auth | Force auth type: oauth1, oauth2, or app | |
--username | -u | Which OAuth2 account to use (if you have multiple) |
--verbose | -v | Forbidden in agent/LLM sessions (can leak auth headers/tokens) |
Raw API Access
The shortcut commands cover the most common operations. For anything else, use xurl's raw curl‑style mode — it works with any X API v2 endpoint:
# GET request (default)
xurl /2/users/me
# POST with JSON body
xurl -X POST /2/tweets -d '{"text":"Hello world!"}'
# PUT, PATCH, DELETE
xurl -X DELETE /2/tweets/1234567890
# Custom headers
xurl -H "Content-Type: application/json" /2/some/endpoint
# Force streaming mode
xurl -s /2/tweets/search/stream
# Full URLs also work
xurl https://api.x.com/2/users/me
Streaming
Streaming endpoints are auto‑detected. Known streaming endpoints include:
/2/tweets/search/stream/2/tweets/sample/stream/2/tweets/sample10/stream
You can force streaming on any endpoint with -s:
xurl -s /2/some/endpoint
Output Format
All commands return JSON to stdout, pretty‑printed with syntax highlighting. The output structure matches the X API v2 response format. A typical response looks like:
{
"data": {
"id": "1234567890",
"text": "Hello world!"
}
}
Errors are also returned as JSON:
{
"errors": [
{
"message": "Not authorized",
"code": 403
}
]
}
Common Workflows
Post with an image
# 1. Upload the image
xurl media upload photo.jpg
# 2. Copy the media_id from the response, then post
xurl post "Check out this photo!" --media-id MEDIA_ID
Reply to a conversation
# 1. Read the post to understand context
xurl read https://x.com/user/status/1234567890
# 2. Reply
xurl reply 1234567890 "Here are my thoughts..."
Search and engage
# 1. Search for relevant posts
xurl search "topic of interest" -n 10
# 2. Like an interesting one
xurl like POST_ID_FROM_RESULTS
# 3. Reply to it
xurl reply POST_ID_FROM_RESULTS "Great point!"
Check your activity
# See who you are
xurl whoami
# Check your mentions
xurl mentions -n 20
# Check your timeline
xurl timeline -n 20
Set up multiple apps
# App credentials must already be configured manually outside agent/LLM context.
# Authenticate users on each pre-configured app
xurl auth default prod
xurl auth oauth2 # authenticates on prod app
xurl auth default staging
xurl auth oauth2 # authenticates on staging app
# Switch between them
xurl auth default prod alice # prod app, alice user
xurl --app staging /2/users/me # one-off request against staging
Error Handling
- Non‑zero exit code on any error.
- API errors are printed as JSON to stdout (so you can still parse them).
- Auth errors suggest re‑running
xurl auth oauth2or checking your tokens. - If a command requires your user ID (like, repost, bookmark, follow, etc.), xurl will automatically fetch it via
/2/users/me. When that endpoint is unreliable, use--username USERNAMEor authenticate withxurl auth oauth2 --app APP_NAME USERNAMEso xurl can fall back to username lookup. - If X returns
client-forbidden/client-not-enrolledafter successful auth, check the app’s X developer-console package and environment. In current testing, moving the app toPay-per-useandProductionfixed/2/*read failures without changing localxurlauth data.
Notes
- Rate limits: The X API enforces rate limits per endpoint. If you get a 429 error, wait and retry. Write endpoints (post, reply, like, repost) have stricter limits than read endpoints.
- Scopes: OAuth 2.0 tokens are requested with broad scopes. If you get a 403 on a specific action, your token may lack the required scope — re‑run
xurl auth oauth2to get a fresh token. - Token refresh: OAuth 2.0 tokens auto‑refresh when expired. No manual intervention needed.
- Multiple apps: Each app has its own isolated credentials, tokens, and optional stored
redirect_uri. Configure credentials manually outside agent/LLM context, then switch withxurl auth defaultor--app. - Redirect URI precedence: The effective redirect URI resolves from
REDIRECT_URIin the environment first, then the app's storedredirect_uriin~/.xurl/auth.yml, then the built-in default. - Redirect URI management: Use
xurl auth apps redirect-uri get [NAME],xurl auth apps redirect-uri set NAME URI, orxurl auth apps update NAME --redirect-uri URIto inspect and manage the stored per-app callback value. - X platform enrollment: A successful OAuth callback does not guarantee
/2/*reads will work. If you seeclient-not-enrolled, verify the app is in the correct X package/environment. Current confirmed fix:Apps->Manage apps->Move to package-> choosePay-per-use, then move the app toProduction. - Multiple accounts: You can authenticate multiple OAuth 2.0 accounts per app and switch between them with
--username/-uor set a default withxurl auth default APP USER. - Default user: When no
-uflag is given, xurl uses the default user for the active app (set viaxurl auth default). If no default user is set, it uses the first available token. - Token storage:
~/.xurlis a directory;~/.xurl/auth.ymlholds each app's credentials and tokens. Never read or send anything under~/.xurl/to LLM context. - Chat key storage:
~/.xurl/keys.ymlholds XChat private encryption keys per user (mode 600). Losing it means losing the ability to decrypt on this machine (recoverable viaxurl chat keys restoreif a Juicebox PIN backup exists). Never read or send this file to LLM context. - Chat key registration: xurl performs none — no public-key registration and no Juicebox writes. Only keys already registered by another XChat client can be restored or imported; unregistered keys are rejected.
- Access tokens:
xurl tokenprints a valid (refreshed) OAuth2 access token for the active app to stdout, refreshing and persisting it if expired. It never opens a browser. The output is a secret — use it only in the user's own scripts, never in agent/LLM sessions. - MCP bridge:
xurl mcp [URL]bridges a stdio MCP client to a remote Streamable HTTP MCP server (defaulthttps://api.x.com/mcp), injectingAuthorization: Bearer <token>and refreshing the token automatically. On first run with no cached token it opens the browser for a one-time OAuth2 login using theCLIENT_ID/CLIENT_SECRETfrom its environment (the handshake waits for it, so set a generousstartup_timeout_sec); on a headless host, authenticate out-of-band first withxurl auth oauth2 --headless. Configure it in an MCP client via the npm launcher:{"command":"npx","args":["-y","@xdevplatform/xurl","mcp","https://api.x.com/mcp"],"env":{"CLIENT_ID":"...","CLIENT_SECRET":"..."},"startup_timeout_sec":300}.
Frequently asked questions about XURL
Similar skills
WinMD API Search
Easily find and explore Windows desktop APIs.
WebMCPify
Transform any web app into an agent-ready platform.
Phoenix Tracing
Instrument LLM applications with OpenInference tracing.
Foundry Hosted Agent CopilotKit
Guidance for developing agentic web apps on Azure.
Power Automate Foundation
Connect AI agents to Power Automate seamlessly.
Power Automate Flow Builder
Efficiently build and deploy Power Automate flows programmatically.
