← Back to Blog

curl Cheat Sheet: 30+ Examples for API Testing & Debugging

curl is the Swiss Army knife of HTTP. Available on every Unix system and Windows 10+, it is the fastest way to test an API endpoint, debug headers, inspect TLS certificates, simulate file uploads, and script HTTP workflows. This cheat sheet covers every command you will reach for, with copy-paste examples.

Why curl Is Still the Best Tool for API Testing

GUI tools like Postman and Insomnia are excellent for exploration, but curl has irreplaceable advantages in real engineering work. It runs in terminals, SSH sessions, Docker containers, and CI pipelines without installation. It produces reproducible, shareable commands. It handles every protocol variant: HTTP/1.1, HTTP/2, HTTP/3, WebSocket upgrades, SFTP, and more. When a colleague reports an API bug, a curl command is the most precise reproduction case you can share.

The catch is that curl has hundreds of flags. This cheat sheet covers the 90% you will actually use.

Basic Requests

GET request

# Simplest GET - output to stdout
curl https://api.example.com/users

# With verbose output (shows request + response headers, TLS info)
curl -v https://api.example.com/users

# Silent mode - suppress progress meter (useful in scripts)
curl -s https://api.example.com/users

# Pretty-print JSON response
curl -s https://api.example.com/users | jq .

# Or with Python's json.tool (no jq required)
curl -s https://api.example.com/users | python3 -m json.tool

POST with JSON body

This is the most common pattern for REST API testing. Always set Content-Type: application/json explicitly:

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

# From a file (cleaner for complex payloads)
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d @payload.json

# From stdin (compose with other commands)
echo '{"name":"test"}' | curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d @-

PUT, PATCH, DELETE

# PUT - replace a full resource
curl -X PUT https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice Smith", "email": "alice@example.com"}'

# PATCH - partial update
curl -X PATCH https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"email": "alice.smith@example.com"}'

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

# DELETE with a body (some APIs require it)
curl -X DELETE https://api.example.com/resources \
  -H "Content-Type: application/json" \
  -d '{"ids": [1, 2, 3]}'

POST with form data

# URL-encoded form (application/x-www-form-urlencoded)
# curl sets method to POST automatically when -d is used
curl -d "username=admin&password=secret" https://api.example.com/login

# Explicit -X POST with multiple fields
curl -X POST https://api.example.com/login \
  --data-urlencode "username=alice+admin" \
  --data-urlencode "password=p@ssw0rd!"

Headers

Setting custom request headers

# Single header
curl -H "Authorization: Bearer eyJhbGci..." https://api.example.com/me

# Multiple headers
curl -H "Authorization: Bearer eyJhbGci..." \
     -H "Accept: application/json" \
     -H "X-Request-ID: $(uuidgen)" \
     -H "X-Client-Version: 2.1.0" \
     https://api.example.com/protected

Viewing response headers

# Include response headers in output (before body)
curl -i https://example.com

# Headers only - performs a HEAD request
curl -I https://example.com

# Save headers to file, body to another file
curl -D response-headers.txt -o response-body.json https://api.example.com/data

# Extract a specific header value
curl -sI https://example.com | grep -i "content-type"

Authentication

HTTP Basic Auth

# curl handles the Base64 encoding automatically
curl -u username:password https://api.example.com/secure

# Prompt for password (avoids it appearing in shell history)
curl -u username https://api.example.com/secure

Bearer token / JWT

TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me

API key variants

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

# API key as query parameter
curl "https://api.example.com/data?api_key=your-key-here"

# AWS-style Signature V4 (with awscurl)
awscurl --service execute-api \
  -X GET https://xyz.execute-api.us-east-1.amazonaws.com/prod/resource

OAuth2 token exchange

# Get an access token (client credentials flow)
curl -X POST https://auth.example.com/oauth/token \
  -d "grant_type=client_credentials" \
  -d "client_id=myapp" \
  -d "client_secret=mysecret" \
  -d "scope=read:users"

# Parse the token from the response with jq
TOKEN=$(curl -s -X POST https://auth.example.com/oauth/token \
  -d "grant_type=client_credentials&client_id=myapp&client_secret=mysecret" \
  | jq -r .access_token)

curl -H "Authorization: Bearer $TOKEN" https://api.example.com/users

Cookies

# Send a cookie inline
curl -b "session=abc123; lang=en" https://example.com/dashboard

# Save cookies from response to file
curl -c cookies.txt https://example.com/login \
  -d "username=alice&password=secret"

# Load cookies from file for subsequent request
curl -b cookies.txt https://example.com/dashboard

# Both save and send (stateful session)
curl -c cookies.txt -b cookies.txt https://example.com/checkout

File Upload and Download

Upload a file (multipart/form-data)

