Skip to content

xh - Modern HTTP Client Cheatsheet

Installation

Platform Command
Ubuntu/Debian curl -sfL https://raw.githubusercontent.com/ducaale/xh/master/install.sh \| sh
Arch Linux pacman -S xh
Fedora/RHEL dnf install xh
macOS (Homebrew) brew install xh
macOS (MacPorts) sudo port install xh
Windows (Scoop) scoop install xh
Windows (Chocolatey) choco install xh
Cargo (All platforms) cargo install xh
Docker docker pull ducaale/xh
Snap snap install xh

Basic Commands

Command Description
xh httpbin.org/get Simple GET request
xh POST httpbin.org/post name=John POST request with JSON data
xh PUT httpbin.org/put status=active PUT request with data
xh PATCH httpbin.org/patch email=new@email.com PATCH request to update resource
xh DELETE httpbin.org/delete DELETE request
xh HEAD httpbin.org/get HEAD request (headers only)
xh OPTIONS httpbin.org/get OPTIONS request
xh httpbin.org/get search==rust GET with query parameter
xh httpbin.org/get page==1 limit==10 Multiple query parameters
xh -a user:pass httpbin.org/basic-auth/user/pass Basic authentication
xh -A bearer -a token123 api.example.com Bearer token authentication
xh httpbin.org/get User-Agent:CustomAgent Custom header
xh -b httpbin.org/json Print only response body
xh -h httpbin.org/json Print only response headers
xh -v httpbin.org/json Verbose output (headers + body)
xh httpbin.org/json -o response.json Download response to file
xh --follow=0 httpbin.org/redirect/3 Don't follow redirects
xh -f POST httpbin.org/post name=John POST with form data
xh POST httpbin.org/post < data.json POST with file content
xh httpbin.org/get Authorization:"Bearer token" Authorization header

Advanced Usage

Command Description
xh POST httpbin.org/post age:=30 active:=true JSON with type coercion (number/boolean)
xh POST httpbin.org/post tags:='["rust","cli"]' Send JSON array
xh POST httpbin.org/post user[name]=John user[age]:=30 Nested JSON objects
xh --session=mysession httpbin.org/cookies/set/token/abc Create named session (persist cookies)
xh --session=mysession httpbin.org/cookies Reuse session
xh --session-read-only=mysession httpbin.org/get Use session without updating it
xh -f POST httpbin.org/post file@/path/to/file.txt Upload file as multipart form
xh POST httpbin.org/post @file.json Upload file as request body
xh --proxy=http:http://proxy.example.com:8080 httpbin.org/get Use HTTP proxy
xh --proxy=all:socks5://localhost:1080 httpbin.org/get Use SOCKS5 proxy
xh --verify=no https://self-signed.badssl.com/ Skip SSL verification (insecure)
xh --cert=/path/to/ca-cert.pem https://api.example.com Custom CA certificate
xh --cert=/path/to/client.crt --cert-key=/path/to/client.key https://api.example.com Client certificate authentication
xh --timeout=30 httpbin.org/delay/5 Set request timeout (seconds)
xh --pretty=none httpbin.org/json Disable colored/formatted output
xh --style=monokai httpbin.org/json Use specific color scheme
xh --stream httpbin.org/stream/100 Stream response
xh --download httpbin.org/image/png -o image.png Download with progress bar
xh --continue httpbin.org/stream/100 -o stream.txt Resume partial download
xh --http-version=1.1 https://httpbin.org/get Force HTTP/1.1
xh --method=CUSTOM httpbin.org/anything Custom HTTP method
xh --multipart POST httpbin.org/post name=John file@doc.pdf Multipart form with mixed data
xh httpbin.org/get Cookie:session=abc123 Send cookies
xh --auth-type=digest -a user:pass httpbin.org/digest-auth/auth/user/pass Digest authentication

JSON Data Types

When sending JSON data, xh supports type coercion with special syntax:

Syntax Type Example
key=value String name=John{"name":"John"}
key:=number Number age:=30{"age":30}
key:=true Boolean active:=true{"active":true}
key:=null Null middle:=null{"middle":null}
key:='[...]' Array tags:='["a","b"]'{"tags":["a","b"]}
key:='{...}' Object meta:='{"v":1}'{"meta":{"v":1}}
key[sub]=value Nested user[name]=John{"user":{"name":"John"}}

Configuration

Session Storage Locations

# Linux/macOS
~/.config/xh/sessions/

# Windows
%APPDATA%\xh\sessions\

Environment Variables

# Set default proxy
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080

# Disable proxy for specific hosts
export NO_PROXY=localhost,127.0.0.1,.example.com

# Default timeout
export XH_TIMEOUT=30

Common Aliases

# Add to ~/.bashrc or ~/.zshrc

# HTTPie compatibility
alias http='xh'
alias https='xh --default-scheme=https'

