curl is the universal language of HTTP. Every API doc includes curl examples. Every debugging session starts with “can you curl it?” If you’re not comfortable with curl, you’re missing the most portable tool in your kit.

Basic Requests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# GET (default)
curl https://api.example.com/users

# With headers shown
curl -i https://api.example.com/users

# Headers only
curl -I https://api.example.com/users

# Silent (no progress bar)
curl -s https://api.example.com/users

# Follow redirects
curl -L https://example.com/redirect

# Verbose (debug mode)
curl -v https://api.example.com/users

HTTP Methods

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# POST
curl -X POST https://api.example.com/users

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

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

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

# HEAD (headers only, like -I)
curl -X HEAD https://api.example.com/users

Sending Data

Form Data

1
2
3
4
5
6
7
# URL-encoded form
curl -X POST https://api.example.com/login \
  -d "username=admin&password=secret"

# From file
curl -X POST https://api.example.com/login \
  -d @credentials.txt

JSON Data

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Inline JSON
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com"}'

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

# Using --json (curl 7.82+)
curl --json '{"name":"Alice"}' https://api.example.com/users

File Upload

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Single file
curl -X POST https://api.example.com/upload \
  -F "file=@document.pdf"

# Multiple files
curl -X POST https://api.example.com/upload \
  -F "file1=@doc1.pdf" \
  -F "file2=@doc2.pdf"

# File with custom filename
curl -X POST https://api.example.com/upload \
  -F "file=@localname.pdf;filename=remote.pdf"

# File with content type
curl -X POST https://api.example.com/upload \
  -F "file=@image.png;type=image/png"

# Mixed form data and files
curl -X POST https://api.example.com/upload \
  -F "title=My Document" \
  -F "file=@document.pdf"

Headers

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Custom header
curl -H "X-Custom-Header: value" https://api.example.com

# Multiple headers
curl -H "Accept: application/json" \
     -H "X-API-Version: 2" \
     https://api.example.com

# User agent
curl -A "MyApp/1.0" https://api.example.com

# Referer
curl -e "https://example.com" https://api.example.com

Authentication

Basic Auth

1
2
3
4
5
6
7
8
# Username and password
curl -u username:password https://api.example.com

# Prompt for password
curl -u username https://api.example.com

# In URL (not recommended)
curl https://username:password@api.example.com

Bearer Token

1
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.example.com

API Key

1
2
3
4
5
# In header
curl -H "X-API-Key: YOUR_KEY" https://api.example.com

# In query string
curl "https://api.example.com?api_key=YOUR_KEY"

OAuth 2.0 Flow

1
2
3
4
5
6
7
8
9
# Get access token
curl -X POST https://auth.example.com/oauth/token \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_SECRET"

# Use token
TOKEN="eyJ..."
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/resource

Digest Auth

1
curl --digest -u username:password https://api.example.com

Output Options

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Save to file
curl -o output.html https://example.com

# Save with remote filename
curl -O https://example.com/file.zip

# Save multiple files
curl -O https://example.com/file1.zip -O https://example.com/file2.zip

# Append to file
curl https://example.com >> output.txt

# Write headers to file
curl -D headers.txt https://example.com

# Output to stdout and file
curl https://example.com | tee output.html

Timeouts and Retries

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Connection timeout (seconds)
curl --connect-timeout 5 https://api.example.com

# Max time for entire operation
curl -m 30 https://api.example.com

# Retry on failure
curl --retry 3 https://api.example.com

# Retry with delay
curl --retry 3 --retry-delay 5 https://api.example.com

# Retry on specific errors
curl --retry 3 --retry-all-errors https://api.example.com

SSL/TLS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Skip certificate verification (insecure!)
curl -k https://self-signed.example.com

# Use specific CA certificate
curl --cacert /path/to/ca.crt https://api.example.com

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

# Force TLS version
curl --tlsv1.2 https://api.example.com
curl --tlsv1.3 https://api.example.com

Proxy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# HTTP proxy
curl -x http://proxy:8080 https://api.example.com

# SOCKS5 proxy
curl --socks5 localhost:1080 https://api.example.com

# Proxy with auth
curl -x http://user:pass@proxy:8080 https://api.example.com

# No proxy for specific hosts
curl --noproxy "localhost,*.internal" https://api.example.com

Cookies

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Send cookie
curl -b "session=abc123" https://api.example.com

# Send cookies from file
curl -b cookies.txt https://api.example.com

# Save cookies to file
curl -c cookies.txt https://api.example.com/login

# Full session (save and send)
curl -b cookies.txt -c cookies.txt https://api.example.com

Response Inspection

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# HTTP status code only
curl -s -o /dev/null -w "%{http_code}" https://api.example.com

