cURL: The Complete Guide for 2026
curl is the universal Swiss army knife for HTTP. It runs everywhere (Linux, macOS, Windows), it’s scriptable, it speaks dozens of protocols, and it’s the fastest way to answer questions like:
- “What does this endpoint return?”
- “Which headers am I actually sending?”
- “Why is my request getting a 401 / 415 / 502?”
- “How do I upload a file or send JSON?”
Table of Contents
- What cURL is (and when to use it)
- Install and check your version
- Basic syntax and mental model
- GET requests and query parameters
- POST/PUT/PATCH/DELETE requests
- Headers: Accept, Content-Type, custom headers
- Authentication: API keys, Bearer tokens, Basic auth, .netrc
- Sending JSON correctly (and safely)
- Uploading files (multipart) and raw uploads
- Downloading files reliably
- Redirects, cookies, and sessions
- Proxies and debugging
- TLS, certificates, and mTLS
- Using curl in scripts: exit codes, retries, timeouts
- Common mistakes (and fixes)
- FAQ
1. What cURL is (and when to use it)
cURL is a command-line client that sends requests and prints responses. It’s ideal when you want repeatable requests (in a shell history, script, CI job, or bug report) and you need full visibility into the raw HTTP exchange.
Use curl when you care about:
- Reproducibility: a single command you can paste into Slack or a ticket
- Observability: exact headers, status codes, redirect hops, TLS details
- Automation: scripting health checks, deploy hooks, smoke tests
2. Install and check your version
curl --version
On most Linux distributions and macOS, curl is already installed. On minimal containers, install it with your package manager:
# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y curl
# Alpine
apk add --no-cache curl
# Fedora
sudo dnf install -y curl
3. Basic syntax and mental model
The simplest curl request is just a URL:
curl https://example.com
Think of curl as:
- A URL (the destination)
- Flags (method, headers, auth, body, output)
| Goal | Flag(s) | Example |
|---|---|---|
| Choose a method | -X | curl -X POST https://api.example.com |
| Add a header | -H | curl -H 'Accept: application/json' https://api.example.com |
| Send request body | --data-raw | curl --data-raw 'a=1' https://api.example.com |
| Show status code | -w | curl -w '\n%{http_code}\n' https://example.com |
| Follow redirects | -L | curl -L https://example.com |
| Verbose debug | -v | curl -v https://example.com |
4. GET requests and query parameters
GET requests are the default:
curl 'https://api.example.com/v1/users?page=2&limit=50'
Use -G + --data-urlencode for safer query strings
Manually building query strings gets annoying when values contain spaces or special characters. Use -G to force query-string mode, and --data-urlencode to encode values:
curl -G 'https://api.example.com/search' \
--data-urlencode 'q=error budget' \
--data-urlencode 'sort=created_at desc'
5. POST/PUT/PATCH/DELETE requests
For APIs, you’ll commonly use:
POSTto createPUTorPATCHto updateDELETEto delete
Example:
curl -X DELETE 'https://api.example.com/v1/users/123'
--data/--data-raw, curl will switch the method to POST unless you explicitly force something else with -X.
6. Headers: Accept, Content-Type, custom headers
Add headers with -H. Use one flag per header:
curl 'https://api.example.com/v1/users' \
-H 'Accept: application/json' \
-H 'X-Request-Id: 123e4567-e89b-12d3-a456-426614174000'
When to set Accept vs Content-Type
Accept: what you want back (response format)Content-Type: what you are sending (request body format)
7. Authentication: API keys, Bearer tokens, Basic auth, .netrc
Bearer token (OAuth2 / JWT)
curl 'https://api.example.com/v1/me' \
-H 'Authorization: Bearer YOUR_TOKEN'
Basic auth
curl -u 'username:password' 'https://api.example.com/v1/me'
Keep secrets out of shell history
Three safer options:
- Use environment variables:
-H "Authorization: Bearer $TOKEN" - Use
curl -u username(curl prompts for password) - Use
~/.netrcfor Basic auth:curl -n https://api.example.com
8. Sending JSON correctly (and safely)
When sending JSON, set Content-Type: application/json and send the body with --data-raw:
curl -X POST 'https://api.example.com/v1/users' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data-raw '{"name":"Alice","role":"admin"}'
Send JSON from a file
curl -X POST 'https://api.example.com/v1/users' \
-H 'Content-Type: application/json' \
--data-binary @payload.json
jq to pretty-print JSON (see our jq guide) or paste it into the JSON Formatter tool.
9. Uploading files (multipart) and raw uploads
Multipart form upload with -F
curl -X POST 'https://api.example.com/v1/upload' \
-F 'file=@./report.pdf' \
-F 'project=alpha'
Raw file upload with -T (PUT)
curl -T ./backup.tar.gz 'https://storage.example.com/backups/backup.tar.gz'
10. Downloading files reliably
Use -O to save with the remote filename, or -o to set your own file name:
# remote filename
curl -O 'https://example.com/files/archive.zip'
# custom filename
curl -o archive.zip 'https://example.com/files/archive.zip'
Resume a partial download
curl -C - -O 'https://example.com/files/archive.zip'
11. Redirects, cookies, and sessions
Follow redirects
curl -L 'https://example.com'
Store and send cookies
# Save cookies to a jar
curl -c cookies.txt 'https://example.com/login'
# Send cookies from the jar
curl -b cookies.txt 'https://example.com/dashboard'
12. Proxies and debugging
Verbose mode
curl -v 'https://api.example.com/v1/health'
Show only response headers
curl -I 'https://api.example.com/v1/health'
Use a proxy
curl -x 'http://127.0.0.1:8080' 'https://example.com'
13. TLS, certificates, and mTLS
Inspect TLS without turning verification off
If you’re diagnosing TLS issues, start with -v. Only use -k as a last resort (testing only):
curl -v 'https://self-signed.example.internal'
# Testing only
curl -k 'https://self-signed.example.internal'
Mutual TLS (mTLS)
curl 'https://mtls.example.com' \
--cert client.crt \
--key client.key \
--cacert ca.crt
14. Using curl in scripts: exit codes, retries, timeouts
For scripts and CI, you usually want:
-sS(silent, but still show errors)--fail-with-body(treat 4xx/5xx as an error)--connect-timeoutand--max-time--retryfor transient failures
curl -sS --fail-with-body \
--connect-timeout 5 \
--max-time 20 \
--retry 3 \
--retry-delay 1 \
'https://api.example.com/v1/health'
Capture response body and status code
status=$(curl -sS --fail-with-body \
-o /tmp/resp.json \
-w '%{http_code}' \
'https://api.example.com/v1/users/123')
echo "HTTP $status"
cat /tmp/resp.json
15. Common mistakes (and fixes)
1) Getting 415 Unsupported Media Type
You likely forgot Content-Type: application/json for JSON bodies.
2) JSON parsing errors on the server
Shell quoting is a common culprit. If your JSON contains quotes or newlines, prefer sending from a file with --data-binary @payload.json.
3) “Why did curl send POST?”
Because you used -d/--data. Curl assumes POST when you provide a request body.
4) “Works in Postman but not in curl”
Compare the exact request: headers, auth, and body. Use -v in curl, and check that your API key is in the same header Postman uses.
FAQ
How do I convert a request to a curl command?
If you have a URL, headers, and a body, you can build a correct curl command with our cURL Command Builder and then tweak flags like -L, -v, and --fail-with-body.
How do I pretty-print JSON responses?
Pipe the output to jq (jq guide) or paste it into our JSON Formatter.