skip to content

curl β€” HTTP Client

Transfer data with URLs. Covers HTTP methods, headers, authentication, forms, TLS, cookies, proxies, timeouts, parallel downloads, and a comprehensive recipe collection.

4 min read 14 snippets 2d ago intermediate

curl β€” HTTP Client#

Basic requests#

curl https://example.com                   # GET, print to stdout
curl -o file.html https://example.com      # save to file
curl -O https://example.com/file.tar.gz   # save with remote filename
curl -L https://example.com               # follow redirects
curl -s https://example.com               # silent (no progress)
curl -S https://example.com               # show error even with -s
curl -sS https://example.com | jq '.'     # common: silent + error visible
curl -v https://example.com               # verbose (headers + body)
curl -I https://example.com               # HEAD only (headers)
curl --head https://example.com           # same as -I

HTTP methods#

curl -X GET    https://api.example.com/users
curl -X POST   https://api.example.com/users
curl -X PUT    https://api.example.com/users/1
curl -X PATCH  https://api.example.com/users/1
curl -X DELETE https://api.example.com/users/1

Request headers#

curl -H "Accept: application/json"    https://api.example.com
curl -H "Content-Type: application/json" \
     -H "Authorization: Bearer TOKEN"  https://api.example.com

# Multiple headers
curl -H "X-Request-ID: abc123" \
     -H "X-Client-Version: 2.0" \
     https://api.example.com

Request body#

# JSON body (POST)
curl -X POST https://api.example.com/users \
     -H "Content-Type: application/json" \
     -d '{"name":"Alice","email":"alice@example.com"}'

# From a file
curl -X POST https://api.example.com/upload \
     -H "Content-Type: application/json" \
     -d @payload.json

# Form data (application/x-www-form-urlencoded)
curl -X POST https://example.com/login \
     -d "username=alice&password=secret"

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

# Binary stdin
cat image.png | curl -X POST https://api.example.com/image \
     -H "Content-Type: image/png" \
     --data-binary @-

Authentication#

# Basic auth
curl -u user:password https://example.com
curl -u user https://example.com          # prompt for password

# Bearer token
curl -H "Authorization: Bearer TOKEN" https://api.example.com

# API key in header
curl -H "X-API-Key: my-key" https://api.example.com

# API key in query string
curl "https://api.example.com/data?api_key=my-key"

# Digest auth
curl --digest -u user:pass https://example.com

# Client certificate
curl --cert client.pem --key client.key https://api.example.com

TLS / SSL#

curl -k https://self-signed.example.com           # skip cert verification
curl --insecure https://dev.local                 # same as -k

curl --cacert /path/to/ca.pem https://internal.example.com
curl --capath /etc/ssl/certs https://example.com

# Show certificate info
curl -vI https://example.com 2>&1 | grep -A20 "Server certificate"

# Specify TLS version
curl --tlsv1.2 https://example.com
curl --tls-max 1.3 https://example.com

Output and response info#

curl -w "\nHTTP %{http_code} %{time_total}s\n" -o /dev/null -s https://example.com

# Full timing breakdown
curl -w "\n
    DNS:        %{time_namelookup}s
    Connect:    %{time_connect}s
    TLS:        %{time_appconnect}s
    TTFB:       %{time_starttransfer}s
    Total:      %{time_total}s
    HTTP:       %{http_code}\n" -o /dev/null -sS https://example.com

# Get only the HTTP status code
curl -o /dev/null -s -w "%{http_code}" https://example.com

# Get response headers into a variable
HEADERS=$(curl -sI https://example.com)

Cookies#

curl -c cookies.txt https://example.com/login -d "user=alice&pass=x"
curl -b cookies.txt https://example.com/dashboard
curl -c cookies.txt -b cookies.txt https://example.com  # send + save

# Send a specific cookie
curl -H "Cookie: session=abc123; pref=dark" https://example.com

Redirects#

curl -L https://short.url/abc                # follow redirects
curl --max-redirs 5 -L https://example.com  # max 5 redirects
curl -D - https://example.com              # dump headers to stdout

Proxy#

curl -x http://proxy:8080 https://example.com
curl --proxy socks5://proxy:1080 https://example.com
curl --noproxy localhost,192.168.0.0/16 https://example.com
export http_proxy=http://proxy:8080         # environment variable

Timeouts and retries#

curl --connect-timeout 5 https://example.com      # connection timeout (s)
curl --max-time 30 https://example.com            # total operation timeout
curl --retry 3 https://example.com                # retry on transient error
curl --retry 3 --retry-delay 2 https://example.com
curl --retry 3 --retry-all-errors https://example.com

Download management#

# Resume interrupted download
curl -C - -O https://example.com/bigfile.tar.gz

# Speed limit
curl --limit-rate 500k -O https://example.com/file.tar.gz

# Progress bar
curl -# -O https://example.com/file.tar.gz

# Parallel downloads (curl 7.66+)
curl --parallel --parallel-max 5 -O -O -O \
     https://example.com/file1.tar.gz \
     https://example.com/file2.tar.gz \
     https://example.com/file3.tar.gz

Config file ~/.curlrc#

# Always follow redirects
--location

# Silent by default
--silent
--show-error

# Default headers
--header "Accept: application/json"

# Timeout
--max-time 30
--connect-timeout 10

# Retry
--retry 3

Practical API recipes#

# Health check
check() { curl -fsS "$1" > /dev/null && echo "UP" || echo "DOWN"; }
check https://api.example.com/health

# Paginate GitHub API
for page in $(seq 1 5); do
  curl -sS "https://api.github.com/users/USER/repos?per_page=100&page=$page"
done | jq -r '.[].full_name'

# POST JSON and extract field from response
TOKEN=$(curl -sS -X POST https://auth.example.com/token \
  -H "Content-Type: application/json" \
  -d '{"client_id":"ID","client_secret":"SECRET"}' \
  | jq -r '.access_token')

# Check HTTP status in a script
STATUS=$(curl -o /dev/null -sS -w "%{http_code}" https://api.example.com)
[ "$STATUS" = "200" ] || { echo "API returned $STATUS"; exit 1; }

# Stream NDJSON (newline-delimited JSON)
curl -sS --no-buffer https://api.example.com/stream \
  | while IFS= read -r line; do echo "$line" | jq '.event'; done

# Send multipart with metadata
curl -X POST https://api.example.com/upload \
     -F 'metadata={"filename":"report.pdf"};type=application/json' \
     -F 'file=@report.pdf;type=application/pdf'

[!TIP] Use curl --write-out '%{http_code}' --fail (-f) together: -f makes curl exit non-zero on HTTP error (4xx/5xx), and -w '%{http_code}' still lets you capture the code. In CI, -fsS is the canonical quiet-but-reliable combination.