HandyTools Hub

← All guides

The Complete curl Command Guide: Every Flag You'll Actually Use

2026-08-17

curl https://example.com is the most-installed HTTP client on the planet, and it’s hiding in plain sight: the single most useful debugging tool a developer owns, outshone only by the documentation gap around it. Once you learn a dozen flags, curl becomes the fastest way to probe an API, reproduce a bug, and test a fix without opening a browser or writing a throwaway script. This guide walks through the flags in the order you’ll actually need them.

The Anatomy of a curl Command

A curl invocation is one URL plus zero or more flags:

curl [flags] URL

By default curl sends a GET request and writes the response body to standard output. The flags are what turn that bare command into a full HTTP client — and since flags are order-independent, the same command can be written several ways. If you ever get a flag’s behavior wrong, curl --help lists everything and curl --help all shows the full manual.

Reading Responses: -i, -I, -L, -s

The body alone rarely tells you enough. Three flags make the response legible:

curl -i https://api.example.com/users          # include response headers
curl -I https://api.example.com/users          # HEAD only: headers, no body
curl -L https://example.com/login              # follow redirects (3xx)
  • -i / --include — prints the response status line and headers above the body. The first thing to add when a request “doesn’t work,” because the status code is where the answer starts.
  • -I / --head — sends a HEAD request (see the HTTP methods guide for what that means) and prints headers only. Great for checking existence, size, or content-type of a resource without downloading it.
  • -L / --location — re-issues the request at every redirect location. Without it, a 301/302 just prints the redirect response and stops — a classic “why is my curl returning HTML for an API call?” gotcha.
  • -s / --silent — suppresses the progress meter. Pair it with -S (--show-error) so errors still print: -sS is the quiet-but-informative combo for scripts.

Headers and Methods: -H, -X

Custom headers and explicit methods are where curl starts feeling like a real client:

curl -X POST https://api.example.com/users
curl -H "Content-Type: application/json" \
     -H "Authorization: Bearer $TOKEN" \
     https://api.example.com/users/7
  • -H "Name: value" — sets one request header. Repeat the flag for multiple headers; use -H with an empty value to unset a default.
  • -X METHOD — sets the HTTP method. Common gotcha: curl’s -X POST overrides only the method, not the implicit behaviors that -d or -F trigger (like setting Content-Type). In practice you rarely need -X — sending -d already implies POST, and -F implies multipart POST — but -X PUT, -X PATCH, and -X DELETE are what you’ll reach for.

Sending Data: -d, JSON, —data-binary

curl -d "name=ada&role=admin" https://api.example.com/users          # form-encoded
curl -H "Content-Type: application/json" \
     -d '{"name": "ada", "role": "admin"}' \
     https://api.example.com/users                                    # JSON
curl --data-binary @body.json https://api.example.com/users          # raw file body
  • -d "key=value" / --data — sends form-encoded data (application/x-www-form-urlencoded) and implies POST. Multiple -d flags are concatenated with & automatically.
  • With -H "Content-Type: application/json" and a JSON string, the same -d sends a JSON body — this is the workhorse of every API call you’ll ever make.
  • --data-binary @file — sends the file’s contents verbatim as the request body (no newline stripping). Prefix with @ any time the body comes from a file.
  • To log the exact request curl is building, add -v (below) — the body and headers will be visible.

Files: -o, -O, -F, -T

curl -o report.pdf https://example.com/report.pdf   # save body to a named file
curl -O https://example.com/report.pdf              # save with remote filename
curl -F "avatar=@photo.png" https://example.com/upload     # multipart file upload
curl -F "avatar=@photo.png;type=image/png" https://example.com/upload
curl -T backup.zip https://example.com/uploads/     # PUT a file
  • -o file / -O — write the body to a file instead of stdout. Use -o /dev/null to discard a large body while keeping the headers visible.
  • -F "field=@file" — multipart/form-data upload, the equivalent of an HTML <input type="file"> form. Add ;type= to force the Content-Type of the uploaded part.
  • -T file / --upload-file — PUT the file’s contents to the URL. Combine with a download flag for a quick backup round-trip.

