curl Command: The Complete Guide for Developers

curl http api command-line developer-tools

curl is a command-line tool for transferring data using URLs. Developers use it to test HTTP APIs, download files, inspect headers, send form data, and debug network requests — all from the terminal without a GUI client.

Basic GET Request

The simplest curl invocation fetches a URL and prints the response body to stdout:

curl https://api.example.com/users

By default curl follows no redirects, shows no headers, and prints the body. You’ll build on this with flags.

Essential Flags

FlagLong formEffect
-X--requestSet HTTP method (GET is default)
-H--headerAdd a request header
-d--dataSend request body
-o--outputWrite response to file
-O--remote-nameWrite response to file named from URL
-i--includeInclude response headers in output
-I--headFetch headers only (HEAD request)
-L--locationFollow redirects
-s--silentSuppress progress meter
-v--verboseShow full request + response details
-w--write-outPrint metadata after transfer
-u--userBasic auth credentials
--compressedRequest compressed response, auto-decompress

HTTP Methods

# GET (default)
curl https://api.example.com/posts

# POST
curl -X POST https://api.example.com/posts

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

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

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

# HEAD (headers only, no body)
curl -I https://api.example.com/posts

Sending JSON

POST and PUT requests to REST APIs typically send JSON. Set the Content-Type header and pass the body with -d:

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

For multi-line JSON or to avoid shell escaping issues, read from a file:

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

The @ prefix tells curl to read the body from a file. Use the JSON Formatter tool to format and validate your JSON payload before sending.

Viewing Response Headers

Include response headers in the output with -i:

curl -i https://api.example.com/users

Output:

HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: public, max-age=3600
x-ratelimit-remaining: 98

[response body]

To see only headers (HEAD request), use -I:

curl -I https://api.example.com/users

For a reference on what status codes mean, see the HTTP Status Codes guide.

Authentication

Basic Auth

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

curl encodes the credentials as Base64 and sends an Authorization: Basic <token> header.

Bearer Token

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
  https://api.example.com/protected

API Key in Header

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

API Key in Query String

curl "https://api.example.com/data?api_key=your-api-key"

Note the quotes around the URL when it contains special characters like & or ?. For proper URL encoding of query parameters, see the URL Encoding guide.

Downloading Files

Download and save with the original filename:

curl -O https://example.com/archive.zip

Save with a custom filename:

curl -o myfile.zip https://example.com/archive.zip

Download multiple files:

curl -O https://example.com/file1.zip \
     -O https://example.com/file2.zip

Resume an interrupted download:

curl -C - -O https://example.com/largefile.zip

The -C - flag tells curl to detect the offset automatically and resume from where it stopped.

Sending Form Data

URL-Encoded Form (application/x-www-form-urlencoded)

curl -X POST https://example.com/login \
  -d "username=alice&password=secret"

curl sets Content-Type: application/x-www-form-urlencoded automatically when you use -d with plain key=value strings.

Multipart Form (file uploads)

curl -X POST https://api.example.com/upload \
  -F "file=@photo.jpg" \
  -F "description=Profile photo"

The -F flag triggers multipart encoding. @filename includes the file; plain values are sent as text fields.

Request and Response Inspection

Verbose Mode

-v prints the full transaction — DNS resolution, TLS handshake, request headers, response headers, and response body:

