UseToolSuite UseToolSuite

cURL for Developers: The Commands You'll Use Every Day

A definitive engineering guide to cURL for backend architects. Master complex OAuth authentication, multipart binary data uploads, deep TLS handshake debugging, network latency profiling, and HTTP header architectures.

Necmeddin Cunedioglu Necmeddin Cunedioglu 9 min read
Part of the HTTP Status Codes: The Complete Guide for Developers series

Practice what you learn

cURL to Code Converter

Try it free →

cURL for Developers: The Commands You’ll Use Every Day

Open the API docs for Stripe, Twilio, GitHub, AWS, or nearly any RESTful platform and you’ll see the same thing: a code block starting with curl.

cURL (Client for URLs) is the common language of network programming. It lets a Python developer in Berlin show a 403 Forbidden bug to a Go engineer in Tokyo without arguing about syntax, libraries, or dependencies. If you can reproduce a bug with a raw cURL command, the bug is real and it lives at the network layer.

cURL is also huge. It speaks FTP, IMAP, POP3, SCP, SMB, and Telnet, has over 380 flags, and its man page runs past 5,000 lines. Memorizing all of it isn’t worth your time.

In practice, a backend developer, DevOps engineer, or SRE only needs about 15 cURL commands to handle 99% of API testing, OAuth debugging, JSON payloads, and latency profiling.

This is a no-nonsense reference for the commands you’ll actually use day to day.

Stop writing boilerplate request code manually. Paste any complex cURL command into our local cURL to Code Converter to instantly and flawlessly translate it into production-ready Python requests, Node.js fetch, or Golang http.Client execution code.

1. The Absolute Basics: Requests and Headers

The Simple GET Request

curl https://api.example.com/v1/health-check

This is the baseline architectural foundation. It performs an HTTP GET request to the specified URL and dumps the raw response body directly to your terminal’s standard output (stdout). If the API endpoint responds with a large 5MB JSON array, cURL will ruthlessly flood your terminal buffer.

Inspecting Headers (The Detective Mode)

When a critical API call fails in production, the raw JSON response body often returns a completely useless, generic "500 Internal Server Error". The real diagnostic clues (like Rate Limit remaining tokens, CORS configuration headers, or Nginx cache hits) are hidden within the HTTP Response Headers.

# Fetch ONLY the Headers (Performs an HTTP HEAD request)
# Use this to check if a large file exists without actually downloading it
curl -I https://api.example.com/v1/users

# Fetch the Headers AND the Body together in a single request
curl -i https://api.example.com/v1/users

The Ultimate Debugger: Verbose Network Mode

If you are debugging complex Cross-Origin Resource Sharing (CORS) failures, aggressive SSL handshake rejections, or mysterious TCP connection drops across a VPN, you must initialize Verbose Mode.

curl -v https://api.example.com/v1/users

Verbose mode (-v) strips away all abstraction and exposes the entire lifecycle of the network request. It prints the exact DNS resolution IP address, the full TLS certificate verification chain, exactly what raw byte strings were sent in the HTTP Request (denoted by >), and exactly what bytes were received from the server (denoted by <).

2. Crafting Complex Payload Architectures (POST, PUT, PATCH)

Sending JSON Payloads

To mutate state on a remote server, you must change the HTTP Method (-X), explicitly define the MIME content type (-H), and attach the stringified data payload (-d).

curl -X POST https://api.example.com/v1/customers \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "Alice Developer",
    "email": "alice@engineering.com",
    "subscription_tier": "enterprise",
    "metadata": { "region": "eu-west-1" }
  }'

Architectural Note: If you utilize the -d flag, cURL automatically assumes you wish to execute a POST request. However, it is an engineering best practice to explicitly declare -X POST for team readability and bash script maintainability.

Updating Resources (PUT vs PATCH Mathematics)

Standard RESTful API architectures utilize PUT for complete, destructive resource replacement, and PATCH for partial, localized resource updates.

# PUT: Destructively replace the entire customer record
curl -X PUT https://api.example.com/v1/customers/cus_987654321 \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice Edited", "email": "alice@newdomain.com", "tier": "free"}'

# PATCH: Update ONLY the email field, leaving the tier and metadata entirely intact
curl -X PATCH https://api.example.com/v1/customers/cus_987654321 \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@newdomain.com"}'

Streaming Payloads directly from a File

