Skip to content
Home
cURL: A Complete Guide with Practical Examples

cURL: A Complete Guide with Practical Examples

Developer Tools Developer Tools 7 min read 1455 words Beginner ExcellentWiki Editorial Team

cURL is a command-line tool for transferring data with URLs. It supports HTTP, HTTPS, FTP, SFTP, LDAP, and dozens of other protocols. For developers, cURL is the most common tool for testing APIs, debugging web services, and automating data transfers.

Basic Usage

# Simple GET request
curl https://api.example.com/users

# Include response headers
curl -i https://api.example.com/users

# Only response headers (no body)
curl -I https://api.example.com/users

# Silent mode (no progress meter, no errors)
curl -s https://api.example.com/users

# Silent with error output
curl -sS https://api.example.com/users

# Follow redirects
curl -L http://example.com

The -i flag is useful for debugging — it shows both the HTTP status code and headers alongside the response body. -I sends a HEAD request, which is useful for checking if a resource exists without downloading it.

HTTP Methods

# GET (default)
curl https://api.example.com/users

# POST with JSON body
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com"}'

# PUT (update)
curl -X PUT https://api.example.com/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated"}'

# PATCH (partial update)
curl -X PATCH https://api.example.com/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email":"new@example.com"}'

# DELETE
curl -X DELETE https://api.example.com/users/1

The -d flag sends data in the request body and automatically sets Content-Type: application/x-www-form-urlencoded unless you override it with -H. Always set Content-Type explicitly when sending JSON.

Headers and Authentication

Custom Headers

# Single header
curl -H "Authorization: Bearer token123" https://api.example.com/users

# Multiple headers
curl -H "Authorization: Bearer token123" \
  -H "Accept: application/json" \
  -H "X-Custom-Header: value" \
  https://api.example.com/users

# User agent
curl -A "Mozilla/5.0 (X11; Linux x86_64)" https://example.com

Authentication Methods

# Basic auth (username:password)
curl -u alice:secret https://api.example.com/protected

# Bearer token (alternative to -H)
# Not built-in, use -H instead

# Digest auth
curl --digest -u alice:secret https://api.example.com/digest

# OAuth 2.0 with bearer token in query param
curl "https://api.example.com/data?access_token=token123"

For Basic auth, -u sends the credentials as an HTTP header encoded in Base64. This is not encrypted — always use HTTPS with Basic auth, never HTTP.

URL Parameters

# Query parameters (use -G with -d)
curl -G https://api.example.com/users \
  -d "page=1" \
  -d "limit=10" \
  -d "sort=name"

# URL encoding is automatic
curl -G https://api.example.com/search \
  --data-urlencode "q=hello world"

# Manual URL (not recommended)
curl "https://api.example.com/users?page=1&limit=10"

Using -G with -d is cleaner than building URLs manually. The --data-urlencode flag properly encodes special characters, which is essential for search queries and user-supplied input.

File Uploads

# Upload a file (multipart/form-data)
curl -F "file=@photo.jpg" https://api.example.com/upload

# Multiple fields
curl -F "file=@photo.jpg" \
  -F "description=Vacation photo" \
  -F "album=summer" \
  https://api.example.com/upload

# Upload with different filename
curl -F "file=@photo.jpg;filename=profile.jpg" \
  https://api.example.com/upload

# Binary data upload
curl --data-binary "@data.bin" https://api.example.com/ingest

The -F flag automatically sets Content-Type: multipart/form-data with proper boundaries. For simple file uploads, this is all you need. The server sees the file with its original filename unless you override it with ;filename=.

File Downloads

# Download and save with original filename
curl -O https://example.com/file.zip

# Save with custom filename
curl -o output.zip https://example.com/file.zip

# Resume download
curl -C - -O https://example.com/large-file.zip

# Download multiple files
curl -O https://example.com/file1.zip \
  -O https://example.com/file2.zip

# Limit download rate (1 MB/s)
curl --limit-rate 1M -O https://example.com/large-file.zip

Debugging and Verbose Output

# Verbose mode (shows request and response headers)
curl -v https://api.example.com/users

# Very verbose (includes SSL/TLS details)
curl --trace-ascii trace.txt https://api.example.com/users

# Full trace with hex dump
curl --trace trace.txt https://api.example.com/users

# Show timing breakdown
curl -w "\nTime: %{time_total}s\n" https://api.example.com/users

# Comprehensive timing
curl -w "\n\
  DNS: %{time_namelookup}s\n\
  Connect: %{time_connect}s\n\
  TLS: %{time_appconnect}s\n\
  TTFB: %{time_starttransfer}s\n\
  Total: %{time_total}s\n" \
  https://api.example.com/users

The -v flag is your primary debugging tool. It shows every header sent and received, the SSL certificate chain, and any redirects. For deeper debugging, --trace-ascii captures the full request and response including binary data.

REST API Testing Workflow

Full API Test Script

#!/bin/bash

BASE="https://api.example.com"
TOKEN="your-token-here"

# 1. Health check
echo "=== Health Check ==="
curl -sS "$BASE/health" | jq .

# 2. List users
echo "=== List Users ==="
curl -sS -H "Authorization: Bearer $TOKEN" "$BASE/users" | jq .

