Before Postman, before Insomnia, there was curl. It’s still the fastest way to test an API, and it’s available on every server you’ll ever SSH into.
Basic Requests# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
# GET request
curl https://api.example.com/users
# With headers shown
curl -i https://api.example.com/users
# Headers only
curl -I https://api.example.com/users
# Silent (no progress bar)
curl -s https://api.example.com/users
# Follow redirects
curl -L https://api.example.com/old-endpoint
HTTP Methods# 1
2
3
4
5
6
7
8
9
10
11
# POST
curl -X POST https://api.example.com/users
# PUT
curl -X PUT https://api.example.com/users/123
# PATCH
curl -X PATCH https://api.example.com/users/123
# DELETE
curl -X DELETE https://api.example.com/users/123
Sending Data# JSON Body# 1
2
3
4
5
6
7
8
9
10
11
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 @user.json
# Pretty JSON with jq
curl -s https://api.example.com/users | jq .
1
2
3
4
5
6
7
8
# URL-encoded form
curl -X POST https://api.example.com/login \
-d "username=alice&password=secret"
# Multipart form (file upload)
curl -X POST https://api.example.com/upload \
-F "file=@photo.jpg" \
-F "description=My photo"
Query Parameters# 1
2
3
4
5
6
7
# In URL
curl "https://api.example.com/search?q=hello&limit=10"
# With --data-urlencode (safer)
curl -G https://api.example.com/search \
--data-urlencode "q=hello world" \
--data-urlencode "limit=10"
1
2
3
4
5
6
7
8
9
10
11
12
13
# Single header
curl -H "Authorization: Bearer token123" https://api.example.com/me
# Multiple headers
curl -H "Authorization: Bearer token123" \
-H "Accept: application/json" \
-H "X-Request-ID: abc123" \
https://api.example.com/me
# Common patterns
curl -H "Content-Type: application/json" ...
curl -H "Accept: application/json" ...
curl -H "Authorization: Basic $( echo -n 'user:pass' | base64) " ...
Authentication# Bearer Token# 1
curl -H "Authorization: Bearer eyJhbGc..." https://api.example.com/me
Basic Auth# 1
2
3
4
curl -u username:password https://api.example.com/secure
# Or manually
curl -H "Authorization: Basic $( echo -n 'user:pass' | base64) " https://api.example.com/secure
API Key# 1
2
3
4
5
# In header
curl -H "X-API-Key: your-api-key" https://api.example.com/data
# In query string
curl "https://api.example.com/data?api_key=your-api-key"
Response Handling# Save to File# 1
2
3
4
5
6
7
8
# Save response body
curl -o response.json https://api.example.com/data
# Save with remote filename
curl -O https://example.com/file.zip
# Save headers and body separately
curl -D headers.txt -o body.json https://api.example.com/data
1
2
3
4
5
6
7
8
9
# HTTP status code only
curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health
# Response time
curl -s -o /dev/null -w "%{time_total}" https://api.example.com/health
# Multiple metrics
curl -s -o /dev/null -w "Status: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\n" \
https://api.example.com/data
Write-Out Variables# 1
2
3
4
5
6
7
8
9
curl -w "
HTTP Code: %{http_code}
Total Time: %{time_total}s
DNS Lookup: %{time_namelookup}s
Connect: %{time_connect}s
TTFB: %{time_starttransfer}s
Download Size: %{size_download} bytes
Download Speed: %{speed_download} bytes/sec
" -o /dev/null -s https://api.example.com/data
Debugging# Verbose Output# 1
2
3
4
5
6
7
8
# Show request and response headers
curl -v https://api.example.com/data
# Even more verbose (includes TLS handshake)
curl -vv https://api.example.com/data
# Trace everything (hex dump)
curl --trace - https://api.example.com/data
Debug TLS/SSL# 1
2
3
4
5
6
7
8
9
10
11
# Show certificate info
curl -vI https://api.example.com 2>& 1 | grep -A 10 "Server certificate"
# Skip certificate verification (don't use in production!)
curl -k https://self-signed.example.com/data
# Use specific CA cert
curl --cacert /path/to/ca.crt https://api.example.com/data
# Client certificate auth
curl --cert client.crt --key client.key https://api.example.com/data
Timeouts and Retries# 1
2
3
4
5
6
7
8
9
10
11
# Connection timeout (seconds)
curl --connect-timeout 5 https://api.example.com/data
# Max time for entire operation
curl --max-time 30 https://api.example.com/data
# Retry on failure
curl --retry 3 --retry-delay 2 https://api.example.com/data
# Retry only on specific errors
curl --retry 3 --retry-connrefused https://api.example.com/data
Cookies# 1
2
3
4
5
6
7
8
9
10
11
# Send cookie
curl -b "session=abc123" https://api.example.com/dashboard
# Save cookies to file
curl -c cookies.txt https://api.example.com/login -d "user=alice&pass=secret"
# Use saved cookies
curl -b cookies.txt https://api.example.com/dashboard
# Both save and send
curl -b cookies.txt -c cookies.txt https://api.example.com/page
Proxies# 1
2
3
4
5
6
7
8
# HTTP proxy
curl -x http://proxy.example.com:8080 https://api.example.com/data
# SOCKS5 proxy
curl --socks5 localhost:1080 https://api.example.com/data
# Proxy with auth
curl -x http://user:pass@proxy.example.com:8080 https://api.example.com/data
Practical Examples# Health Check Script# 1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
URL = "https://api.example.com/health"
STATUS = $( curl -s -o /dev/null -w "%{http_code}" --max-time 5 " $URL " )
if [ " $STATUS " = "200" ] ; then
echo "OK"
exit 0
else
echo "FAIL: HTTP $STATUS "
exit 1
fi
API Test with Error Handling# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
response = $( curl -s -w "\n%{http_code}" \
-X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Test User"}' )
body = $( echo " $response " | head -n -1)
status = $( echo " $response " | tail -n 1)
if [ " $status " = "201" ] ; then
echo "Created user: $( echo " $body " | jq -r '.id' ) "
else
echo "Error $status : $( echo " $body " | jq -r '.error' ) "
exit 1
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
page = 1
while true; do
response = $( curl -s "https://api.example.com/items?page= $page &per_page=100" )
count = $( echo " $response " | jq '.items | length' )
if [ " $count " = "0" ] ; then
break
fi
echo " $response " | jq -r '.items[].name'
(( page++))
done
OAuth Token Flow# 1
2
3
4
5
6
7
8
9
# Get token
TOKEN = $( curl -s -X POST https://auth.example.com/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id= $CLIENT_ID " \
-d "client_secret= $CLIENT_SECRET " \
| jq -r '.access_token' )
# Use token
curl -H "Authorization: Bearer $TOKEN " https://api.example.com/data
Config Files# Save common options in ~/.curlrc:
# - - s - - H D m r e a e " f x t A a - r c u t y c l i e t m 2 p e t o : p 3 t 0 a i p o p n l s i c a t i o n / j s o n " Or use command-line config:
1
curl -K myconfig.txt https://api.example.com/data
Quick Reference# Option Purpose -X METHODHTTP method -H "Header: Value"Add header -d "data"Send data (POST) -d @fileSend data from file -F "field=value"Multipart form -u user:passBasic auth -o fileSave output -sSilent mode -vVerbose -iInclude headers -LFollow redirects -kSkip TLS verify --max-time NTimeout -w "format"Custom output
curl is one of those tools that rewards memorization. The more commands you know by heart, the faster you can debug APIs. Start with -X, -H, -d, and -s — those four cover most API testing scenarios.
📬 Get the Newsletter Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.