Understanding cURL: The Complete Guide
Understanding cURL: The Complete Guide
If you work with APIs, servers, or anything on the web, cURL is one of the most important tools in your arsenal. Short for "Client URL," cURL is a command-line utility for transferring data using various network protocols. It ships preinstalled on macOS, most Linux distributions, and modern versions of Windows 10 and 11. Whether you're debugging a webhook, testing an API endpoint, or downloading a file from a remote server, cURL is the tool developers reach for first.
What makes cURL indispensable is its universality. API documentation almost always includes cURL examples. When a teammate shares a failing request, they paste a cURL command. When you need to reproduce a browser request outside the browser, you right-click in DevTools and select "Copy as cURL." It is the lingua franca of HTTP communication, and understanding it deeply will make you a significantly more effective developer.
Basic Syntax
At its simplest, a cURL command is just the word curl followed by a URL. This performs an HTTP GET request and prints the response body to your terminal:
curl https://api.github.comThe general pattern for any cURL command is:
curl [options] [URL]Options (also called flags) modify the behavior of the request. You can set the HTTP method, add headers, include a request body, follow redirects, save output to a file, and much more. The order of flags does not matter — cURL processes them all before making the request.
Essential Flags
There are dozens of cURL flags, but mastering the following set will cover the vast majority of real-world use cases.
-X (Request Method)
The -X flag specifies the HTTP method. By default, cURL uses GET. Use -X to switch to POST, PUT, PATCH, DELETE, or any other method:
curl -X POST https://api.example.com/users
curl -X DELETE https://api.example.com/users/42
curl -X PATCH https://api.example.com/users/42Note that when you use -d(data), cURL automatically sets the method to POST, so you don't need -X POST in that case. However, many developers include it anyway for clarity, and there is nothing wrong with being explicit.
-H (Headers)
The -H flag adds a custom header to the request. You can use it multiple times to add several headers. This is essential for setting content types, authorization tokens, and custom headers:
curl -H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
https://api.example.com/dataHeaders are case-insensitive per the HTTP specification, but it is conventional to use title case (e.g., Content-Type rather than content-type).
-d (Data / Request Body)
The -d flag sends data in the request body. When you use -d, cURL automatically sets the method to POST and the Content-Type to application/x-www-form-urlencoded unless you override it with -H:
# Send JSON data
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "[email protected]"}'
# Send form data
curl -X POST https://api.example.com/login \
-d "username=alice&password=secret"
# Read data from a file
curl -X POST https://api.example.com/upload \
-H "Content-Type: application/json" \
-d @payload.jsonThe @ prefix tells cURL to read the body from a file instead of using the literal string. This is extremely useful when working with large JSON payloads.
-o (Output to File)
The -o flag saves the response body to a file instead of printing it to the terminal:
curl -o response.json https://api.example.com/data
curl -o image.png https://example.com/photo.pngUse -O (uppercase) to save the file using the remote filename from the URL.
-v (Verbose)
The -v flag enables verbose output, showing the full request and response including headers, TLS handshake details, and connection information. This is your best friend when debugging:
curl -v https://api.example.com/healthVerbose output lines starting with > show what cURL sent, and lines starting with < show what the server returned. Lines starting with * are informational messages from cURL itself, such as DNS resolution and TLS negotiation details.
-L (Follow Redirects)
By default, cURL does not follow HTTP redirects (301, 302, etc.). The -L flag tells it to automatically follow the Location header:
curl -L http://github.comWithout -L, you would get the redirect response itself rather than the final destination. This flag is essential when working with URLs that may redirect, such as shortened URLs or HTTP-to-HTTPS upgrades.
-k (Insecure / Skip TLS Verification)
The -k flag disables SSL certificate verification. Use this only in development when working with self-signed certificates:
curl -k https://localhost:8443/api/healthWarning: Never use -k in production scripts. It disables a critical security check and makes your request vulnerable to man-in-the-middle attacks.
-u (Authentication)
The -u flag provides HTTP Basic Authentication credentials:
curl -u username:password https://api.example.com/protected
# If you omit the password, cURL will prompt for it interactively
curl -u username https://api.example.com/protectedcURL encodes the credentials as a Base64 string in the Authorization: Basic header automatically.
-I (Head Request)
The -I flag sends an HTTP HEAD request and displays only the response headers. This is useful for checking if a resource exists, examining cache headers, or reading the content type without downloading the full body:
curl -I https://example.com-s (Silent Mode)
The -s flag suppresses the progress bar and error messages. This is essential when using cURL in scripts where you only want the response body:
# Without -s, you get a progress bar on stderr
# With -s, you get clean output
curl -s https://api.example.com/data | jq .--compressed
The --compressed flag tells cURL to request compressed responses (using gzip, deflate, or brotli) and automatically decompress them. This can significantly speed up large responses:
curl --compressed https://api.example.com/large-datasetReal API Examples
Theory only goes so far. Here are practical, real-world cURL examples using popular APIs. Replace placeholder tokens with your actual credentials.
GitHub API
The GitHub API is one of the most well-documented REST APIs available. Here is how to interact with it using cURL:
# List your repositories (authenticated)
curl -H "Authorization: token ghp_YOUR_PERSONAL_ACCESS_TOKEN" \
https://api.github.com/user/repos
# Get a specific repository's details
curl https://api.github.com/repos/facebook/react
# Create an issue on a repository
curl -X POST \
-H "Authorization: token ghp_YOUR_PERSONAL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Bug: Login page crashes on mobile",
"body": "Steps to reproduce:\n1. Open login page on iPhone\n2. Tap the email field\n3. App crashes",
"labels": ["bug", "mobile"]
}' \
https://api.github.com/repos/YOUR_USER/YOUR_REPO/issuesStripe API
Stripe uses HTTP Basic Auth with your API key as the username and an empty password. This is a common pattern for API key authentication:
# Create a customer
curl -X POST https://api.stripe.com/v1/customers \
-u sk_test_YOUR_STRIPE_SECRET_KEY: \
-d "[email protected]" \
-d "name=Jane Doe" \
-d "description=New customer from signup form"
# Retrieve a charge
curl https://api.stripe.com/v1/charges/ch_1234567890 \
-u sk_test_YOUR_STRIPE_SECRET_KEY:
# List all customers with a limit
curl "https://api.stripe.com/v1/customers?limit=10" \
-u sk_test_YOUR_STRIPE_SECRET_KEY:Notice the trailing colon after the API key in -u. This tells cURL the password is empty, preventing it from prompting you interactively.
Slack API
Slack's Web API uses Bearer token authentication and JSON payloads:
# Send a message to a channel
curl -X POST https://slack.com/api/chat.postMessage \
-H "Authorization: Bearer xoxb-YOUR-SLACK-BOT-TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channel": "C0123456789",
"text": "Deployment to production completed successfully!",
"unfurl_links": false
}'
# List channels
curl -H "Authorization: Bearer xoxb-YOUR-SLACK-BOT-TOKEN" \
"https://slack.com/api/conversations.list?limit=20"Combining Flags
In practice, most cURL commands use several flags together. Here are some complex, real-world examples that demonstrate how flags combine:
# POST JSON with auth, follow redirects, verbose output, and save to file
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"query": "SELECT * FROM users LIMIT 10"}' \
-L \
-v \
-o result.json \
https://api.example.com/query
# Silent request piped to jq for pretty-printing
curl -s -H "Accept: application/json" \
https://api.example.com/users | jq '.[0:5]'
# Upload a file with progress and custom timeout
curl -X PUT \
-H "Content-Type: application/octet-stream" \
--data-binary @./build.zip \
--connect-timeout 10 \
--max-time 300 \
-o upload-response.json \
https://storage.example.com/artifacts/build.zip
# Multiple requests using the same connection (HTTP/2)
curl --http2 \
-H "Authorization: Bearer YOUR_TOKEN" \
https://api.example.com/endpoint1 \
https://api.example.com/endpoint2Common Mistakes
Even experienced developers stumble on these. Here are the most frequent cURL mistakes and how to avoid them:
- Forgetting Content-Type for JSON: If you send JSON with
-dbut forget to set-H "Content-Type: application/json", the server receivesapplication/x-www-form-urlencodedas the content type. Many APIs will reject the request or misparse the body. Always set the Content-Type header explicitly when sending JSON. - Quoting issues on Windows vs. Unix: On macOS and Linux, you wrap JSON in single quotes:
-d '{...}'. On Windows Command Prompt, you must use double quotes and escape internal quotes:-d "{\\"key\\": \\"value\\"}". PowerShell has its own escaping rules. This is the single most common source of "it works on my machine" issues with cURL. - Not using -s in scripts: Without
-s, cURL prints a progress bar to stderr, which pollutes script output and log files. Always add-swhen using cURL in shell scripts, CI pipelines, or anywhere the output is processed programmatically. - Ignoring HTTP status codes: cURL returns exit code 0 for any completed HTTP response, even a 404 or 500. To fail on HTTP errors, use
-f(fail silently) or--fail-with-body(fail but still output the response body). Without this, your script may happily process an error response as if it were valid data. - Forgetting to URL-encode query parameters: Special characters in query strings (spaces, ampersands, plus signs) must be percent-encoded. Use
--data-urlencodefor form data, or manually encode your URLs. An unencoded space in a URL will cause cURL to fail silently or send a malformed request. - Using -X GET unnecessarily: GET is the default method. Writing
-X GETis redundant and can actually cause subtle issues with redirects, because cURL will reuse the explicitly set method on redirects rather than switching to GET as the HTTP specification requires.
cURL to Code
Once you have a working cURL command, you often need to reproduce that same request in your application code. Manually translating a complex cURL command with multiple headers, authentication, and a JSON body into Python, JavaScript, Go, or another language is tedious and error-prone.
Converter tools exist that can parse a cURL command and generate equivalent code in languages like Python (using the requests library), JavaScript (fetch or axios), Go, PHP, Ruby, Java, and more. These tools save significant time, especially when dealing with complex requests that have many headers or nested JSON bodies. Pastefix includes a cURL Converter that supports 13 languages and provides explanations of each part of the command.
The workflow is straightforward: copy a cURL command from API documentation or browser DevTools, paste it into a converter, and get ready-to-use code for your language of choice. This eliminates an entire class of translation bugs and speeds up API integration work.
Conclusion
cURL is one of those tools that rewards deep knowledge. The basics are simple — curl URL — but mastering flags like -H, -d, -v, and -s transforms you from someone who copies and pastes cURL commands to someone who can debug any API issue from the command line.
Start by using cURL every time you would normally reach for Postman or a similar GUI tool. Force yourself to construct requests manually for a week. You will quickly internalize the most common flag combinations, and the speed advantage of staying in the terminal will become obvious. When you encounter a complex command you need in application code, use a converter tool to translate it instantly. And when something is not working, remember that -v is always your first debugging step — it shows you exactly what is going over the wire.
Tip: You can paste any cURL command into Pastefix to instantly convert it to 13 programming languages and get a detailed breakdown of every flag and parameter.
Try it yourself
Use Pastefix's cURL Converter to instantly analyze and understand your own data — no signup required.
Open cURL Converter→