curl is how you test a JSON API
Before you write a line of client code, curl lets you hit a JSON API from the terminal and see exactly what it returns. The catch is that sending JSON correctly needs two things people routinely forget — the right header and the right quoting. Get those and curl is the fastest way to probe an endpoint. (To turn a working curl command into code afterward, use the curl-to-code tool.)
The canonical JSON POST
Three flags do the job — the method, the content type, and the body:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Ada", "role": "admin"}'- `-X POST` sets the HTTP method. (curl uses POST automatically when you send a body, but being explicit is clearer.)
- `-H "Content-Type: application/json"` is the one everyone forgets. Without it, curl sends
Content-Type: application/x-www-form-urlencoded, and many servers then fail to parse your JSON — a classic cause of a 415 Unsupported Media Type or a confusing 400. - `-d '...'` is the request body. Wrap it in single quotes so the shell doesn't touch the double quotes your JSON needs.
The quoting rule that prevents most errors
JSON requires double quotes around keys and string values. Your shell also uses quotes. The reliable pattern is single-quote the whole JSON body so the double quotes pass through untouched:
# ✅ single quotes outside, double quotes inside — correct
-d '{"email": "ada@example.com"}'
# ❌ double quotes outside — the shell eats the inner quotes, JSON breaks
-d "{"email": "ada@example.com"}"If a value itself must contain a variable ($VAR), single quotes won't expand it — use --json with a heredoc or escape carefully. But for static bodies, single quotes are the habit that avoids most "invalid JSON" errors.
--json: the shortcut in modern curl
curl 7.82+ added a --json flag that sets the body, the Content-Type: application/json header, and the Accept: application/json header in one go:
curl --json '{"name": "Ada"}' https://api.example.com/usersThat single flag replaces -H "Content-Type: application/json" -H "Accept: application/json" -d. If your curl is recent enough, prefer it — there's nothing to forget.
Posting a JSON file
For anything longer than a line, keep the body in a file and reference it with @:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d @user.json # send the contents of user.json
curl --json @user.json https://api.example.com/users # same, modern flagThis sidesteps shell quoting entirely and keeps large payloads readable and version-controlled.
Auth headers and other methods
Real APIs need authentication and other verbs — add more -H flags and change the method:
# authenticated PUT
curl -X PUT https://api.example.com/users/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"role": "member"}'
# GET with an Accept header
curl -H "Accept: application/json" https://api.example.com/users/1See HTTP methods for JSON APIs for when to use POST vs PUT vs PATCH.
Reading the response
- Pretty-print it by piping to jq:
curl ... | jq ., or paste the output into the JSON formatter. - See the status and headers with
-i(include response headers) or-v(verbose, shows the full request too — invaluable when debugging why a body isn't accepted). - Follow redirects with
-L, so an auth redirect doesn't leave you staring at an HTML login page instead of JSON.
The quick checklist
- Set `Content-Type: application/json` (or use
--json) — the top cause of failed JSON posts. - Single-quote the body, double-quote inside.
- Use `-d @file.json` for anything non-trivial.
- Add `-i` or `-v` when it doesn't work, to see the real status and headers.
- Pipe to `jq` to read the response.
To convert a finished curl command into a fetch/axios/Python snippet, drop it into the curl-to-code converter; for the errors you'll hit along the way see common JSON mistakes.