Overview
curl is the fastest tool for manually testing HTTP endpoints before writing client code. This card collects the invocations for the most common API testing scenarios: CRUD operations, JSON payloads, authentication, file uploads, and response inspection. For the full flag reference see curl-flags; for parsing JSON responses see json-jq-recipes.
GET requests
| Pattern | Command | Notes |
|---|---|---|
| Basic GET | curl https://api.example.com/items | Outputs body to stdout |
| With query params | curl "https://api.example.com/items?page=2&limit=10" | Quote the URL to prevent shell expansion of & |
| Follow redirects | curl -L https://example.com | Follows 3xx chains |
| Save to file | curl -o out.json https://api.example.com/data | Named output file |
| Show response headers | curl -I https://api.example.com/items | HEAD request only |
| Show headers + body | curl -D - https://api.example.com/items | -D - prints headers to stdout before body |
| Timing breakdown | curl -w "@curl-format.txt" -s -o /dev/null https://example.com | Custom format for DNS, connect, TTFB |
| Verbose | curl -v https://api.example.com/items | Shows request + response headers and TLS handshake |
| Silent + fail | curl -sf https://api.example.com/items | No progress; exit code 22 on HTTP error |
For API testing, combine -s (silent) and -S (show errors) to suppress progress but not hide curl errors.
POST and PUT with JSON
| Pattern | Command | Notes |
|---|---|---|
| POST JSON inline | curl -X POST https://api.example.com/items -H "Content-Type: application/json" -d '{"name":"foo"}' | Single-line JSON body |
| POST JSON from file | curl -X POST https://api.example.com/items -H "Content-Type: application/json" -d @payload.json | @ reads from file |
| PUT update | curl -X PUT https://api.example.com/items/42 -H "Content-Type: application/json" -d '{"name":"bar"}' | Replace resource |
| PATCH partial | curl -X PATCH https://api.example.com/items/42 -H "Content-Type: application/json" -d '{"status":"active"}' | Partial update |
| DELETE | curl -X DELETE https://api.example.com/items/42 | No body required |
| Accept header | curl https://api.example.com/items -H "Accept: application/json" | Request specific format |
| Multiple headers | curl ... -H "Content-Type: application/json" -H "X-Request-ID: abc123" | Repeat -H for each |
-d sets method to POST implicitly; add -X POST explicitly for clarity in scripts.
Authentication patterns
| Auth type | Command | Notes |
|---|---|---|
| Bearer token | curl -H "Authorization: Bearer $TOKEN" https://api.example.com/protected | Standard JWT or OAuth |
| Basic auth | curl -u username:password https://api.example.com/resource | URL-encodes credentials |
| Basic auth (prompt) | curl -u username https://api.example.com/resource | Prompts for password |
| API key header | curl -H "X-API-Key: $API_KEY" https://api.example.com/resource | Key name varies by service |
| API key query param | curl "https://api.example.com/resource?api_key=$API_KEY" | Less secure; logged in URLs |
| Client certificate | curl --cert client.crt --key client.key https://api.example.com/resource | mTLS |
| Cookie jar | curl -c cookies.txt -b cookies.txt https://api.example.com/login | Save and send cookies |
| Digest auth | curl --digest -u user:pass https://api.example.com/resource | Challenge-response |
Store tokens in environment variables, not inline. export TOKEN=$(cat ~/.tokens/myapi).
File uploads and multipart
| Pattern | Command | Notes |
|---|---|---|
| Single file upload | curl -F "file=@photo.jpg" https://api.example.com/upload | Multipart form |
| File + field | curl -F "file=@photo.jpg" -F "title=My Photo" https://api.example.com/upload | Mix file and fields |
| File with MIME type | curl -F "file=@data.csv;type=text/csv" https://api.example.com/upload | Override detected type |
| Binary body (PUT) | curl -X PUT --data-binary @image.png -H "Content-Type: image/png" https://api.example.com/images/1 | Raw binary, no multipart |
| URL-encoded form | curl -d "name=foo&value=bar" https://api.example.com/form | application/x-www-form-urlencoded |
-F triggers multipart/form-data; -d triggers application/x-www-form-urlencoded. JSON bodies need -H "Content-Type: application/json" explicitly.
Response inspection and scripting
| Pattern | Command | Notes |
|---|---|---|
| HTTP status code only | curl -s -o /dev/null -w "%{http_code}" https://api.example.com/items | Useful in scripts |
| Pretty-print JSON | curl -s https://api.example.com/items | jq . | Pipe to jq |
| Extract field | curl -s https://api.example.com/item/1 | jq '.name' | Single field |
| Store in variable | BODY=$(curl -s https://api.example.com/item/1) | Capture for reuse |
| Retry on failure | curl --retry 3 --retry-delay 2 https://api.example.com/items | Transient error recovery |
| Timeout | curl --max-time 10 https://api.example.com/items | Abort after 10 seconds |
| Ignore TLS errors | curl -k https://localhost:8443/items | Dev only; never production |
| Proxy | curl -x http://proxy:3128 https://api.example.com/items | HTTP proxy |
Chain curl with jq for lightweight API scripting: curl -sf https://api.example.com/users | jq '.[].email' dumps all emails.
Common gotchas
- Single quotes around
{}in shell:'{"key":"val"}'is safe; double quotes require escaping internal quotes. -d @filereads the file as-is, including newlines. Minify JSON before sending if the API is picky.curlexits 0 on HTTP 4xx/5xx unless you pass-f/--fail. Scripts that check only$?miss server errors.--compressedenablesAccept-Encoding: gzipand decompresses the response automatically. Without it, a gzip response prints binary garbage.- Redirect following (
-L) may change the method from POST to GET on a 302. Use--post301and--post302to preserve POST. -u user:passappears in process listing on Linux. Prefer-H "Authorization: Basic $(echo -n user:pass | base64)"in automated scripts.