Quick verdict: Use -d '{"key":"value"}' -H "Content-Type: application/json" for JSON. Use -F "field=value" -F "[email protected]" for multipart file uploads. Use -d "name=alice&age=30" for form-encoded data. cURL adds -X POST automatically when you use -d or -F, so you rarely need it explicitly.
The most common pattern in 2026:
curl -X POST https://api.example.com/users -H "Content-Type: application/json" -d '{"name":"Alice","email":"[email protected]"}'Three things to know:
-X POST is technically optional — -d implies POST. But explicit is clearer in scripts.-d by default sets Content-Type: application/x-www-form-urlencoded. Override with -H for JSON.For complex JSON, read from a file:
curl -X POST https://api.example.com/users -H "Content-Type: application/json" -d @user.jsonThe @ prefix tells cURL "read body from this file." Useful when JSON has special chars or is generated by another tool (jq, etc.).
Old-school HTML form submission:
curl -X POST https://example.com/login -d "username=alice" -d "password=secret123"Multiple -d flags concatenate with &. Equivalent to:
curl -X POST https://example.com/login -d "username=alice&password=secret123"Content-Type defaults to application/x-www-form-urlencoded. The body is exactly the encoded string (no JSON wrapping).
If your form values have spaces, ampersands, or equal signs, use --data-urlencode:
curl -X POST https://example.com/search --data-urlencode "query=hello world & more" --data-urlencode "page=1"cURL escapes the space, ampersand, and other unsafe chars before sending. Without --data-urlencode, the unescaped & would split your value mid-string.
Forms with file inputs use multipart/form-data. Use -F:
curl -X POST https://api.example.com/upload -F "title=Beach photo" -F "file=@/home/alice/photo.jpg"The @ in file=@path is the file-upload syntax. cURL reads the file, sets the right MIME type, and builds the multipart body for you.
Override the MIME type:
-F "[email protected];type=image/jpeg"Set the filename in the form (different from the disk path):
-F "file=@/tmp/upload.bin;filename=document.pdf;type=application/pdf"Multiple files at once:
curl -X POST https://api.example.com/upload -F "[email protected]" -F "[email protected]"Sometimes you want the body sent verbatim, without cURL stripping newlines or interpreting @:
curl -X POST https://api.example.com/raw -H "Content-Type: text/plain" --data-raw "$(cat body.txt)"--data-raw is identical to -d except it does NOT interpret @ as "read from file." Useful when your body genuinely starts with an @ sign (uncommon but exists).
For binary data:
curl -X POST https://api.example.com/binary -H "Content-Type: application/octet-stream" --data-binary @image.png--data-binary @file reads the file byte-for-byte. -d @file strips line endings, which corrupts binary data.
Bearer token + JSON body:
curl -X POST https://api.example.com/items -H "Authorization: Bearer eyJhbGc..." -H "Content-Type: application/json" -d '{"name":"widget"}'Basic auth + form data:
curl -X POST https://api.example.com/items -u user:pass -d "name=widget"See cURL basic authentication for all the auth options.
Useful for testing geo-locked APIs or routing through residential IPs:
curl -X POST https://api.example.com/data --proxy http://USER:[email protected]:8000 -H "Content-Type: application/json" -d '{"region":"us-east"}'For high-volume API calls behind anti-bot protection, route through Premium Residential proxies for natural-looking traffic.
By default cURL prints the body to stdout. Capture it:
# Save to file
curl -X POST https://api.example.com/data -d '...' -o response.json
# Variable in bash
RESPONSE=$(curl -X POST https://api.example.com/data -d '...')
# Get HTTP status code
STATUS=$(curl -X POST https://api.example.com/data -d '...' -o /dev/null -w "%{http_code}")Pipe through jq for JSON pretty-printing:
curl -s -X POST https://api.example.com/data -d '{}' | jq .The -v flag shows the full request and response:
curl -v -X POST https://api.example.com/data -d '{"foo":1}'You will see lines starting with > (request) and < (response). Critical things to check:
Use --trace-ascii /dev/stdout for even more detail — shows the bytes cURL sent on the wire.
curl vs wget compares the two download tools. cURL timeout settings covers --max-time and --connect-timeout. Mastering cURL is the full reference. For the JavaScript equivalent, see cURL in JavaScript.