The same curl -X POST command works perfectly when you type it in your
terminal, but returns HTTP 400 (or a JSON parse error) when you paste it into a
.sh script. Or worse: it works locally but fails in CI.
Embedding JSON inside single quotes (-d '{...}'). The shell treats the
single quotes as literal delimiters, but the moment your JSON contains an
apostrophe (a name like O'Brien, a comment with it's, or a SQL WHERE name = 'foo') the quote-pair breaks and the shell starts parsing the rest of
the command as code.
Use --data-raw with a here-doc, or pipe the body via stdin:
curl -X POST https://api.example.com/things \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{"name": "O'Brien", "tag": "it's a test"}
EOF
The <<'EOF' (with quotes around the delimiter) disables variable expansion
inside the body, so $ characters and backticks stay literal.
The shell only does its quote-matching at parse time. Inside '...' the shell
treats every byte as literal, but the first unescaped ' ends the string.
JSON often contains apostrophes, so single-quoting is fragile. Heredocs with
a quoted delimiter bypass shell parsing entirely.
# Read JSON body from a file (cleanest)
curl -X POST https://api.example.com/things \
-H "Content-Type: application/json" \
--data-binary @body.json
curl ... | jq so you can see parsed JSON..http file and use the
REST Client extension in VS Code.requests, Node
fetch) instead of shelling out.Get a fresh guide every Monday. £12/mo, £100/yr, or £250 lifetime.