Writing large, heavily nested, complex JSON payloads directly inside a bash terminal is a nightmare of escaped quotation marks. Instead, save your JSON structure to a physical file and instruct cURL to read it using the @ injection symbol.

curl -X POST https://api.example.com/v1/products \
  -H "Content-Type: application/json" \
  -d @new-product-payload-v2.json

3. The Authentication Masterclass

Almost every production API requires you to cryptographically prove your identity. Here is how to pass those credentials securely through the command line.

A. Bearer Tokens (OAuth 2.0 / JWT)

This is the most standard authentication architecture on the modern web. The JSON Web Token (JWT) is passed explicitly in the standard Authorization header.

curl https://api.example.com/v1/dashboard/metrics \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

B. HTTP Basic Authentication

Legacy APIs (and many modern internal microservices) utilize Basic Auth, which requires a username and a password to be combined via a colon, Base64 encoded, and sent in the header. cURL automates the entire Base64 encoding process for you with the -u flag.

# cURL automatically Base64 encodes the string "admin:supersecret_password"
curl -u admin:supersecret_password https://api.example.com/admin/settings

# SECURITY WARNING: If you type your password in the terminal, it is saved in your ~/.bash_history file!
# To prevent this, omit the password. cURL will securely prompt you to type it.
curl -u admin https://api.example.com/admin/settings

C. Custom Proprietary Header Keys

Large SaaS platforms (like Stripe, AWS API Gateway, or SendGrid) routinely require proprietary custom headers for API Key validation.

curl https://api.stripe.com/v1/billing/invoices \
  -H "X-Api-Key: pk_live_51Habcdefg12345" \
  -H "Stripe-Account: acct_1032D82eB69"

4. Advanced Data and Binary Operations

Uploading Binary Files (Multipart Form Data)

If you are attempting to upload an image, a PDF, or a compiled binary to a server, you absolutely cannot use the standard -d (data) flag. You must utilize the -F (form) flag, which forces cURL to construct a strict multipart/form-data request with complex cryptographic boundary separators.

curl -X POST https://api.example.com/v1/kyc/documents \
  -H "Authorization: Bearer my-secure-token" \
  -F "document_type=passport" \
  -F "file=@/Users/alice/Desktop/confidential_passport_scan.pdf"

Downloading and Streaming Files to Disk

If you hit an API endpoint that returns a compiled PDF, an MP4 video, or a .tar.gz ZIP file, allowing cURL to print raw binary data to your terminal’s stdout will catastrophically corrupt your terminal interface (forcing you to execute a reset command). You must save binary streams directly to the disk.

# Specify the exact custom filename to save the stream as
curl -o quarterly_financial_report_2026.pdf https://api.example.com/reports/latest

# Use the -O (capital O) flag to force cURL to dynamically parse and use the remote server's filename
curl -O https://cdn.example.com/assets/linux-binary-v2.0.tar.gz

# Silent mode (-s) aggressively hides the download progress bar, which is vital for background cron jobs
curl -s -o output.json https://api.example.com/data/export

5. Network Reliability, Firewalls, and SSL Bypassing

Following 301/302 Redirects

If you hit an API endpoint that has been migrated, the server will return an HTTP 301 Moved Permanently or 302 Found. By default, cURL does not automatically follow redirects; it will simply return the empty 301 response headers and terminate the connection.

To force cURL to automatically follow the chain of redirects until it hits the final destination server, use the -L (Location) flag.

curl -L https://example.com/short-link-to-api-v2

Enforcing Strict Connection Timeouts

If you are writing a production bash script that pings an API, and that API server crashes and hangs without closing the TCP connection, your bash script will hang infinitely in memory. Never deploy a cURL command in an automated script without setting an explicit timeout parameter.

# --connect-timeout: Terminate if the initial TCP handshake takes longer than 10 seconds.
# --max-time: Terminate if the ENTIRE operation (including a large 500MB download) takes longer than 30 seconds.
curl --connect-timeout 10 --max-time 30 https://api.example.com/massive-data-dump

Bypassing Aggressive SSL/TLS Errors