# Single file upload
curl -F "file=@/path/to/document.pdf" https://api.example.com/upload

# File with custom MIME type and additional form fields
curl -F "file=@image.jpg;type=image/jpeg" \
     -F "title=Profile Photo" \
     -F "album_id=42" \
     https://api.example.com/photos

# Multiple files
curl -F "files[]=@file1.txt" \
     -F "files[]=@file2.txt" \
     https://api.example.com/batch-upload

Upload raw binary content

# PUT binary file directly (S3-style)
curl -X PUT --data-binary @image.png \
     -H "Content-Type: image/png" \
     https://bucket.s3.amazonaws.com/images/photo.png

Download files

# Save to a specific filename
curl -o output.zip https://example.com/archive.zip

# Save using the remote filename
curl -O https://example.com/archive.zip

# Resume interrupted download
curl -C - -O https://example.com/large-file.iso

# Show download progress bar
curl --progress-bar -O https://example.com/large-file.iso

# Download multiple files in parallel
curl -O https://example.com/file1.txt \
     -O https://example.com/file2.txt \
     -O https://example.com/file3.txt

Following Redirects

# Follow all redirects (301, 302, 307, 308)
curl -L https://example.com/old-url

# Limit the number of redirects
curl -L --max-redirs 3 https://example.com/redirect-chain

# Show redirect chain (verbose grep)
curl -Ls -o /dev/null -w "%{url_effective}" https://bit.ly/example

SSL/TLS

# Skip certificate verification (useful for self-signed certs in dev)
# NEVER use this in production scripts
curl -k https://self-signed.internal.example.com

# Use a custom CA bundle
curl --cacert /path/to/ca-bundle.crt https://internal.example.com

# Mutual TLS (mTLS) - client certificate authentication
curl --cert client.pem --key client-key.pem https://mtls.example.com

# Combined cert+key in one file
curl --cert combined.pem https://mtls.example.com

# Inspect the server's TLS certificate
curl -vI https://example.com 2>&1 | grep -A 20 "Server certificate"

# Check which TLS version is negotiated
curl -v https://example.com 2>&1 | grep "SSL connection"

Verbose Debug Mode

# Full verbose - shows TLS handshake, request headers, response headers, body
curl -v https://api.example.com/health

# Verbose but discard the body (just headers/debug info)
curl -vs -o /dev/null https://api.example.com/health

# Full hex+ASCII trace to a file
curl --trace trace.log https://api.example.com/health

# ASCII-only trace
curl --trace-ascii trace.log https://api.example.com/health

Timing and Performance Measurement

The -w (write-out) flag is curl's built-in benchmarking tool. Use it to measure every phase of the request lifecycle:

curl -o /dev/null -s -w "\
  DNS lookup:    %{time_namelookup}s\n\
  TCP connect:   %{time_connect}s\n\
  TLS handshake: %{time_appconnect}s\n\
  TTFB:          %{time_starttransfer}s\n\
  Total:         %{time_total}s\n\
  HTTP status:   %{http_code}\n\
  Downloaded:    %{size_download} bytes\n" \
  https://example.com
# Set timeouts to avoid hanging scripts
curl --connect-timeout 5 --max-time 30 https://api.example.com/data

# Retry on transient failures (5xx, connection refused)
curl --retry 3 --retry-delay 2 --retry-all-errors https://api.example.com/data

Proxies

# HTTP proxy
curl -x http://proxy.example.com:8080 https://api.example.com/data

# SOCKS5 proxy (e.g. SSH tunnel or Tor)
curl --socks5 127.0.0.1:9050 https://api.example.com/data

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

# Bypass proxy for specific hosts
curl --noproxy "localhost,127.0.0.1,.internal.example.com" \
     -x http://proxy.example.com:8080 \
     https://api.example.com/data

Useful One-Liners

Check if a URL returns 200

curl -s -o /dev/null -w "%{http_code}" https://example.com
# Output: 200

Get your public IP address

curl -s https://ifconfig.me
curl -s https://api.ipify.org

Test CORS headers

curl -sI -H "Origin: https://myapp.com" \
     -H "Access-Control-Request-Method: POST" \
     -X OPTIONS \
     https://api.example.com/users

Load test with a loop

for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{http_code}\n" https://api.example.com/health
done | sort | uniq -c

Test WebSocket upgrade

curl -sI -H "Connection: Upgrade" \
     -H "Upgrade: websocket" \
     -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
     -H "Sec-WebSocket-Version: 13" \
     https://ws.example.com/socket

Bypass Cloudflare or CDN - test origin directly

# Hit origin IP directly with the expected Host header
curl -sk -H "Host: example.com" https://1.2.3.4/api/health

Format and Validate Your API Responses

