curljsonapihttpcommand-linedevtools

How to POST JSON with curl: The Practical Guide

·7 min read·Developer Tools

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:

bash
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:

bash
# ✅ 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:

bash
curl --json '{"name": "Ada"}' https://api.example.com/users

That 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 @:

bash
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 flag

This 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:

bash
# 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/1

See 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

  1. Set `Content-Type: application/json` (or use --json) — the top cause of failed JSON posts.
  2. Single-quote the body, double-quote inside.
  3. Use `-d @file.json` for anything non-trivial.
  4. Add `-i` or `-v` when it doesn't work, to see the real status and headers.
  5. 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.

Frequently asked questions

Use curl -X POST <url> -H "Content-Type: application/json" -d '{"key":"value"}'. The Content-Type header tells the server the body is JSON, and single-quoting the body preserves the double quotes JSON requires. In modern curl you can use --json '{"key":"value"}' as a shortcut.

The two most common causes are a missing Content-Type: application/json header (curl otherwise sends form-encoded, which the server can't parse as JSON) and shell quoting that strips the double quotes from your JSON. Single-quote the whole body and set the header explicitly.

Introduced in curl 7.82, --json sets the request body and automatically adds both the Content-Type: application/json and Accept: application/json headers, replacing three separate flags. It also accepts @file.json to read the body from a file.

Use -d @filename.json (or --json @filename.json). The @ tells curl to read the request body from that file, which avoids shell-quoting problems and keeps large payloads readable.

Pipe curl's output to jq: curl ... | jq .. That formats and colorizes the JSON. You can also add -i to see response headers or -v for the full verbose exchange when debugging.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.