Introduction
curl (Client URL) is the Swiss Army knife of HTTP requests from the command line. Whether you’re testing APIs, downloading files, automating workflows, or debugging web services, curl is the tool millions of developers rely on daily. This guide provides everything you need to master curl, from basic requests to advanced techniques.
Core Concepts & Terminology 📚
What is CURL?
CURL stands for Client URL—it’s a command-line tool and library for transferring data using URLs. It supports multiple protocols but is primarily used for HTTP/HTTPS requests.
Key characteristics:
- Protocol-agnostic: Works with HTTP, HTTPS, FTP, SFTP, SCP, LDAP, and more
- Scriptable: Perfect for automation and CI/CD pipelines
- Lightweight: Available on nearly all Unix-like systems
- Powerful: Handles authentication, cookies, redirects, proxies, and more
HTTP Fundamentals Refresher
Understanding HTTP is crucial for mastering curl:
| Concept | Explanation |
|---|---|
| HTTP Request | A message sent by the client (your curl command) to the server |
| HTTP Response | The server’s reply, containing status code, headers, and body |
| HTTP Methods | GET (retrieve), POST (submit), PUT (update), DELETE (remove), PATCH (partial update), HEAD (headers only), OPTIONS (allowed methods) |
| Status Code | 3-digit number indicating result: 2xx (success), 3xx (redirect), 4xx (client error), 5xx (server error) |
| Headers | Metadata about the request/response (Content-Type, User-Agent, Authorization, etc.) |
| Body | The actual data being sent (POST/PUT) or received |
Important Abbreviations
| Abbreviation | Full Name | Meaning |
|---|---|---|
| HTTP | HyperText Transfer Protocol | Unencrypted web communication (legacy) |
| HTTPS | HyperText Transfer Protocol Secure | Encrypted web communication (current standard) |
| REST | Representational State Transfer | Architecture style for web APIs |
| JSON | JavaScript Object Notation | Lightweight data format (most common for APIs) |
| XML | eXtensible Markup Language | Alternative data format (older APIs) |
| URL | Uniform Resource Locator | Web address (e.g., https://example.com/api/users) |
| Query String | Parameters in URL | Data after ? (e.g., ?name=John&age=30) |
| SSL/TLS | Secure Sockets Layer / Transport Layer Security | Encryption protocols for HTTPS |
| Proxy | Intermediary server | Routes requests through another server |
| Redirect | HTTP 3xx response | Server tells client to go to a different URL |
| CORS | Cross-Origin Resource Sharing | Browser security policy for cross-domain requests |
Architecture: How CURL Works 🏗️
Here’s a simplified view of how curl makes a request:
┌──────────────┐
│ Your Device │
│ (Terminal) │
└──────┬───────┘
│
│ curl command
│ (DNS lookup → TCP connection → TLS handshake → HTTP request)
▼
┌──────────────────────┐
│ Internet/Network │
│ (with possible │
│ proxies, firewalls)│
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ Web Server │
│ (listens on :80 │
│ or :443) │
└──────┬───────────────┘
│
│ HTTP Response
│ (headers + body)
▼
┌──────────────────────┐
│ curl parses │
│ - Status code │
│ - Response headers │
│ - Response body │
└──────────────────────┘
Basic HTTP Methods & Examples 🌐
1. GET Request (Retrieve Data)
GET is the default method—curl sends it automatically without the -X flag.
# Simple GET request
curl https://api.github.com/users/github
# GET with query parameters
curl "https://api.example.com/search?q=nodejs&limit=10"
# GET with custom headers
curl -H "Authorization: Bearer YOUR_TOKEN" https://api.example.com/me
# GET with verbose output (shows all details)
curl -v https://example.com
# GET and save to file
curl https://example.com/data.json -o data.json
What it retrieves: Status code, response headers, response body
Real-world use: Fetching API data, checking if a server is online, downloading files
2. POST Request (Submit Data)
POST sends data to the server. Use -X POST to specify the method.
# Simple POST with form data
curl -X POST -d "username=john&password=secret" https://example.com/login
# POST with JSON data
curl -X POST \
-H "Content-Type: application/json" \
-d '{"username":"john","email":"[email protected]"}' \
https://api.example.com/users
# POST with data from a file
curl -X POST -d @data.json https://api.example.com/upload
# POST with multiple files
curl -F "[email protected]" -F "description=My photo" https://example.com/upload
What it sends: Request body (form data, JSON, files)
Real-world use: Creating new records, logging in, submitting forms, uploading files
3. PUT Request (Update Entire Resource)
PUT replaces an entire resource at a given URL.
# PUT with JSON (replaces entire resource)
curl -X PUT \
-H "Content-Type: application/json" \
-d '{"username":"john_updated","email":"[email protected]"}' \
https://api.example.com/users/123
# PUT with data from file
curl -X PUT -d @updated.json https://api.example.com/users/123
Key difference from POST: PUT is idempotent (multiple identical requests have same effect as one)
Real-world use: Updating user profiles, replacing configuration files
4. PATCH Request (Partial Update)
PATCH updates only specified fields, leaving others unchanged.
# PATCH to update only email (username stays same)
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
https://api.example.com/users/123
# Useful for large objects where you only want to change one field
Real-world use: Updating individual user fields, toggling a feature flag
5. DELETE Request (Remove Resource)
# Simple DELETE
curl -X DELETE https://api.example.com/users/123
# DELETE with authorization header
curl -X DELETE \
-H "Authorization: Bearer TOKEN" \
https://api.example.com/posts/456
Real-world use: Deleting user accounts, removing data, cleaning up resources
Essential Curl Options & Flags 🔧
Headers Management
# View response headers
curl -i https://example.com # Shows status line + headers + body
# Only show response headers
curl -I https://example.com
# Add custom header
curl -H "X-API-Key: abc123" https://api.example.com
# Multiple headers
curl -H "X-API-Key: abc123" -H "Accept: application/json" https://api.example.com
# View request headers being sent (verbose output)
curl -v https://example.com 2>&1 | grep ">"
Authentication
# Basic authentication (username:password)
curl -u "username:password" https://api.example.com/protected
# Bearer token (for OAuth/JWT)
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." https://api.example.com
# API Key in header
curl -H "X-API-Key: your-api-key-here" https://api.example.com
# API Key in query parameter
curl "https://api.example.com/data?apiKey=your-api-key-here"
Data Management
# Send form data
curl -d "param1=value1¶m2=value2" https://example.com
# Send JSON
curl -H "Content-Type: application/json" -d '{"key":"value"}' https://api.example.com
# Send data from file
curl -d @file.txt https://example.com
# URL-encode data automatically
curl --data-urlencode "message=Hello World" https://example.com
Output Control
# Save to file with original name
curl -O https://example.com/document.pdf
# Save to file with custom name
curl -o myfile.pdf https://example.com/document.pdf
# Append to file instead of overwriting
curl https://example.com/data >> data.json
# Show only response body (default behavior)
curl https://example.com
# Show only response headers
curl -I https://example.com
# Pretty-print JSON (pipe to jq)
curl https://api.example.com/users | jq .
Advanced Techniques 🚀
1. Handling Redirects
# Follow redirects automatically
curl -L https://short-url.com/abc123
# Follow redirects and show which URLs it visited
curl -L -v https://short-url.com/abc123
# Limit maximum redirects (default is unlimited)
curl -L --max-redirs 5 https://example.com
Why it matters: Many URLs redirect (301, 302, 303, 307, 308). Without -L, curl stops at the redirect and shows the response.
2. Working with Cookies
# Save cookies from response
curl https://example.com -c cookies.txt
# Use saved cookies in next request
curl https://example.com -b cookies.txt
# Send specific cookie value
curl -b "session_id=abc123def456" https://example.com
# Combine: save and send cookies in session
curl -c cookies.txt -b cookies.txt https://example.com/page1
curl -b cookies.txt https://example.com/page2
Real-world use: Maintaining login sessions across multiple requests
3. Proxy Requests
# HTTP proxy
curl -x "http://proxy.company.com:8080" https://example.com
# SOCKS5 proxy (with authentication)
curl -x "socks5://user:pass@localhost:1080" http://remote-site.com
# SOCKS5 proxy (alternative syntax)
curl --socks5 "user:pass@localhost:1080" http://remote-site.com
# Proxy with specific user/password
curl -x "http://proxy.company.com:8080" \
-U "proxyuser:proxypass" \
https://example.com
When to use: Behind corporate firewalls, anonymity, accessing geo-restricted content
4. Timeouts & Retries
# Connection timeout (seconds to establish connection)
curl --connect-timeout 10 https://slow-server.com
# Maximum time for entire operation
curl -m 30 https://example.com # max 30 seconds total
# Combined: connect in 5 sec, total operation 30 sec
curl --connect-timeout 5 -m 30 https://example.com
# Retry on transient errors (not built-in, use wrapper)
# Better approach: use a bash loop
for i in {1..3}; do
curl https://example.com && break
sleep 2
done
5. SSL/TLS & Certificate Handling
# Disable SSL certificate verification (NOT recommended in production!)
curl -k https://self-signed-cert.example.com
# Use specific certificate
curl --cert /path/to/cert.pem https://example.com
# Use certificate with key
curl --cert /path/to/cert.pem --key /path/to/key.pem https://example.com
# Specify CA bundle for verification
curl --cacert /path/to/ca-bundle.crt https://example.com
# Show SSL/TLS details
curl -v https://example.com 2>&1 | grep -A 20 "SSL"
6. Request/Response Inspection
# Verbose output (shows entire exchange)
curl -v https://example.com
# More verbose (includes data transfer)
curl -vv https://example.com
# Show only headers, not body
curl -i https://example.com
# Show request being sent (useful for debugging)
curl -v https://example.com 2>&1 | grep ">"
# Save all headers to file
curl -D headers.txt https://example.com
# Trace request (extremely verbose)
curl --trace trace.log https://example.com
7. Working with APIs: Real-World Examples
Example: GitHub API
# Get user information
curl https://api.github.com/users/linus
# Get user repositories (with pagination)
curl "https://api.github.com/users/linus/repos?page=1&per_page=10"
# Create a repository (requires authentication)
curl -X POST \
-H "Authorization: token YOUR_GITHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "my-new-repo",
"description": "My new repository",
"private": false
}' \
https://api.github.com/user/repos
Example: OpenWeather API
# Get weather for a city
curl "https://api.openweathermap.org/data/2.5/weather?q=London&appid=YOUR_API_KEY"
# Parse JSON result with jq
curl -s "https://api.openweathermap.org/data/2.5/weather?q=London&appid=KEY" | jq '.main.temp'
Example: Creating a Resource on Custom API
# Complete example with multiple options
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGc..." \
-d '{
"title": "New Article",
"content": "Article content here",
"tags": ["curl", "api"]
}' \
-v \
https://api.example.com/articles
Common Pitfalls & Best Practices ⚠️
Pitfall #1: Forgetting Quotes for URLs with Parameters
❌ Wrong:
curl https://api.example.com/search?q=hello&page=1
# & is interpreted as background job operator
✅ Right:
curl "https://api.example.com/search?q=hello&page=1"
# or escape the &:
curl https://api.example.com/search?q=hello\&page=1
Pitfall #2: Not Setting Content-Type for JSON
❌ Wrong:
curl -X POST -d '{"name":"John"}' https://api.example.com/users
# Server might treat this as form data, not JSON
✅ Right:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"John"}' \
https://api.example.com/users
Pitfall #3: Storing Credentials in Plain Text
❌ Wrong:
curl -u "admin:password123" https://api.example.com
# Password visible in bash history!
✅ Better:
# Use environment variables
curl -u "$API_USER:$API_PASSWORD" https://api.example.com
# Or use token from file
TOKEN=$(cat ~/.api-token)
curl -H "Authorization: Bearer $TOKEN" https://api.example.com
# Or use -u without password (curl will prompt)
curl -u "admin" https://api.example.com # Will ask for password
Pitfall #4: Ignoring SSL Certificate Errors
❌ Wrong:
curl -k https://self-signed.example.com # In production!
# This disables all security checks
✅ Right:
# Get proper certificate OR add to trusted store
curl --cacert /path/to/cert.pem https://self-signed.example.com
# If self-signed certificate in development only:
curl -k https://self-signed-dev.example.com # Only in dev!
Pitfall #5: Not Handling Redirects
❌ Wrong:
curl https://short.url/abc123
# Returns 301/302, shows HTML redirect page
✅ Right:
curl -L https://short.url/abc123
# Automatically follows redirects to final destination
Best Practices ✅
1. Always Quote URLs with Special Characters
curl "https://example.com/search?q=hello world&filter=recent"
2. Use -s (Silent) in Scripts
# Don't show progress bar in scripts
curl -s https://api.example.com/data | jq .
3. Use -S (Show Errors Even in Silent Mode)
# Silent, but still show errors
curl -sS https://api.example.com/data
4. Set User-Agent for Better Compatibility
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" https://example.com
# Some servers reject requests without User-Agent
5. Handle HTTP Status Codes in Scripts
# Get HTTP status code
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.example.com)
if [ $STATUS -eq 200 ]; then
echo "Success"
else
echo "Failed with status $STATUS"
fi
6. Pretty-Print JSON Responses
# Use jq for formatting
curl -s https://api.example.com/data | jq '.'
# Or use Python for on-demand pretty-printing
curl -s https://api.example.com/data | python -m json.tool
7. Save Both Headers and Body
# Headers to file, body to stdout
curl -i https://example.com > response.txt
# Or separate them
curl -D headers.txt -o body.txt https://example.com
8. Retry on Failure (Bash Script)
#!/bin/bash
retry_count=0
max_retries=3
while [ $retry_count -lt $max_retries ]; do
if curl -s https://api.example.com; then
echo "Success"
exit 0
fi
retry_count=$((retry_count + 1))
echo "Attempt $retry_count failed, retrying..."
sleep 2
done
echo "Failed after $max_retries attempts"
exit 1
Debugging & Troubleshooting 🔍
Get Detailed Request/Response Information
# Full verbose output
curl -v https://example.com 2>&1
# Very verbose (includes data hex dumps)
curl -vv https://example.com 2>&1
# Save trace log
curl --trace-ascii debug.txt https://example.com
# Show just request headers sent by curl
curl -v https://example.com 2>&1 | grep ">"
# Show just response headers
curl -v https://example.com 2>&1 | grep "<"
Check Response Time
# Get timing breakdown
curl -w "@curl-format.txt" -o /dev/null -s https://example.com
# Create curl-format.txt with:
# time_namelookup: %{time_namelookup}\n
# time_connect: %{time_connect}\n
# time_appconnect: %{time_appconnect}\n
# time_pretransfer: %{time_pretransfer}\n
# time_redirect: %{time_redirect}\n
# time_starttransfer: %{time_starttransfer}\n
# ---
# time_total: %{time_total}\n
# Or simpler version
curl -w "Response time: %{time_total}s\n" -o /dev/null -s https://example.com
Test Connection Without Making Request
# Just connect, don't send request
curl -v --head https://example.com
# Or only show headers
curl -I https://example.com
CURL vs. Alternatives 🔄
Pros & Cons: CURL
Pros ✅
- Available on nearly all Unix/Linux systems
- Extremely lightweight and fast
- Rich feature set (proxies, authentication, cookies, redirects)
- Perfect for scripts and automation
- Handles multiple protocols (HTTP, HTTPS, FTP, etc.)
- Easy to learn; simple one-liner commands
Cons ❌
- Command-line interface can be verbose for complex requests
- JSON formatting requires piping to external tools (jq)
- No built-in request templating/collection management
When to Use Alternatives
| Alternative | Use Case | Pros |
|---|---|---|
| wget | Downloading files, recursive downloads | Better at file downloads; recursive website mirroring |
| Postman | API testing, team collaboration | GUI; request collections; environment variables; testing |
| Insomnia | API testing; similar to Postman | Lightweight; modern UI; free |
| httpie | Human-friendly HTTP requests | Prettier output; intuitive syntax; auto JSON formatting |
| Python requests | Scripting, automation | More readable code; better for complex logic |
| Node.js axios/fetch | Web development, automation | Native to JavaScript ecosystem |
Quick Comparison Table
| Tool | Use Case | Learning Curve | Speed |
|---|---|---|---|
| curl | Scripts, one-liners, CI/CD | Easy | Very Fast |
| wget | File downloads | Easy | Very Fast |
| httpie | Quick API testing | Easy | Fast |
| Postman | Team API testing | Medium | Medium |
| Python requests | Complex automation | Medium | Depends on script |
Useful Resources & References 📖
Official Documentation
- CURL Official Manual: https://curl.se/docs/manpage.html
- CURL Tutorials: https://curl.se/docs/manual.html
- CURL Source Code: https://github.com/curl/curl
Learning Resources
- CURL by Example: https://catonmat.net/curl-by-example
- httpbin.org: https://httpbin.org/ — Great for testing curl requests
- RequestBin/Webhook.site: Inspect HTTP requests you send
- Linux Man Pages:
man curl
Related Tools & Technologies
JSON Processing:
- jq: https://stedolan.github.io/jq/ — JSON query and transform tool
- Python json.tool: Built-in pretty-printer for JSON
API Development & Testing:
- Postman: https://www.postman.com/
- Insomnia: https://insomnia.rest/
- Swagger/OpenAPI: https://swagger.io/ — API documentation standard
Bash/Shell Scripting Resources:
- ShellCheck: https://www.shellcheck.net/ — Finds bugs in shell scripts
- Bash Guide: https://mywiki.wooledge.org/BashGuide
CI/CD Integration:
- GitHub Actions: Use curl in workflow files
- Jenkins: curl plugins and integration
- GitLab CI: curl in pipelines
Books & Deep Dives
- “HTTP: The Definitive Guide” — Comprehensive HTTP reference
- “RESTful Web Services” — Understanding REST API design
Advanced Architecture: Integrating CURL in Workflows 🏗️
Typical CI/CD Pipeline with CURL
┌──────────────┐
│ Git Commit │
└──────┬───────┘
│
▼
┌──────────────────┐
│ CI/CD System │ (GitHub Actions, Jenkins, GitLab CI)
│ (runs on trigger)│
└──────┬───────────┘
│
▼
┌──────────────────────────────┐
│ Build & Test Phase │
│ curl -v http://localhost:3000 │ (check if service is ready)
└──────┬───────────────────────┘
│
▼
┌──────────────────────────────┐
│ API Integration Tests │
│ curl -X POST ... -d @test.json│ (verify API responses)
└──────┬───────────────────────┘
│
▼
┌──────────────────────────────┐
│ Deploy to Production │
│ curl -X POST -H "Auth: ..." .│ (trigger deployment API)
└──────┬───────────────────────┘
│
▼
┌──────────────────────────────┐
│ Post-Deploy Health Check │
│ curl -I https://example.com │ (verify endpoint is up)
└──────────────────────────────┘
Real-World Scenarios 🎯
Scenario 1: Monitor API Availability
#!/bin/bash
# Check if API is up every 60 seconds
while true; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health)
if [ $STATUS -eq 200 ]; then
echo "✓ API is up ($(date))"
else
echo "✗ API DOWN - Status: $STATUS ($(date))"
# Send alert
mail -s "API Down Alert" [email protected] <<< "API returned status $STATUS"
fi
sleep 60
done
Scenario 2: Batch Process API Requests
#!/bin/bash
# Process list of user IDs and fetch their data
USERS="user1 user2 user3 user4 user5"
for user in $USERS; do
echo "Fetching data for $user..."
curl -s \
-H "Authorization: Bearer $API_TOKEN" \
"https://api.example.com/users/$user" | jq '.'
# Add delay to avoid rate limiting
sleep 1
done
Scenario 3: Upload File with Progress
#!/bin/bash
# Upload file with progress tracking
curl -F "[email protected]" \
-F "description=Backup file" \
--progress-bar \
https://upload.example.com/files
Scenario 4: Conditional API Request Based on Response
#!/bin/bash
# Check if resource exists, create if not
RESPONSE=$(curl -s -H "Authorization: Bearer $TOKEN" \
"https://api.example.com/resources/my-resource")
if echo "$RESPONSE" | grep -q "not found"; then
echo "Resource doesn't exist, creating..."
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"my-resource"}' \
https://api.example.com/resources
else
echo "Resource already exists"
echo "$RESPONSE" | jq '.'
fi
Summary & Quick Reference 🎯
Most Common curl Commands
# GET request
curl https://example.com
# POST with JSON
curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' https://api.example.com
# Show headers
curl -i https://example.com
# Follow redirects
curl -L https://short.url/abc
# With authentication
curl -u "username:password" https://api.example.com
# Save to file
curl https://example.com -o output.html
# Through proxy
curl -x "socks5://user:pass@localhost:1080" https://example.com
# Verbose mode (debugging)
curl -v https://example.com
# With custom headers
curl -H "X-API-Key: abc123" https://api.example.com
Conclusion
Mastering curl unlocks powerful automation capabilities. Whether you’re testing APIs, monitoring services, or building CI/CD pipelines, curl is an indispensable tool in any developer’s toolkit. The combinations are virtually unlimited—start with the basics and build up your expertise through experimentation.
Next step: Pick a public API (GitHub, OpenWeather, etc.) and experiment with curl commands. Practice builds muscle memory!
See Also:
Comments