Commandcurl

POST JSON to an API

Sends a JSON request body to an API endpoint with the right Content-Type header set, so the server parses it as JSON instead of form data.

Last updated

Commandcurl
curl -X POST https://api.example.com/items -H "Content-Type: application/json" -d '{"name":"widget"}'

How it works

-H "Content-Type: application/json" tells the server how to interpret the request body — without it, many frameworks assume the default application/x-www-form-urlencoded and either reject a JSON payload outright or silently fail to parse it, since curl doesn't set this header on its own just because the body looks like JSON. -d supplies that body; curl switches its request method to POST automatically the moment -d is present, which is why -X POST reads as slightly redundant here but is kept explicit anyway.

-d already implies POST, so -X POST is redundant here — and curl's own manual advises against the habit: an explicit -X is reapplied to every hop of a -L redirect chain, which can re-POST your body to the redirect target instead of following it as a GET. Reach for -X only for methods nothing implies, like DELETE.

Watch out for

  • →-X POST is technically inferred once -d is present, but dropping -d without also removing -X POST sends an empty POST request with no body — a common source of "why is my API getting nothing" confusion.
  • →The JSON payload is wrapped in single quotes here so the shell doesn't try to interpret the double quotes or any $variables inside it. On Windows cmd.exe (not PowerShell or WSL), quoting rules are different — wrap the JSON in double quotes instead and escape the inner double quotes with backslashes.
  • →For a payload too large or awkward to inline, use -d @payload.json to read the request body from a file instead of typing it on the command line.

Part by part

-X POST
Sets the method explicitly. Strictly redundant here — supplying a body with -d already implies POST — but it documents intent for the next reader.
-H "Content-Type: application/json"
Without this header curl sends form-encoded, and an API that reads JSON will reject the body or silently see nothing.
-d
The request body. Single quotes around it stop the shell interpreting the double quotes and braces inside.

Common mistakes

  • curl -X POST https://api.example.com/items -d '{"name":"widget"}'

    Without the header curl defaults to application/x-www-form-urlencoded, so a JSON API sees a form field whose name is the entire JSON string. The usual symptom is a 400 that mentions a missing required field.

    curl -X POST https://api.example.com/items -H "Content-Type: application/json" -d '{"name":"widget"}'
  • curl -X POST https://api.example.com/items -H "Content-Type: application/json" -d "{"name":"widget"}"

    Double-quoting the body lets the shell strip the inner quotes before curl sees them, so the JSON arrives malformed. Single quotes outside, double quotes inside, is the reliable arrangement.

  • curl -X GET https://api.example.com/items -d '{"q":"widget"}'

    Forcing GET while supplying a body produces a request many servers and proxies simply ignore the body of. Query parameters are the correct way to send data with a GET.

Questions

How do I send a body from a file instead?

Use -d @filename, with the at-sign telling curl to read the file rather than treat the text as literal. That avoids all shell quoting problems, which is worth it for anything longer than a line.

Why does my API key not work?

Most commonly the header name or scheme is wrong. Add -v to see exactly what curl sent, and check whether the service expects Authorization: Bearer, a custom header, or a query parameter.

Is it safe to put a token in the command?

No — the full command line lands in your shell history and is visible to anyone who can list processes. Read it from an environment variable or a file with -H "Authorization: Bearer $TOKEN" instead.

How do I see the response headers as well as the body?

-i includes them in the output, while -v shows the whole exchange including what was sent. Use -i when you care about the response, -v when you are working out why the request was rejected.

Related in Commands