curl -v https://api.example.com/users
*   Trying 93.184.216.34:443...
* Connected to api.example.com (93.184.216.34) port 443
* TLS 1.3 connection using TLS_AES_256_GCM_SHA384
> GET /users HTTP/2
> Host: api.example.com
> User-Agent: curl/8.1.2
> Accept: */*
>
< HTTP/2 200
< content-type: application/json
<
[body]

Lines starting with > are sent; lines starting with < are received.

Timing Breakdown

The -w flag prints structured metadata after the transfer:

curl -s -o /dev/null -w "
  dns_lookup:     %{time_namelookup}s
  tcp_connect:    %{time_connect}s
  tls_handshake:  %{time_appconnect}s
  ttfb:           %{time_starttransfer}s
  total:          %{time_total}s
  http_code:      %{http_code}
" https://api.example.com/users

This is a useful performance diagnostic for API endpoints. The -s suppresses the progress meter; -o /dev/null discards the body.

Working with APIs

Pretty-Print JSON Response

Pipe the response to python -m json.tool or jq:

curl -s https://api.example.com/users | python -m json.tool
curl -s https://api.example.com/users | jq .

Chaining Requests

Extract a value from one response and use it in the next:

TOKEN=$(curl -s -X POST https://api.example.com/auth \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"secret"}' \
  | python -c "import sys,json; print(json.load(sys.stdin)['token'])")

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

Rate Limiting — Check Remaining Quota

curl -s -I https://api.example.com/data | grep -i "ratelimit"

Inspect the X-RateLimit-Remaining and X-RateLimit-Reset headers to avoid hitting limits.

HTTPS and SSL

Skip Certificate Verification (Testing Only)

curl -k https://localhost:3000/api
# or
curl --insecure https://localhost:3000/api

Only use -k against local development servers you control. Never skip certificate verification in production scripts — it removes protection against man-in-the-middle attacks.

Specify a CA Certificate

curl --cacert /path/to/ca-bundle.crt https://internal.example.com/api

Client Certificate Authentication

curl --cert client.crt --key client.key https://api.example.com/secure

Proxy Support

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

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

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

Saving and Reusing Configuration

For frequently used flags, create a .curlrc file in your home directory:

# ~/.curlrc
silent
location           # always follow redirects
compressed         # always request compressed responses

curl reads this file automatically on every invocation. Project-specific config can live in .curlrc in the current directory.

Common Patterns for CI/CD

Health Check

# Fail if HTTP status is not 200
curl --fail -s https://yoursite.com/health || exit 1

Wait for a Service to Start

until curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -q "200"; do
  sleep 2
done
echo "Service ready"

POST with Error Handling

HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" \
  -X POST https://api.example.com/deploy \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version": "1.2.3"}')

if [ "$HTTP_STATUS" -ne 200 ]; then
  echo "Deploy failed with status $HTTP_STATUS"
  cat response.json
  exit 1
fi

curl vs Other HTTP Tools

ToolBest for
curlScripting, CI/CD, quick terminal tests
HTTPieHuman-readable output, interactive terminal use
PostmanGUI-based API exploration, collections
wgetRecursive downloads, mirroring
fetch (browser)Client-side JavaScript HTTP requests

curl’s advantage is ubiquity — it’s available on every Unix-like system, Windows 10+, and most CI runners without installation. The Git Commands cheat sheet covers another essential developer CLI tool with a similar depth of flags and options. For encoding URLs correctly before passing them to curl, use the URL Encoder tool.

Debugging Connection Issues

Check TLS Certificate Details

curl -v --head https://api.example.com 2>&1 | grep -E "SSL|TLS|certificate|subject|issuer"

Test a Specific IP or Host

Bypass DNS and connect directly to an IP while sending the correct Host header — useful for testing CDN origin servers or debugging DNS propagation:

curl -H "Host: api.example.com" https://93.184.216.34/endpoint

Test with a Specific Protocol Version

curl --http1.1 https://api.example.com/data   # force HTTP/1.1
curl --http2 https://api.example.com/data     # force HTTP/2

If an API behaves differently on HTTP/1.1 vs HTTP/2, this isolates the protocol as a variable.

Cookies

# Send a cookie
curl -b "session_id=abc123; theme=dark" https://api.example.com/

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

# Load cookies from a file
curl -b cookies.txt https://api.example.com/dashboard

# Save and load cookies (full session simulation)
curl -c cookies.txt -b cookies.txt https://api.example.com/login \
  -d "username=alice&password=secret"

Uploading to APIs with Retry Logic

For production scripts, add retry logic to handle transient failures:

curl --retry 3 \
     --retry-delay 2 \
     --retry-max-time 30 \
     --fail \
     -X POST https://api.example.com/events \
     -H "Content-Type: application/json" \
     -d @event.json
  • --retry 3: retry up to 3 times on transient failures (network errors, 5xx responses)
  • --retry-delay 2: wait 2 seconds between retries
  • --retry-max-time 30: abort if total retry time exceeds 30 seconds
  • --fail: exit with error code on HTTP 4xx/5xx (without this, curl exits 0 even on failure)

WebSockets

curl has limited WebSocket support (added in curl 7.86.0). For basic testing:

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

For interactive WebSocket testing, websocat or browser DevTools are more practical. curl’s WebSocket support is primarily useful for CI health checks that verify a WebSocket endpoint accepts upgrade requests.

Environment Variables for curl Configuration

When automating curl in scripts, use environment variables for secrets to avoid exposing credentials in process lists or shell history:

export API_TOKEN="your-secret-token"
export BASE_URL="https://api.example.com"

curl -H "Authorization: Bearer $API_TOKEN" \
     "$BASE_URL/users"

In CI systems (GitHub Actions, GitLab CI), inject secrets via the platform’s secret management rather than hardcoding them in workflow files. For reference on how HTTP authentication works at the protocol level, see the HTTP Status Codes guide.

curl on Windows

On Windows 10 and later, curl is available natively in PowerShell and Command Prompt (it’s an alias for Invoke-WebRequest in PowerShell, but the actual curl binary is in C:\Windows\System32\curl.exe).

To use the real curl binary from PowerShell:

curl.exe -s https://api.example.com/users
# Note: use curl.exe to avoid PowerShell's Invoke-WebRequest alias

The flags and behavior are identical to Linux/macOS curl since both use the same underlying libcurl library.

Quick Reference

TaskCommand
Simple GETcurl https://example.com
GET with headers showncurl -i https://example.com
POST JSONcurl -X POST -H "Content-Type: application/json" -d '{}' https://example.com
Bearer authcurl -H "Authorization: Bearer TOKEN" https://example.com
Download filecurl -O https://example.com/file.zip
Follow redirectscurl -L https://example.com
Verbose debugcurl -v https://example.com
Timing breakdowncurl -s -o /dev/null -w "%{time_total}" https://example.com
Skip TLS (dev only)curl -k https://localhost:3000
Retry on failurecurl --retry 3 --fail https://example.com