toolhq.io

All posts
7 min readby Jameel Haider

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

FlagLong formDoes
-X--requestSet the HTTP method
-H--headerAdd a request header
-d--dataSend a request body (implies POST)
-i--includeShow response headers with the body
-I--headHEAD request: headers only
-L--locationFollow redirects
-o--outputWrite body to a file
-s--silentNo progress meter
-v--verboseShow the full conversation
-u--userBasic 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-Type is not optional. Without it, -d sends application/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-RestMethod or curl inside WSL.
  • -X POST is actually redundant when -d is 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.