# Quick shortcuts
alias xhj='xh --pretty=all'  # Force JSON pretty-print
alias xhn='xh --pretty=none' # No formatting
alias xhv='xh -v'            # Verbose by default
alias xhd='xh --download'    # Download mode

Common Use Cases

Use Case: Testing REST API with Authentication

# Login and save session
xh --session=api POST https://api.example.com/login \
  username=admin password=secret

# Make authenticated requests using saved session
xh --session=api GET https://api.example.com/users

# Update resource
xh --session=api PUT https://api.example.com/users/123 \
  name="John Doe" email=john@example.com status=active

# Delete resource
xh --session=api DELETE https://api.example.com/users/123

Use Case: File Upload with Metadata

# Upload file with additional form fields
xh -f POST https://api.example.com/upload \
  file@/path/to/document.pdf \
  title="Important Document" \
  category=legal \
  tags:='["contract","2024"]'

# Multiple file upload
xh -f POST https://api.example.com/batch-upload \
  file1@image1.jpg \
  file2@image2.jpg \
  album="Vacation 2024"

Use Case: API Testing with Different Environments

# Development environment
xh --session=dev POST https://dev-api.example.com/test \
  Authorization:"Bearer dev-token-123" \
  data=test

# Staging environment
xh --session=staging POST https://staging-api.example.com/test \
  Authorization:"Bearer staging-token-456" \
  data=test

# Production environment (with SSL verification)
xh --session=prod POST https://api.example.com/test \
  Authorization:"Bearer prod-token-789" \
  data=test

Use Case: Debugging Webhook Payloads

# Capture webhook payload to file
xh POST https://webhook.site/your-unique-url \
  event=user.created \
  user[id]:=12345 \
  user[email]=new@example.com \
  timestamp:=$(date +%s) \
  -v -o webhook-debug.txt

# Test webhook with retry logic (in script)
for i in {1..3}; do
  xh POST https://your-app.com/webhook \
    event=test \
    attempt:=$i && break || sleep 5
done

Use Case: Downloading and Processing API Data

# Download JSON data
xh GET https://api.github.com/users/octocat/repos \
  -o repos.json

# Download with authentication and save
xh -a token:ghp_your_token \
  GET https://api.github.com/user/repos \
  visibility==private \
  -o private-repos.json

# Stream large dataset
xh --stream GET https://api.example.com/export/data \
  format==json \
  | jq '.[] | select(.active == true)' \
  > active-records.json

Best Practices

  • Use sessions for authenticated workflows: Sessions persist cookies and headers, eliminating the need to re-authenticate for each request. Use --session=name for multi-step API interactions.

  • Leverage type coercion for JSON: Use := for numbers and booleans (age:=30, active:=true) instead of string values to ensure correct JSON types in API requests.

  • Prefer xh over curl for API testing: The intuitive syntax (xh POST api.com name=value) is more readable than curl's verbose flags, making it ideal for documentation and quick testing.

  • Use verbose mode for debugging: Add -v to see full request and response headers when troubleshooting API issues or authentication problems.

  • Store sensitive tokens in environment variables: Instead of xh -a token:secret_key, use xh -a token:$API_TOKEN to avoid exposing credentials in shell history.

  • Utilize output redirection for automation: Save responses with -o file.json for processing or comparison in CI/CD pipelines and automated testing scripts.

  • Set appropriate timeouts: Use --timeout=30 for slow APIs to prevent hanging requests. Set --timeout=0 only for streaming or long-running operations.

  • Organize sessions by environment: Create separate sessions for dev, staging, and production (--session=prod-api) to avoid accidentally using wrong credentials.

Troubleshooting

Issue Solution
SSL certificate verification failed Use --verify=no to skip verification (insecure) or --cert=/path/to/ca.pem to provide custom CA certificate
Connection timeout Increase timeout with --timeout=60 or check network connectivity and proxy settings
401 Unauthorized Verify authentication method: use -a user:pass for basic auth, -A bearer -a token for bearer tokens, or check session validity
JSON parsing error Ensure proper syntax: use := for non-strings (age:=30), quote arrays (tags:='["a","b"]'), and check for unescaped special characters
Command not found: xh Verify installation path is in $PATH, reinstall using preferred method, or use full path /usr/local/bin/xh
Session not persisting Check session directory permissions (~/.config/xh/sessions/), ensure using same session name, verify not using --session-read-only
Proxy connection failed Verify proxy URL format --proxy=http:http://host:port, check proxy authentication if required, ensure proxy allows target host
File upload fails Use -f or --multipart flag for form uploads, verify file path is correct, check file permissions and size limits
Response not formatted/colored Force formatting with --pretty=all, check terminal supports colors, or pipe through jq for JSON formatting
HTTP/2 not working Verify server supports HTTP/2, check that OpenSSL/TLS is properly configured, try forcing HTTP/1.1 with --http-version=1.1 to compare