Back to blog
Guides6 min read

How to POST JSON with cURL: Examples and Gotchas

Send JSON in a cURL POST request: the Content-Type header, the -d flag, sending from a file, adding auth, quoting on Windows vs Unix, and why -d already implies POST.

By Saifur Rahman Shochib

cURL is the fastest way to test an API from the terminal, but sending JSON has two reliable traps: forgetting the Content-Type header and getting the shell quoting wrong. Both produce confusing failures. This short guide shows exactly how to POST JSON with cURL, from the basic command to files, auth, and the cross-platform quoting rules.

The basic command

A JSON POST needs three things: the method, the header, and the body. The canonical form is curl -X POST https://api.example.com/users -H "Content-Type: application/json" -d '{"name":"Leo","age":26}'. The -H flag sets the header that tells the server the body is JSON, and -d carries the payload. That single line covers the vast majority of API testing.

POST a JSON bodybash
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Leo","age":26}'

Why the Content-Type header is required

By default, when you pass data with -d, cURL sets Content-Type to application/x-www-form-urlencoded — the format for HTML forms, not JSON. If you omit the header, the server may try to parse your JSON as form data and reject it or misread it. Always add -H "Content-Type: application/json" so the server applies its JSON parser. This is the single most common cURL-and-JSON mistake.

You do not need -X POST with -d

Passing -d (or its alias --data) automatically switches the request to POST, so -X POST is redundant when you send a body. Many examples include it for clarity, which is harmless, but curl -H "Content-Type: application/json" -d '{...}' URL is already a POST. Knowing this avoids confusion when a command works without an explicit method.

Sending JSON from a file

For anything beyond a tiny payload, put the JSON in a file and reference it with an @ prefix: curl -X POST URL -H "Content-Type: application/json" -d @payload.json. This avoids fighting shell quoting for large bodies and keeps your command readable. It is also the cleanest way to reuse a request body across calls.

Send a larger body from a filebash
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d @payload.json

Adding authentication

Most real APIs need a token. Add a second header: -H "Authorization: Bearer YOUR_TOKEN" alongside the Content-Type header. You can repeat -H as many times as needed. For basic auth, the -u user:password flag works instead. The order of flags does not matter.

Add a bearer tokenbash
curl https://api.example.com/me \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"active"}'

Quoting on Windows versus Unix

Quoting is where cross-platform pain lives. On macOS and Linux, wrap the JSON body in single quotes and use double quotes for the JSON keys and values, since the shell treats them differently. On Windows Command Prompt, single quotes are not special, so you must use double quotes around the body and escape the inner double quotes — which is exactly why sending the body from a file with @payload.json is the most portable approach. To craft and verify the payload first, a JSON formatter or validator confirms it is well-formed before you send it.

Frequently asked questions

How do I POST JSON with cURL?

Use curl -X POST URL -H "Content-Type: application/json" -d '{"key":"value"}'. The -H flag sets the JSON content type and -d carries the body. Passing -d already makes it a POST, so -X POST is optional.

Why do I need the Content-Type header in cURL?

Because cURL defaults to application/x-www-form-urlencoded when you use -d. Without -H "Content-Type: application/json", the server may parse your JSON as form data and reject it. The header tells the server to use its JSON parser.

Do I need -X POST when sending JSON with cURL?

No. Using -d (or --data) automatically switches the request to POST, so -X POST is redundant. Many examples include it for readability, but it is not required when you send a body.

How do I send a JSON file with cURL?

Reference the file with an @ prefix: -d @payload.json. This avoids shell-quoting problems for large bodies and is the most portable approach across operating systems.

How do I fix cURL JSON quoting errors on Windows?

Windows Command Prompt does not treat single quotes as special, so wrap the body in double quotes and escape the inner ones, or — far easier — send the JSON from a file with -d @payload.json to sidestep quoting entirely.

Related tools

Try these tools

Keep reading

Related guides