# 3. Create user
echo "=== Create User ==="
NEW_USER=$(curl -sS -X POST "$BASE/users" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Test User","email":"test@example.com"}')
echo "$NEW_USER" | jq .
USER_ID=$(echo "$NEW_USER" | jq -r '.id')

# 4. Update user
echo "=== Update User ==="
curl -sS -X PUT "$BASE/users/$USER_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Updated Name"}' | jq .

# 5. Delete user
echo "=== Delete User ==="
curl -sS -X DELETE "$BASE/users/$USER_ID" \
  -H "Authorization: Bearer $TOKEN"
echo ""

Cookie-Based Authentication

# Login and save cookies
curl -c cookies.txt -X POST https://example.com/login \
  -d "username=alice&password=secret"

# Use cookies for authenticated requests
curl -b cookies.txt https://example.com/profile

# Login in one command (capture cookie)
curl -c - -X POST https://example.com/login \
  -d "username=alice&password=secret"

Sending Different Data Formats

# JSON (explicit flag)
curl -X POST https://api.example.com/data \
  --json '{"key": "value"}'

# URL-encoded form
curl -X POST https://api.example.com/form \
  -d "name=Alice" \
  -d "age=30"

# Raw data
curl -X POST https://api.example.com/raw \
  -H "Content-Type: text/plain" \
  -d "Hello, server!"

# From a file
curl -X POST https://api.example.com/import \
  -H "Content-Type: application/json" \
  -d @data.json

Error Handling and Exit Codes

cURL returns specific exit codes that you can check in scripts:

CodeMeaning
0Success
1Unsupported protocol
6Could not resolve host
7Failed to connect
22HTTP page not retrieved
28Operation timeout
35SSL connect error
55Failed sending network data
56Failure in receiving network data

Always check exit codes in automated scripts: if [ $? -ne 0 ]; then echo "Request failed"; fi. Combine with --retry and --retry-delay for robust production scripts.

Best Practices

  • Use -sS in scripts — silent mode suppresses progress, but errors are still shown
  • Always check exit codescurl returns 0 on success, non-zero on failure
  • Limit redirects with --max-redirects 5 to prevent infinite loops
  • Set timeouts--connect-timeout 10 --max-time 30 for production scripts
  • Verify SSL — do not use -k (insecure) in production; fix certificate issues properly
  • Rate limiting — add --sleep 1 between requests to avoid overwhelming APIs

FAQ

What is the difference between -X POST and -d?

The -d flag sends data and implicitly sets the request method to POST. You only need -X POST when you want to be explicit or when -d is not used. Some APIs require GET with body data, though this is non-standard.

How do I send gzipped data with cURL?

Use --data-binary @file.gz with the header Content-Encoding: gzip. Note that the server must be configured to accept compressed request bodies, as most web servers handle only compressed responses.

Why does cURL return “SSL certificate problem”?

This means the server’s SSL certificate is self-signed, expired, or doesn’t match the hostname. Use -k only for testing. For production, fix the certificate issue or add the CA certificate with --cacert.

How do I mock slow responses with cURL?

Use --limit-rate to simulate slow connections for download testing. For upload testing, combine with --expect100-timeout to test how your application handles slow server responses.

Can cURL send concurrent requests?

cURL is single-threaded by nature. For concurrent requests, use tools like xargs with -P for parallelism, or use curl --parallel (available in newer versions with parallel support enabled).


Related: Learn SSH keys setup and REST API vs GraphQL.

Advanced Curl Techniques

Beyond basic HTTP requests, curl offers powerful features for API debugging and automation. The -w flag outputs connection timing details, invaluable for performance profiling: curl -w "@timing-format.txt" -o /dev/null https://api.example.com. Curl’s --resolve flag overrides DNS resolution for a specific host, enabling requests to a load balancer’s IP while preserving the Host header. Session cookies can be captured and replayed with -c cookies.txt and -b cookies.txt, simulating browser-like multi-step flows. For GraphQL APIs, curl’s -d with proper Content-Type: application/json and query escaping lets you test mutations and queries directly from the terminal. The --parallel flag (curl 7.68+) dispatches multiple transfers simultaneously: curl --parallel --parallel-immediate -O url1 -O url2. Combined with jq for JSON parsing, curl becomes a complete API testing toolkit. Rate-limited endpoints can be tested with --limit-rate 100K to simulate throttled connections. The --retry flag automatically retries failed requests up to a specified count, with --retry-delay controlling the pause between attempts.

Curl in CI/CD Pipelines

Curl is a staple in continuous integration scripts. Health-check endpoints are verified with curl --retry 5 --retry-delay 10 --retry-connrefused http://service:8080/health. API deployment verification uses HTTP status code inspection via -o /dev/null -w "%{http_code}". Access tokens stored as environment variables are passed with -H "Authorization: Bearer $TOKEN". Curl’s silent mode (-sS) suppresses progress output while showing errors, keeping CI logs clean. Upload artifacts to artifact repositories with --upload-file and proper content-type headers. Curl’s --fail flag exits with a non-zero code on HTTP errors, which most CI systems interpret as a pipeline failure.

Section: Developer Tools 1455 words 7 min read Beginner 756 articles in section Report inaccuracy Back to top