curl Mastery

Essential Flags

Flag Purpose Example

-s

Silent (no progress)

curl -s …​;

-S

Show errors (with -s)

curl -sS …​;

-f

Fail silently on HTTP errors

curl -sf …​;

-L

Follow redirects

curl -sL …​;

-I

Headers only (HEAD request)

curl -sI …​;

-i

Include headers in output

curl -si …​;

-v

Verbose (debug)

curl -v …​;

-o

Output to file

curl -so file.json …​;

-O

Save with remote filename

curl -sO …​/file.zip

-w

Write-out format

curl -sw '%{http_code}' …​

HTTP Methods

# GET (default)
curl -s https://api.example.com/users

# POST
curl -s -X POST https://api.example.com/users

# PUT
curl -s -X PUT https://api.example.com/users/1

# PATCH
curl -s -X PATCH https://api.example.com/users/1

# DELETE
curl -s -X DELETE https://api.example.com/users/1

Headers

# Single header
curl -s -H "Authorization: Bearer TOKEN" https://...

# Multiple headers
curl -s \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  https://...

# Custom headers
curl -s \
  -H "X-Request-ID: $(uuidgen)" \
  -H "X-Custom-Header: value" \
  https://...

Sending Data

JSON Body

# Inline JSON
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d '{"name": "John", "email": "john@example.com"}' \
  https://api.example.com/users

# JSON from file
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d @payload.json \
  https://api.example.com/users

# JSON from stdin (heredoc)
curl -s -X POST \
  -H "Content-Type: application/json" \
  -d @- https://api.example.com/users <<'EOF'
{
  "name": "John",
  "email": "john@example.com"
}
EOF

Form Data

# URL-encoded form
curl -s -X POST \
  -d "username=john&password=secret" \
  https://api.example.com/login

# Multipart form (file upload)
curl -s -X POST \
  -F "file=@document.pdf" \
  -F "description=My file" \
  https://api.example.com/upload

Query Parameters

# In URL (must quote for special chars)
curl -s "https://api.example.com/search?q=hello&limit=10"

# With --data-urlencode (auto-encodes)
curl -s -G \
  --data-urlencode "q=hello world" \
  --data-urlencode "filter=name:john" \
  https://api.example.com/search

Authentication

# Bearer token
curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1..." https://...

# API key in header
curl -s -H "X-API-Key: your-api-key" https://...

# API key in query param
curl -s "https://api.example.com/data?api_key=your-key"

# Basic auth
curl -s -u "username:password" https://...

# Basic auth (explicit header)
curl -s -H "Authorization: Basic $(echo -n 'user:pass' | base64)" https://...

# Session cookie
curl -s -H "Cookie: session=abc123" https://...

# Cookie from file
curl -s -b cookies.txt https://...

# Save cookies to file
curl -s -c cookies.txt https://...

Response Handling

Status Code Only

# Get HTTP status code
curl -s -o /dev/null -w '%{http_code}' https://api.example.com/health

# Check if successful
if [[ $(curl -s -o /dev/null -w '%{http_code}' https://...) == "200" ]]; then
  echo "OK"
fi

Response Time

# Time breakdown
curl -s -o /dev/null -w '
    namelookup: %{time_namelookup}s
       connect: %{time_connect}s
   pretransfer: %{time_pretransfer}s
      redirect: %{time_redirect}s
 starttransfer: %{time_starttransfer}s
         total: %{time_total}s
' https://api.example.com/

# Just total time
curl -s -o /dev/null -w '%{time_total}s\n' https://...

Save Response and Headers

# Response to file, headers to stderr
curl -sD - https://... > response.json

# Headers to file, response to stdout
curl -sD headers.txt https://... | jq

# Both to files
curl -s -D headers.txt -o response.json https://...

Practical Patterns

Retry on Failure

curl -s --retry 3 --retry-delay 2 https://...

Timeout

# Connection timeout
curl -s --connect-timeout 5 https://...

# Max time for entire operation
curl -s --max-time 30 https://...

Rate Limiting (loop with delay)

for id in 1 2 3 4 5; do
  curl -s "https://api.example.com/users/$id" | jq -r '.email'
  sleep 0.5  # Be nice to the API
done

Parallel Requests

# Using xargs
echo "1 2 3 4 5" | xargs -P 5 -n 1 -I {} \
  curl -s "https://api.example.com/users/{}" -o "user_{}.json"

# Using GNU parallel
parallel -j 5 curl -s "https://api.example.com/users/{}" -o "user_{}.json" ::: 1 2 3 4 5

Error Handling

# Fail on HTTP error, capture response
response=$(curl -sf https://api.example.com/data) || {
  echo "Request failed with exit code $?"
  exit 1
}
echo "$response" | jq

curl + jq Combos

# Pretty print
curl -s https://... | jq

# Extract field
curl -s https://... | jq -r '.data.email'

# Filter array
curl -s https://... | jq '.users[] | select(.active)'

# Transform
curl -s https://... | jq '{id: .id, name: .name}'

# Count
curl -s https://... | jq '.items | length'

# First N items
curl -s https://... | jq '.items[:5]'

Pro Tips

Create curl Config File

# ~/.curlrc
silent
show-error
location
user-agent = "MyApp/1.0"

Alias for Common Patterns

# ~/.zshrc
alias curls='curl -sS'
alias curlj='curl -sS -H "Content-Type: application/json"'
alias curlv='curl -v'

Debug Mode

# See everything
curl -v --trace-ascii /dev/stderr https://...

# Just request/response
curl -v https://... 2>&1 | grep -E '^[<>]'

Exercises

  1. Fetch a URL and output only the status code

  2. POST JSON data and capture the response ID

  3. Download a file with progress bar

  4. Make 10 parallel requests and save each to a file

  5. Implement retry logic for a flaky endpoint