If you are executing API tests in a local Docker development environment (e.g., https://localhost:8080), you are inevitably utilizing a self-signed SSL certificate. cURL’s security engine will detect this and aggressively terminate the request, throwing an SSL certificate problem fatal error.

To temporarily bypass SSL verification algorithms, use the -k (Insecure) flag.

# FATAL WARNING: NEVER USE THE -k FLAG IN PRODUCTION SCRIPTS OR CI/CD PIPELINES!
# It makes you completely vulnerable to Man-In-The-Middle (MITM) attacks.
curl -k https://localhost:8080/api/local-test

6. Network Latency Profiling with cURL

cURL is not merely a request tool; it is a highly capable network latency profiling instrument. You can instruct cURL to format its standard output to reveal the exact microsecond execution timings of the DNS resolution, the TCP handshake, and the TLS cryptographic negotiation.

Create a local text file named curl-format.txt:

\n
      DNS Lookup: %{time_namelookup} seconds\n
     TCP Connect: %{time_connect} seconds\n
   TLS Handshake: %{time_appconnect} seconds\n
  Server Compute: %{time_starttransfer} seconds\n
---------------------------------------------------\n
      Total Time: %{time_total} seconds\n
\n

Now, execute cURL using this formatting map, piping the actual payload body to /dev/null so you only see the timing metrics:

curl -w "@curl-format.txt" -o /dev/null -s "https://api.stripe.com/v1/charges"

The Output Matrix:

      DNS Lookup: 0.023 seconds
     TCP Connect: 0.045 seconds
   TLS Handshake: 0.112 seconds
  Server Compute: 0.245 seconds
---------------------------------------------------
      Total Time: 0.380 seconds

This output is invaluable for debugging microservice latency. If your total request time is 2.5 seconds, but the Server Compute time is only 0.1 seconds, you immediately know the backend database is fast—the bottleneck is a slow DNS resolver, a bloated TLS certificate chain, or geographic routing latency.

7. Converting cURL to Production Application Code

Once you have successfully executed the cURL command and received the desired JSON payload, the next step is porting that mathematical logic into your application’s actual codebase. Manually translating headers, payload stringification logic, and multipart boundaries is tedious and prone to syntax errors.

Here is exactly how an identical POST request looks translated across modern backend languages.

The Original Baseline cURL:

curl -X POST https://api.example.com/data \
  -H "Authorization: Bearer secret_production_token_123" \
  -H "Content-Type: application/json" \
  -d '{"status":"active", "user_id": 9876}'

JavaScript (Modern Node.js Fetch API):

const response = await fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer secret_production_token_123',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    status: "active",
    user_id: 9876
  })
});
const data = await response.json();

Python (The Requests Library):

import requests

headers = {
    'Authorization': 'Bearer secret_production_token_123',
    'Content-Type': 'application/json'
}
json_payload = {
    "status": "active", 
    "user_id": 9876
}

response = requests.post('https://api.example.com/data', headers=headers, json=json_payload)
print(response.json())

Golang (net/http Standard Library):

package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload := []byte(`{"status":"active", "user_id": 9876}`)
	req, err := http.NewRequest("POST", "https://api.example.com/data", bytes.NewBuffer(payload))
	if err != nil {
		panic(err)
	}

	req.Header.Add("Authorization", "Bearer secret_production_token_123")
	req.Header.Add("Content-Type", "application/json")

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}

8. Comprehensive FAQ & Advanced Use Cases

Q: How do I spoof my User-Agent string to test mobile endpoints? A: Some APIs dynamically return different data based on the browser. You can override the default curl/7.81.0 User-Agent string using the -A flag: curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)" https://api.example.com.

Q: Can cURL handle HTTP/2 or HTTP/3? A: Yes, if your version is compiled with the correct underlying libraries. You can force cURL to negotiate specific protocols by appending --http2 or --http3 to the command.

Q: How do I execute a cURL request through a corporate Proxy server? A: Use the -x (lowercase x) flag to define the proxy IP and port: curl -x http://proxy.corporate.net:8080 https://api.example.com.

Q: How do I submit an HTML form? A: Form submissions use application/x-www-form-urlencoded. cURL handles this natively if you use the data flag but omit the JSON content-type: curl -d "username=alice" -d "password=secret" https://example.com/login.

Further Reading


Stop wasting critical engineering time translating complex headers and binary data flags. Paste any cURL command into our cURL to Code Converter to instantly generate production-ready code in 15+ backend languages. If an external API is aggressively rejecting your requests, utilize the HTTP Header Analyzer to debug your payload architecture.

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author
9 min read
-- views

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.