Got a messy JSON response from curl? Paste it into our JSON Formatter for instant pretty-printing, validation, and syntax highlighting. Free, runs in your browser.

Open JSON Formatter

curl with JSON Workflow

curl and jq together form a powerful API scripting toolkit. Here are common patterns:

# Extract a specific field from response
curl -s https://api.example.com/user/42 | jq .email

# Filter an array
curl -s https://api.example.com/users | jq '[.[] | select(.role == "admin")]'

# Build a curl command from environment variables
BASE_URL="https://api.example.com"
AUTH_TOKEN="$(cat ~/.tokens/api_token)"

curl -s \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  "${BASE_URL}/users" | jq .

# POST and capture the response ID
USER_ID=$(curl -s -X POST "${BASE_URL}/users" \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com"}' \
  | jq -r .id)

echo "Created user ID: $USER_ID"

Quick Flags Reference

  • -X METHOD - HTTP method (GET, POST, PUT, PATCH, DELETE)
  • -H "Header: value" - add a request header (repeatable)
  • -d "data" - request body (implies POST)
  • -d @file - read request body from file
  • -F "field=@file" - multipart file upload
  • -u user:pass - HTTP Basic authentication
  • -b "k=v" - send cookies
  • -c file - save response cookies to file
  • -o file - write response body to file
  • -O - save using server filename
  • -L - follow redirects
  • -k - skip TLS certificate verification
  • -v - verbose (shows headers, TLS info)
  • -s - silent (no progress meter)
  • -i - include response headers in output
  • -I - HEAD request (headers only)
  • -w "fmt" - custom output after completion (timing, status code)
  • --connect-timeout N - connection timeout in seconds
  • --max-time N - total request timeout in seconds
  • --retry N - retry on failure N times
  • --retry-delay N - seconds between retries
  • -x proxy - use HTTP/HTTPS proxy
  • --socks5 host:port - use SOCKS5 proxy
  • --cert file - client TLS certificate
  • --cacert file - custom CA bundle
  • --http2 - force HTTP/2
  • --compressed - request compressed response (gzip/br)

FAQ

Why does curl say "Could not resolve host"?

DNS resolution failed. Check: (1) the URL is correct, (2) your DNS resolver is reachable (nslookup example.com), (3) you are not behind a proxy that needs -x proxy:port. If testing an internal service, add it to /etc/hosts or use --resolve hostname:port:ip to bypass DNS entirely: curl --resolve api.example.com:443:192.168.1.100 https://api.example.com.

How do I make curl follow redirects and show the final URL?

Use -L to follow redirects and -w "%{url_effective}" to print the final URL: curl -Ls -o /dev/null -w "%{url_effective}\n" https://bit.ly/example. To see the full redirect chain, use -v and look for Location: headers in the response.

What is the difference between -d and --data-urlencode?

-d "field=value" sends the data as-is. If value contains special characters like @, +, &, or spaces, they must be percent-encoded manually. --data-urlencode "field=value with spaces & special chars" handles the encoding for you. Use --data-urlencode when the value comes from user input or contains characters that need escaping.

How do I test an API that requires a specific TLS version?

Use --tls-max and --tlsv flags: curl --tlsv1.2 --tls-max 1.2 https://legacy-api.example.com. To force TLS 1.3: curl --tlsv1.3 https://api.example.com. To see what TLS version was negotiated: curl -v https://api.example.com 2>&1 | grep "TLS\|SSL".

How do I send a request with a custom Host header (bypass CDN)?

Use -H "Host: domain.com" to override the Host header while connecting to a specific IP: curl -sk -H "Host: api.example.com" https://203.0.113.10/health. This bypasses Cloudflare, Fastly, or any other CDN/proxy and hits the origin server directly. The -k flag skips TLS verification since the IP address won't match the certificate CN.

Why does my curl POST work in the terminal but fail in a script?

Common causes: (1) variable expansion - single-quoted JSON -d '{"key":"$VAR"}' does not expand variables; use double quotes or -d "{\"key\":\"$VAR\"}". (2) Newlines in the data - heredoc or multiline strings may add unintended newlines. (3) Missing Content-Type header - curl defaults to application/x-www-form-urlencoded with -d, not JSON. Always set -H "Content-Type: application/json" explicitly for JSON payloads.

The Bottom Line

curl is available everywhere, produces reproducible commands, and handles every HTTP scenario from a simple GET to mutual TLS with custom CA bundles. Bookmark this cheat sheet and use man curl or curl --help all for the full reference when you need something more obscure.

Use our free tool here → JSON Formatter to pretty-print and validate the API responses you capture with curl, without writing a line of code.

UK
Written by Usman Khan
DevOps Engineer | MSc Cybersecurity | CEH | AWS Solutions Architect

Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.