curl API Patterns

curl recipes for API testing, debugging, and scripting.

Request Fundamentals

GET with headers and jq pipeline
curl -s -H "Accept: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  https://api.example.com/v1/devices | jq '.[] | {name, status}'
POST JSON payload from stdin
jq -n '{name: "sw-01", vlan: 10}' | \
  curl -s -X POST https://api.example.com/v1/devices \
    -H "Content-Type: application/json" \
    -d @- | jq .
POST from file — large payloads or templated requests
curl -s -X POST https://api.example.com/v1/devices \
  -H "Content-Type: application/json" \
  -d @payload.json | jq .

Response Inspection

Show response headers only — debug content type, rate limits
curl -sI https://api.example.com/v1/devices
Show headers AND body — full round-trip visibility
curl -s -D- https://api.example.com/v1/devices | head -20
Extract HTTP status code — for scripting conditionals
status=$(curl -s -o /dev/null -w '%{http_code}' https://api.example.com/v1/health)
if [[ "$status" -ne 200 ]]; then
    echo "API unhealthy: HTTP $status" >&2
    exit 1
fi
Capture response time — performance monitoring
curl -s -o /dev/null -w 'DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n' \
  https://api.example.com/v1/health

Authentication Patterns

Basic auth — ISE ERS pattern
curl -sk -u "$ISE_USER:$ISE_PASS" \
  -H "Accept: application/json" \
  https://10.50.1.20:9060/ers/config/networkdevice
Bearer token — Vault pattern
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
  https://10.50.1.60:8200/v1/secret/data/domus/api-keys | jq '.data.data'

Retry and Error Handling

Retry with exponential backoff
for attempt in 1 2 3 4 5; do
    response=$(curl -s -w '\n%{http_code}' https://api.example.com/v1/data)
    status=$(echo "$response" | tail -1)
    body=$(echo "$response" | sed '$d')

    if [[ "$status" -eq 200 ]]; then
        echo "$body" | jq .
        break
    elif [[ "$status" -eq 429 ]]; then
        retry_after=$(curl -sI https://api.example.com/v1/data | grep -i retry-after | awk '{print $2}' | tr -d '\r')
        sleep "${retry_after:-$(( 2 ** attempt ))}"
    else
        echo "Attempt $attempt failed: HTTP $status" >&2
        sleep $(( 2 ** attempt ))
    fi
done
Silent failure detection — curl exit code vs HTTP status
# curl exit 0 means TCP success, NOT HTTP success
# Always check HTTP status separately
if ! curl -sf https://api.example.com/v1/health > /dev/null; then
    echo "Request failed (network or HTTP error)" >&2
fi

Output Formatting

Write response body to file while showing progress
curl -o response.json https://api.example.com/v1/export
Pipe through jq for filtered output
curl -s https://api.example.com/v1/devices | \
  jq -r '.[] | [.name, .ip, .status] | @tsv' | \
  column -t
Save headers and body separately
curl -s -D headers.txt -o body.json https://api.example.com/v1/devices

Multipart and File Upload

Upload file with multipart form
curl -s -X POST https://api.example.com/v1/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@backup.tar.gz" \
  -F "description=ISE config backup"

Proxy and TLS

Skip certificate verification — lab/dev only
curl -sk https://self-signed.lab.local/api/v1/data
-k disables TLS verification. Never use in production scripts. Pin the CA cert instead with --cacert.
Use specific CA certificate
curl -s --cacert /etc/ssl/certs/domus-ca.pem \
  https://vault-01.inside.domusdigitalis.dev:8200/v1/sys/health
Verbose output — debug TLS handshake and headers
curl -v https://api.example.com/v1/health 2>&1 | grep -E '^\*|^[<>]'