# Response time
curl -s -o /dev/null -w "%{time_total}s" https://api.example.com

# Detailed timing
curl -s -o /dev/null -w "
    DNS:        %{time_namelookup}s
    Connect:    %{time_connect}s
    TLS:        %{time_appconnect}s
    Start:      %{time_starttransfer}s
    Total:      %{time_total}s
    Size:       %{size_download} bytes
    Speed:      %{speed_download} bytes/sec
" https://api.example.com

# Content type
curl -s -o /dev/null -w "%{content_type}" https://api.example.com

Scripting Patterns

Health Check

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash
URL="https://api.example.com/health"

STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
if [ "$STATUS" -eq 200 ]; then
    echo "OK"
    exit 0
else
    echo "FAIL: HTTP $STATUS"
    exit 1
fi

API Wrapper

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
API_BASE="https://api.example.com"
API_KEY="${API_KEY:?API_KEY required}"

api_get() {
    curl -s -H "Authorization: Bearer $API_KEY" "$API_BASE$1"
}

api_post() {
    curl -s -X POST \
        -H "Authorization: Bearer $API_KEY" \
        -H "Content-Type: application/json" \
        -d "$2" \
        "$API_BASE$1"
}

# Usage
api_get "/users" | jq '.'
api_post "/users" '{"name":"Alice"}'

Retry with Backoff

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
URL="$1"
MAX_RETRIES=5
RETRY_DELAY=1

for i in $(seq 1 $MAX_RETRIES); do
    RESPONSE=$(curl -s -w "\n%{http_code}" "$URL")
    STATUS=$(echo "$RESPONSE" | tail -1)
    BODY=$(echo "$RESPONSE" | sed '$d')
    
    if [ "$STATUS" -eq 200 ]; then
        echo "$BODY"
        exit 0
    fi
    
    echo "Attempt $i failed (HTTP $STATUS), retrying in ${RETRY_DELAY}s..." >&2
    sleep $RETRY_DELAY
    RETRY_DELAY=$((RETRY_DELAY * 2))
done

echo "Failed after $MAX_RETRIES attempts" >&2
exit 1

Parallel Requests

1
2
3
4
5
# Using xargs
cat urls.txt | xargs -P 10 -I {} curl -s -o /dev/null -w "{}: %{http_code}\n" {}

# Using GNU parallel
parallel -j 10 curl -s -o /dev/null -w "{}: %{http_code}\n" ::: $(cat urls.txt)

Config Files

Create ~/.curlrc for defaults:

#####-----AlSsSsDmDuloiihheaeswclloofxfeaaeewwa-arytnn-utu-sitteelilaorrtmtgfnbrreeoyootunlrri3stldsm0eoeer"wfoMauayrutgCeleudtnritlr/e1c.t0s"

Debugging

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Maximum verbosity
curl -v https://api.example.com

# Trace everything (including data)
curl --trace - https://api.example.com

# Trace to file
curl --trace trace.log https://api.example.com

# Show only request headers
curl -v -o /dev/null https://api.example.com 2>&1 | grep "^>"

# Show only response headers  
curl -v -o /dev/null https://api.example.com 2>&1 | grep "^<"

Common Patterns

POST JSON and Extract Response

1
2
3
curl -s -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice"}' | jq '.id'

Check If Endpoint Exists

1
2
3
4
5
if curl -s --head --fail https://api.example.com/resource > /dev/null; then
    echo "Exists"
else
    echo "Not found or error"
fi

Download with Progress

1
curl -# -O https://example.com/large-file.zip

Test API Latency

1
2
3
for i in {1..10}; do
    curl -s -o /dev/null -w "%{time_total}\n" https://api.example.com
done | awk '{sum+=$1} END {print "Average:", sum/NR "s"}'

Quick Reference

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
-X METHOD       # HTTP method
-H "Header"     # Add header
-d "data"       # POST data
-F "field=val"  # Form/file upload
-o file         # Output to file
-O              # Output to remote filename
-i              # Include response headers
-I              # Headers only
-s              # Silent
-S              # Show errors (with -s)
-v              # Verbose
-L              # Follow redirects
-u user:pass    # Basic auth
-b cookies      # Send cookies
-c cookies      # Save cookies
-m seconds      # Max time
-k              # Insecure (skip cert verify)
-x proxy        # Use proxy
-w "format"     # Custom output format
--json "data"   # POST JSON (7.82+)

curl is more than a download tool—it’s a complete HTTP client that fits in your terminal. Learn it well, and you’ll never need Postman for a quick test again.

Start with simple GETs, graduate to authenticated POSTs, and eventually you’ll be scripting entire API workflows from the command line.