curl · http · api
curl: A Pocket Companion
Talk to any HTTP API from the terminal — GET, POST JSON, auth, and the flags that make debugging obvious.
curl is how you talk to the web from the command line — test an endpoint, reproduce a
bug, script a deploy, or poke at someone’s API before writing a line of code. This guide
covers the moves that come up daily, using the kind of JSON API you’d build with our
Docker pocket companion.
$ curl https://api.example.com/health
{"status":"ok"}
Seeing what’s really happening
A plain curl URL prints the response body. The flags that make it useful:
$ curl -i https://api.example.com/health # include response headers
$ curl -s https://api.example.com/health # silent — no progress meter
$ curl -v https://api.example.com/health # verbose — show the full request + response
$ curl -o out.json https://... # write body to a file
$ curl -L https://example.com # follow redirects (301/302)
The everyday combo: -s and -i
-s hides curl’s progress bar (essential when piping to jq); -i shows the status
line and headers so you can see what came back, not just the body. -v is the
big hammer when something’s wrong — it prints the exact bytes sent and received.
Methods and data
# GET is the default. Be explicit with -X for others.
$ curl -X DELETE https://api.example.com/items/42
# POST form data
$ curl -d "name=Ada&role=admin" https://api.example.com/users
# POST JSON — set the header *and* send the body
$ curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Ada","role":"admin"}'
# send a JSON file as the body
$ curl -X POST https://api.example.com/import \
-H "Content-Type: application/json" \
-d @payload.json
JSON needs the Content-Type header
-d defaults to application/x-www-form-urlencoded. If you send JSON without
-H "Content-Type: application/json", most APIs will reject it or mis-parse it. Set
the header every time you send a JSON body.
Authentication
# Bearer token (most APIs)
$ curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me
# Basic auth
$ curl -u username:password https://api.example.com/private
# an API key in a header
$ curl -H "X-API-Key: $KEY" https://api.example.com/data
A real login-then-use flow, exactly how you’d test an admin API:
$ TOKEN=$(curl -s -X POST https://api.example.com/login \
-H "Content-Type: application/json" \
-d '{"email":"me@x.com","password":"secret"}' | jq -r .access_token)
$ curl -s -H "Authorization: Bearer $TOKEN" https://api.example.com/me
(That jq -r .access_token pulls one field out of the JSON response — see the regex and
JSON guides for more on slicing output.)
Uploads and downloads
# upload a file as multipart/form-data (note the @)
$ curl -F "file=@cover.png;type=image/png" https://api.example.com/uploads
# download, keeping the remote filename, resuming if interrupted
$ curl -O -C - https://example.com/big.iso
Inspecting and timing
# headers only (a HEAD request)
$ curl -I https://example.com
# just the HTTP status code (great for scripts / health checks)
$ curl -s -o /dev/null -w "%{http_code}\n" https://api.example.com/health
# where is the time going?
$ curl -s -o /dev/null -w "dns:%{time_namelookup} connect:%{time_connect} total:%{time_total}\n" https://example.com
The status-code one-liner is a tiny but mighty health check — 200 means up, anything
else is worth a look.
Pretty-printing JSON
curl prints raw JSON; pipe it through jq to read it:
$ curl -s https://api.example.com/items | jq
$ curl -s https://api.example.com/items | jq '.[] | .name' # just the names
When it breaks
| Symptom | Likely cause |
|---|---|
curl: (6) Could not resolve host |
DNS / typo in the hostname. |
curl: (7) Failed to connect |
Nothing listening on that host:port, or a firewall. |
curl: (60) SSL certificate problem |
Bad/expired/self-signed cert. -k skips verification — for testing only. |
| Empty body but you expected JSON | Add -i to see the status; you may be getting a 4xx/redirect. |
| JSON body rejected (400) | Missing -H "Content-Type: application/json". |
Pocket cheat-sheet
| Do | Command |
|---|---|
| GET with headers / silent / verbose | curl -i URL / -s / -v |
| POST JSON | curl -X POST URL -H "Content-Type: application/json" -d '{...}' |
| Bearer auth | curl -H "Authorization: Bearer $T" URL |
| Body from file | curl -d @file.json URL |
| Upload a file | curl -F "file=@path" URL |
| Just the status code | curl -s -o /dev/null -w "%{http_code}" URL |
| Follow redirects | curl -L URL |
| Pretty JSON | curl -s URL \| jq |
Once curl is muscle memory you can exercise any API before writing code, reproduce a bug
in one line, and drop health checks into any script. man curl lists the rest — there are
hundreds of flags, but these are the ones you’ll actually use.
Get new field notes by email
Occasional, practical write-ups on building and shipping software. No spam — unsubscribe anytime.