How to POST JSON with curl (Without Quoting Pain)
Every developer's most-retyped command. The shape is simple — method, header, data — and the pain is entirely in shell quoting. Here's the canonical form, the Windows differences, and the @file trick that sidesteps quoting forever.
In short
POST JSON with curl using curl -X POST -H "Content-Type: application/json" -d '<json>' — the Content-Type header is mandatory, not optional, or the server may receive form-encoded data. On Windows, cmd.exe and PowerShell quoting rules differ from Linux/macOS, so the safest cross-platform approach is -d @body.json, reading the payload from a file instead of inlining it.
The canonical command
Three parts: -X POST (often optional — -d implies POST), the Content-Type header (NOT optional — without it servers receive form-encoding and many reject or misparse the body), and -d with the JSON. On Linux/macOS, wrap the JSON in single quotes so the double quotes inside survive untouched.
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{"sku": "KB-75", "qty": 1}'
# add auth + capture status:
curl -s -w "\n%{http_code}\n" -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"sku": "KB-75", "qty": 1}'Windows: where the quoting rules flip
cmd.exe has no single-quoted strings — you must use double quotes outside and escape the inner ones as \". PowerShell's native quoting fights curl too (and aliases curl to Invoke-WebRequest on older versions — use curl.exe explicitly). The sanity-preserving move on Windows: don't inline JSON at all; use a file.
:: cmd.exe — escape inner quotes
curl.exe -X POST https://api.example.com/orders -H "Content-Type: application/json" -d "{\"sku\": \"KB-75\", \"qty\": 1}"
# PowerShell — single quotes work, but curl.exe (not the alias!)
curl.exe -X POST https://api.example.com/orders -H "Content-Type: application/json" -d '{\"sku\": \"KB-75\"}'The @file trick: stop escaping entirely
-d @body.json reads the payload from a file — no shell quoting, no escaping, identical on every OS, and the file is reusable and reviewable. This is the professional default for anything beyond a tiny test body. Validate the file first (one click in our validator) so a typo'd comma doesn't masquerade as an API error, and minify large payloads to one line if logs or tooling prefer it.
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d @body.json
# read from stdin instead:
echo '{"sku":"KB-75"}' | curl -X POST -H "Content-Type: application/json" -d @- https://api.example.com/ordersReading what comes back
APIs answer in minified JSON. Pipe through a formatter — curl … | python -m json.tool or | jq . — or paste the response into our JSON Formatter for folding, tree view, and JMESPath filtering when the response is large. For debugging the request itself, -v shows exactly what was sent: nine times out of ten, the 'API bug' is a missing Content-Type or a body the shell mangled — both instantly visible there.
Sources
Frequently asked questions
Why does the server say 'invalid JSON' when my JSON is valid?
Usually the shell ate your quotes before curl ever sent them — the server received {sku: KB-75} or worse. Run with -v and read the actual body sent. The @file method eliminates this class of bug completely.
Do I need -X POST if I'm using -d?
No — -d switches the request to POST automatically. It's harmless to include and common for clarity. (You DO need an explicit -X for PUT/PATCH with the same -d syntax.)
How do I send JSON with special characters or newlines?
From a file (-d @body.json) — files carry any content byte-perfectly. If it must be inline, JSON-escape the content first (newlines as \n, quotes as \") — our JSON Escape tool produces exactly that string.
Validate the body before you curl it — exact error positions beat deciphering a 400 response. Then minify to one line for clean -d usage.
Prep your payloadKeep working
Related tools
JSON Validator
Strict RFC 8259 validation with the exact line and column of every error — plus one-click repair for broken JSON.
JSON to One Line
Strip every line break and indent from your JSON to get a clean single-line version — perfect for logs, env vars, and curl.
JSON Escape
RFC 8259 string escaping both ways — make any text safe inside a JSON string, or decode the backslash soup back to text.
JSON Formatter
Format, beautify, and validate JSON instantly in a professional VS Code-style editor — free, fast, and 100% private.
URL Encode / Decode
Percent-encoding both ways — make any text safe in a URL, or turn %20-soup back into readable text.
JSON URL Encode
Put JSON in a URL safely — validated, percent-encoded, and decoded back to pretty-printed JSON.
GraphQL Formatter
Canonical GraphQL style for queries, mutations, fragments and full SDL schemas.