Curl JSON Request Example

A JSON API call has two sides, the JSON you send and the JSON you get back, and each has its own header. This example covers the round trip, including what to do with the reply.

Last updated . How this page is checked.

The command

curl https://httpbin.org/anything \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"query":"status:open","limit":10}'

Opens the request console with this request filled in. It is sent from api-tester.com's server rather than from your machine, so it shows what the endpoint returns, not how your own network or curl build behaves.

What the flags do

FlagWhat it does
-H "Content-Type: application/json"Says the body you are sending is JSON.
-H "Accept: application/json"Says you want JSON back.
-d dataThe JSON document. Makes the request a POST unless -X says otherwise.
-X PUT / PATCH / DELETESends the same kind of body with another method.
--json dataSets the body and both headers in one flag (curl 7.82+).

Two headers, two directions

The pair is not redundant. Leave out the first and the server does not run its JSON parser on your body. Leave out the second and an API that supports several formats may reply in XML, or send an HTML error page that your script then tries to read as JSON. A request with no body, such as most GET and DELETE calls, needs only Accept.

The method is independent of all this. The same headers and the same kind of body work with PUT and PATCH; only -X changes.

curl -X PUT https://httpbin.org/anything/users/42 \
  -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{"name":"Ada","active":true}'

curl -X DELETE https://httpbin.org/anything/users/42 -H "Accept: application/json"

Building the body without quoting accidents

Pasting a shell variable into a JSON string works until the value contains a quote, a backslash or a line break, at which point the document is no longer valid. Let jq assemble it: --arg passes a value in as a correctly escaped string, --argjson passes a number or boolean as it is, and the result is piped to curl, which reads it with -d @-.

title='Disk "sda" is 91% full'
jq -n --arg title "$title" --argjson priority 2 \
  '{title: $title, priority: $priority}' \
  | curl -s https://httpbin.org/anything \
      -H "Content-Type: application/json" -H "Accept: application/json" -d @-

Using a value from the reply

Real work is rarely one call. You create something, read its id from the response, and use the id in the next URL. Capture the field with jq -r; the -r removes the surrounding quotes, without which the next URL would contain them. Check the variable before relying on it, because a field that is absent comes out as the four letters of the word null, not as an empty string.

id=$(curl -s https://httpbin.org/anything \
  -H "Content-Type: application/json" -d '{"id":"ord_123"}' | jq -r '.json.id')

curl -s "https://httpbin.org/anything/orders/$id" -H "Accept: application/json" | jq -r '.url'

Common mistakes

  • JSON has no single-quoted strings and no trailing commas. {'a':1,} is fine in several programming languages and rejected by every JSON parser.
  • Numbers and strings are different types. "limit":"10" fails validation on an API that expects "limit":10.
  • An error reply is usually JSON too. A 4xx body often names the exact field at fault, so read it before changing the request.

Related curl examples

More under Request bodies

References

These links leave api-tester.com, which is independent of the projects they point to.

FAQ

How do I send a JSON request with curl?

Send the JSON with -d, add -H "Content-Type: application/json" to describe it and -H "Accept: application/json" to ask for a JSON reply.

What is the difference between Content-Type and Accept in a JSON request?

Content-Type describes the body of the request you send. Accept tells the server which format you want in the response.

How do I put a shell variable into a JSON body for curl?

Build the JSON with jq, for example jq -n --arg name "$NAME" '{name: $name}', and pipe it to curl -d @-. jq escapes the value correctly.