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
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.