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:-fmakes curl exit non-zero on HTTP error (4xx/5xx), and-w '%{http_code}'still lets you capture the code. In CI,-fsSis the canonical quiet-but-reliable combination.