Pretty-Printing JSON Response with HTTP Headers

By Susam Pal on 14 Apr 2024

Often while using curl with URLs that return a JSON response, I need to print the HTTP response headers along with the JSON response. Here is an example that shows how this can be done:

$ curl -sSi https://susam.net/code/lab/json/book.json
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 14 Apr 2024 22:20:49 GMT
Content-Type: application/json
Content-Length: 80
Last-Modified: Sun, 14 Apr 2024 22:17:09 GMT
Connection: keep-alive
ETag: "661c55e5-50"
Accept-Ranges: bytes

{"title": "The Music of the Primes", "author": "Marcus du Satoy", "pages": 366}

The above output is obtained using curl 7.77.0 (x86_64-apple-darwin21.0). The -i option is responsible for including the HTTP response headers. The -s and -S options are not too important for the current discussion but I usually happen to use them out of habit. The -s option suppresses the progress meter and error messages but the -S re-enables the display of error messages. This helps me avoid the progress meter in the output without having to lose visibility of any errors that may arise.

So far so good! But can we also have the JSON response pretty-printed with say jq? The above command prints both the HTTP headers and the response to the standard output, so piping the standard output to jq does not work. The jq command fails with an error as soon as it encounters the HTTP headers.

If, however, we manage to send the HTTP header and the response to different streams or files, then we could utilise jq to pretty-print the stream or file that contains the JSON response. Here is an example that shows how to do this:

$ curl -sSD head.txt -o out.json https://susam.net/code/lab/json/book.json && cat head.txt && jq . out.json
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 14 Apr 2024 22:31:35 GMT
Content-Type: application/json
Content-Length: 80
Last-Modified: Sun, 14 Apr 2024 22:17:09 GMT
Connection: keep-alive
ETag: "661c55e5-50"
Accept-Ranges: bytes

{
  "title": "The Music of the Primes",
  "author": "Marcus du Satoy",
  "pages": 366
}

Alternatively, we can achieve this using a single command by printing the the HTTP headers to standard error. This ensures that only the JSON response is printed to standard output, which we can then pretty-print using jq. Here is an example:

$ curl -sSD /dev/stderr https://susam.net/code/lab/json/book.json | jq .
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 14 Apr 2024 22:34:26 GMT
Content-Type: application/json
Content-Length: 80
Last-Modified: Sun, 14 Apr 2024 22:17:09 GMT
Connection: keep-alive
ETag: "661c55e5-50"
Accept-Ranges: bytes

{
  "title": "The Music of the Primes",
  "author": "Marcus du Satoy",
  "pages": 366
}
Comments | #unix | #shell | #networking | #technology