Cookies, Auth, and Sessions

curl -b "session=abc123" https://example.com/me            # send a cookie manually
curl -c cookies.txt https://example.com/login              # save cookies to a jar
curl -b cookies.txt https://example.com/me                 # replay them later
curl -u user:pass https://api.example.com/private          # HTTP Basic auth
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/private
  • -b — sends cookies, either as an inline "name=value" string or from a file (cookie jar / Netscape format). -c writes the cookies a response sets into a jar.
  • The classic session dance: -c to capture the login cookie, then -b to replay it on subsequent requests — the terminal equivalent of a logged-in browser tab.
  • -u user:pass — HTTP Basic auth (base64-encoded in the header). For token APIs, the Authorization: Bearer header is more common.

Debugging: -v, -w, -f

curl -v https://api.example.com/users            # verbose: full request/response
curl -w "\n%{http_code} in %{time_total}s\n" https://api.example.com/users
curl -f https://api.example.com/users/404        # exit non-zero on HTTP errors
  • -v / --verbose — prints the entire request line, request headers, TLS handshake details, and response headers. When nothing else makes sense, -v shows you exactly what went over the wire. --trace-ascii - shows even the raw bytes.
  • -w 'format' / --write-out — prints extra info after the response. %{http_code}, %{time_total}, and %{size_download} are the ones you’ll use to measure API latency or confirm a status without parsing the body. %{redirect_url} shows where a 3xx wanted to go.
  • -f / --fail — makes curl exit with a non-zero code (22) on HTTP 4xx/5xx instead of printing the error page and “succeeding.” Essential for scripts that need to know a request actually failed.

Timeouts and Retries

curl --connect-timeout 5 https://api.example.com     # fail if TCP connect stalls
curl -m 30 https://api.example.com/slow              # hard total-time cap
curl --retry 3 --retry-delay 2 https://api.example.com/jittery
  • --connect-timeout bounds only the connection phase — use it to fail fast on unreachable hosts.
  • -m / --max-time bounds the whole operation — the guardrail for endpoints that hang.
  • --retry N retries on transient failures (with a delay between attempts); combined with a non-idempotent method, be aware you may be replaying a POST — the HTTP methods guide explains why that matters.

Practical Tips

  • Start minimal, add as needed: curl -i first, then add -L for redirects, then -H and -d once you know the shape of the request.
  • Never paste secrets into shared chats — put tokens in an environment variable ($TOKEN) and reference it, as in the examples above.
  • Turn curl into code: once you’ve confirmed a request works, the curl Converter tool turns the exact command into JavaScript fetch, Python, Go, and other languages — no need to hand-translate.
  • Look up the status code you just got with the HTTP Status Reference — knowing that 405 means “wrong method” (and that the Allow header lists the right ones) resolves most API mysteries.
  • Save your most-used incantations as shell aliases or a small script; a cget() / cjson() wrapper covers 90% of daily API work.

Quick Reference

  • curl -i URL — see status + headers. curl -L URL — follow redirects. curl -sS URL — quiet but show errors.
  • -H "Name: value" for headers; repeat for more. -X PUT / -X PATCH / -X DELETE to change the method.
  • -d "k=v" = form POST; -d '{"json":...}' + -H "Content-Type: application/json" = JSON POST; --data-binary @file = raw body.
  • -o file save, -O save by remote name, -F "field=@file" multipart upload, -T file PUT upload.
  • -b send cookies, -c save cookies, -u user:pass Basic auth, Bearer via -H "Authorization: ...".
  • -v verbose wire dump, -w '%{http_code}' print status, -f fail on HTTP errors.
  • --connect-timeout, -m (max time), --retry N keep flaky requests from hanging forever.