Every developer ends up using curl. It’s installed everywhere, works with any API, and once you learn the patterns, it’s faster than any GUI tool.
Basic Requests#
1
2
3
4
5
6
7
8
9
10
11
| # GET (default)
curl https://api.example.com/users
# POST with data
curl -X POST https://api.example.com/users \
-d '{"name":"Alice"}'
# Other methods
curl -X PUT ...
curl -X DELETE ...
curl -X PATCH ...
|
1
2
3
4
5
6
7
8
9
10
| # Set content type
curl -H "Content-Type: application/json" ...
# Auth header
curl -H "Authorization: Bearer TOKEN" ...
# Multiple headers
curl -H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "X-Custom-Header: value" ...
|
JSON Data#
1
2
3
4
5
6
7
8
9
10
11
12
| # Inline JSON
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
# From file
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d @data.json
# Pretty output (pipe to jq)
curl -s https://api.example.com/users | jq
|
Response Info#
1
2
3
4
5
6
7
8
9
10
11
| # Show response headers
curl -i https://api.example.com/users
# Only headers (no body)
curl -I https://api.example.com/users
# HTTP status code only
curl -s -o /dev/null -w "%{http_code}" https://api.example.com/users
# Timing info
curl -w "Time: %{time_total}s\n" -o /dev/null -s https://api.example.com
|
Authentication#
1
2
3
4
5
6
7
8
9
10
11
| # Basic auth
curl -u username:password https://api.example.com
# Bearer token
curl -H "Authorization: Bearer eyJ..." https://api.example.com
# API key in header
curl -H "X-API-Key: abc123" https://api.example.com
# API key in query
curl "https://api.example.com?api_key=abc123"
|
1
2
3
4
5
6
7
8
| # URL-encoded form
curl -X POST https://example.com/login \
-d "username=alice&password=secret"
# Multipart form (file upload)
curl -X POST https://example.com/upload \
-F "file=@photo.jpg" \
-F "description=My photo"
|
Following Redirects#
1
2
3
4
5
| # Follow redirects (3xx)
curl -L https://example.com/redirect
# Show redirect chain
curl -L -v https://example.com/redirect 2>&1 | grep "< HTTP\|< Location"
|
SSL/TLS#
1
2
3
4
5
6
7
8
| # Ignore SSL errors (dev only!)
curl -k https://self-signed.example.com
# Use specific cert
curl --cacert /path/to/ca.crt https://api.example.com
# Client cert authentication
curl --cert client.crt --key client.key https://api.example.com
|
Timeouts#
1
2
3
4
5
6
7
8
| # Connection timeout (seconds)
curl --connect-timeout 5 https://api.example.com
# Max time for entire operation
curl --max-time 30 https://api.example.com
# Both
curl --connect-timeout 5 --max-time 30 https://api.example.com
|
Debugging#
1
2
3
4
5
6
7
8
| # Verbose output
curl -v https://api.example.com
# Even more verbose (includes SSL handshake)
curl -vv https://api.example.com
# Trace everything to file
curl --trace trace.log https://api.example.com
|
Saving Output#
1
2
3
4
5
6
7
8
| # Save to file
curl -o response.json https://api.example.com/data
# Save with remote filename
curl -O https://example.com/file.zip
# Silent mode (no progress)
curl -s https://api.example.com
|
Cookies#
1
2
3
4
5
6
7
8
| # Send cookie
curl -b "session=abc123" https://example.com
# Save cookies to file
curl -c cookies.txt https://example.com/login -d "user=alice"
# Send cookies from file
curl -b cookies.txt https://example.com/dashboard
|
Retry Logic#
1
2
3
4
5
6
7
8
| # Retry on failure
curl --retry 3 https://api.example.com
# Retry with delay
curl --retry 3 --retry-delay 2 https://api.example.com
# Retry on specific errors
curl --retry 3 --retry-connrefused https://api.example.com
|
Useful Aliases#
Add to your .bashrc:
1
2
3
4
5
6
7
8
| # JSON POST
alias curlj='curl -H "Content-Type: application/json"'
# Pretty JSON output
alias curljq='curl -s | jq'
# Show just status code
alias curlstatus='curl -s -o /dev/null -w "%{http_code}\n"'
|
Real-World Examples#
Test API endpoint:
1
2
| curl -s -X GET "https://api.example.com/users/123" \
-H "Authorization: Bearer $TOKEN" | jq
|
Create resource:
1
2
3
4
| curl -s -X POST "https://api.example.com/users" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"Alice","email":"alice@example.com"}' | jq
|
Upload file:
1
2
3
4
| curl -X POST "https://api.example.com/upload" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@report.pdf" \
-F "folder=documents"
|
Health check script:
1
2
3
4
5
6
| #!/bin/bash
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health)
if [ "$STATUS" != "200" ]; then
echo "API is down! Status: $STATUS"
exit 1
fi
|
Measure response time:
1
2
3
4
5
6
7
| curl -s -o /dev/null -w "
DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
Start: %{time_starttransfer}s
Total: %{time_total}s
" https://api.example.com
|
Download with progress:
1
| curl -# -O https://example.com/large-file.zip
|
Quick Reference#
| Task | Command |
|---|
| GET request | curl URL |
| POST JSON | curl -X POST -H "Content-Type: application/json" -d '{}' URL |
| Auth header | curl -H "Authorization: Bearer TOKEN" URL |
| Basic auth | curl -u user:pass URL |
| Show headers | curl -i URL |
| Follow redirects | curl -L URL |
| Silent + jq | curl -s URL | jq |
| Status code only | curl -s -o /dev/null -w "%{http_code}" URL |
| Upload file | curl -F "file=@path" URL |
| Save response | curl -o file.json URL |
curl does one thing well: talk HTTP. Master these patterns and you’ll debug APIs faster than any GUI tool.
📬 Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.