curl cheat sheet: the commands you actually need
curl is the universal HTTP client: preinstalled on macOS and Linux, shipped with Windows 10 and later, and the shared language of every API's documentation. This is the subset of its two hundred plus flags that covers real daily work, organized by task. If you would rather build and inspect requests visually, the API Tester does the same from your browser, with requests going directly from your browser to the target.
The flags that matter
| Flag | Long form | Does |
|---|---|---|
-X | --request | Set the HTTP method |
-H | --header | Add a request header |
-d | --data | Send a request body (implies POST) |
-i | --include | Show response headers with the body |
-I | --head | HEAD request: headers only |
-L | --location | Follow redirects |
-o | --output | Write body to a file |
-s | --silent | No progress meter |
-v | --verbose | Show the full conversation |
-u | --user | Basic auth credentials |
GET requests
curl https://api.example.com/users # body to stdout
curl -i https://api.example.com/users # include response headers
curl -I https://example.com # headers only
curl -L http://example.com # follow redirects
That last one matters more than it looks: curl does not follow redirects by default. A plain curl against a URL that 301s to https prints nothing but the redirect page. If a request "returns nothing", add -L, and to understand what the redirect chain is doing, see 301 vs 302 redirects.
Query strings with special characters need quoting, and --data-urlencode handles encoding for you on GET via -G:
curl -G https://api.example.com/search --data-urlencode "q=hello world & more"
POST with JSON: the command you will use most
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Ada", "email": "ada@example.com"}'
Three details that save debugging time:
Content-Typeis not optional. Without it,-dsendsapplication/x-www-form-urlencoded, and JSON APIs answer with 400s or worse, silently misparse.- Single quotes around the JSON on macOS and Linux, so the shell leaves the inner double quotes alone. On Windows cmd, the quoting inverts: double quotes outside, backslash escaped inside. PowerShell users are usually happier with
Invoke-RestMethodor curl inside WSL. -X POSTis actually redundant when-dis present, since data implies POST. It is harmless and common as documentation of intent.
Body from a file, which sidesteps all shell quoting pain:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d @user.json
Other methods work the same way:
curl -X PUT ... -d @user.json # replace
curl -X PATCH ... -d '{"name":"Grace"}' # partial update
curl -X DELETE https://api.example.com/users/42
When to use which is the subject of GET vs POST vs PUT vs PATCH.
Authentication
# Bearer token (most modern APIs)
curl -H "Authorization: Bearer eyJhbGci..." https://api.example.com/me
# Basic auth: curl builds the base64 header for you
curl -u username:password https://api.example.com/admin
# API key in a custom header
curl -H "X-API-Key: abc123" https://api.example.com/data
-u username alone, with no colon, makes curl prompt for the password so it never lands in shell history. If the token you are passing is a JWT, remember it is readable by anyone who holds it; paste it into the JWT Decoder to see exactly what it exposes.
Form data and file uploads
# URL encoded form, like an HTML form submit
curl -d "name=Ada" -d "role=admin" https://example.com/form
# multipart upload, like a file input
curl -F "file=@photo.jpg" -F "caption=Hello" https://api.example.com/upload
-d and -F are different content types; a server expecting multipart rejects -d and vice versa.
Saving output
curl -o page.html https://example.com # name it yourself
curl -O https://example.com/file.zip # keep the remote name
curl -C - -O https://example.com/big.iso # resume an interrupted download
How do you see what curl actually did?
-v prints the whole exchange: DNS resolution, TLS handshake, every request and response header. Lines starting > are what you sent, < what came back. Nine times out of ten, the bug is visible in there: a missing header, an unexpected redirect, a TLS failure.
curl -v https://api.example.com/users 2>&1 | less
Timing breakdown, to find where slowness lives:
curl -s -o /dev/null -w "dns: %{time_namelookup}s connect: %{time_connect}s tls: %{time_appconnect}s ttfb: %{time_starttransfer}s total: %{time_total}s\n" https://example.com
Status code only, ideal in scripts:
curl -s -o /dev/null -w "%{http_code}" https://example.com
Add -f in scripts so HTTP errors produce a non zero exit code instead of silently succeeding with an error page as output.
TLS inspection. curl -v shows the negotiated TLS version and the certificate chain, which is enough to spot the expired or misordered chains covered in our certificate chain guide. -k disables certificate verification; treat it as a diagnostic probe, never a fix that ships.
Useful defaults and habits
# JSON pretty printing: pipe to jq
curl -s https://api.example.com/users | jq .
# Send many requests with different data quickly: use a here string per line
curl -s https://httpbin.org/post -d @- <<< '{"test": true}'
# Fake a browser when a server blocks default curl
curl -A "Mozilla/5.0" https://example.com
That user agent trick works because servers read the User-Agent header, the same string dissected in reading a User-Agent string.
For interactive work, building the request, tweaking a header, resending, a visual client is faster than editing a command line. The API Tester runs in your browser with requests going straight from you to the API, nothing proxied through our servers, and shows status, headers, timing, and formatted JSON responses as you iterate.