curl Parse JSON: Extract Fields from API Responses on the Command Line
Learn how to parse JSON from curl API responses using jq, Python, and Node.js. Extract fields, handle errors, and process nested data from the command line.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →curl fetches data. It doesn't parse it. If your API returns JSON and you need to extract a specific field, pass the curl output through a JSON parser. This guide shows every practical approach — from one-liner jq commands to full scripts in Python and Node.js.
The Basic Pattern
curl -s https://api.example.com/users/1 | jq '.name'
The -s flag silences curl's progress output so only the JSON reaches jq. The dot notation navigates the JSON structure. This is the 90% case.
Installing jq
jq is the standard tool for JSON on the command line. If you don't have it:
# Debian/Ubuntu
sudo apt-get install jq
# macOS (Homebrew)
brew install jq
# Alpine/Docker
apk add jq
# Windows (Chocolatey)
choco install jq
Verify: jq --version
jq Field Extraction
Single field
curl -s https://api.github.com/users/octocat | jq '.login'
# → "octocat"
Strip quotes from string output
curl -s https://api.github.com/users/octocat | jq -r '.login'
# → octocat (no quotes — useful for shell variable assignment)
Multiple fields
curl -s https://api.github.com/users/octocat | jq '{name: .name, followers: .followers}'
# → {"name":"The Octocat","followers":14000}
Nested fields
curl -s https://api.example.com/order/123 | jq '.shipping.address.city'
Array index
curl -s https://api.example.com/users | jq '.[0].email'
All items in array
curl -s https://api.example.com/users | jq '.[].email'
Filter array by condition
curl -s https://api.example.com/users | jq '.[] | select(.active == true) | .email'
Assign to shell variable
TOKEN=$(curl -s https://api.example.com/auth \
-d '{"user":"admin","pass":"secret"}' \
-H 'Content-Type: application/json' | jq -r '.token')
echo "Got token: $TOKEN"
Sending JSON with curl
When the API requires a JSON body:
# POST with JSON body
curl -s -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
-d '{"name":"Alice","email":"[email protected]"}' | jq '.'
# POST from a file
curl -s -X POST https://api.example.com/batch \
-H 'Content-Type: application/json' \
-d @payload.json | jq '.results | length'
# PUT request
curl -s -X PUT https://api.example.com/users/1 \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer $TOKEN' \
-d '{"name":"Alice Updated"}' | jq '.id'
Handling Errors
Check HTTP status code alongside response body
HTTP_CODE=$(curl -s -o /tmp/response.json -w "%{http_code}" https://api.example.com/users/1)
if [ "$HTTP_CODE" -ne 200 ]; then
echo "Error $HTTP_CODE: $(cat /tmp/response.json | jq '.message')"
exit 1
fi
jq '.name' /tmp/response.json
The -w "%{http_code}" flag writes only the status code to stdout; -o writes the body to a file. This separates the two so you can check both.
Handle missing fields gracefully
# Returns null instead of an error if field is missing
curl -s https://api.example.com/user | jq '.email // "no email"'
# Exit 1 if field is null/missing
curl -s https://api.example.com/user | jq -e '.email' > /dev/null || echo "email missing"
jq -e sets exit code 1 for null/false results — useful for scripting.
Validate JSON before parsing
response=$(curl -s https://api.example.com/data)
if echo "$response" | jq empty 2>/dev/null; then
echo "$response" | jq '.data'
else
echo "Invalid JSON response: $response"
fi
jq empty exits 0 for valid JSON and 1 for invalid — no output, just a status code.
Python Alternative (No jq Required)
Python's standard library handles JSON without installing anything:
curl -s https://api.example.com/users | python3 -c "
import json, sys
data = json.load(sys.stdin)
print(data['name'])
"
# Multi-field extraction
curl -s https://api.example.com/users | python3 -c "
import json, sys
data = json.load(sys.stdin)
for user in data:
print(user['id'], user['email'])
"
# Save to variable in bash script
NAME=$(curl -s https://api.example.com/user/1 | python3 -c "import json,sys; print(json.load(sys.stdin)['name'])")
Python gives you full error handling but requires more typing than jq for simple field extraction.
Node.js Alternative
curl -s https://api.example.com/data | node -e "
let d=''; process.stdin.on('data',c=>d+=c).on('end',()=>{
const json = JSON.parse(d);
console.log(json.name);
});"
For complex transformations in Node.js, writing a script file is cleaner:
// parse.js
process.stdin.setEncoding('utf8');
let data = '';
process.stdin.on('data', chunk => data += chunk);
process.stdin.on('end', () => {
const json = JSON.parse(data);
console.log(JSON.stringify(json.users.filter(u => u.active), null, 2));
});
// Run it
// curl -s https://api.example.com/users | node parse.js
Prettify JSON from curl
# Pretty-print with jq
curl -s https://api.example.com/data | jq '.'
# Pretty-print with Python
curl -s https://api.example.com/data | python3 -m json.tool
# Pretty-print with Node.js
curl -s https://api.example.com/data | node -e "
let d=''; process.stdin.on('data',c=>d+=c).on('end',()=>
console.log(JSON.stringify(JSON.parse(d),null,2)))"
Dealing with Malformed JSON Responses
Some APIs return almost-valid JSON — with trailing commas from templating, or BOM characters, or markdown-wrapped responses from AI endpoints. When jq fails with parse error, the response isn't valid JSON.
curl -s https://api.example.com/data | jq '.' 2>&1
# parse error (Unexpected trailing comma) at line 3, column 14
Quick fix — strip trailing commas before parsing:curl -s https://api.example.com/data \
| sed 's/,\s*\([}\]]\)/\1/g' \
| jq '.'
For AI API responses (OpenAI, Claude, Gemini) that return JSON wrapped in markdown fences or with other syntax issues, pipe through AI JSONMedic's repair API before parsing:
curl -s https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_KEY" \
-d '{"model":"gpt-4","messages":[{"role":"user","content":"Return JSON: {name, age}"}]}' \
| jq -r '.choices[0].message.content' \
| curl -s -X POST https://aijsonmedic.com/api/repair --data-binary @- \
| jq '.'
This pipeline: fetches the AI response → extracts the message content → repairs the JSON → parses with jq.
curl + jq Cheat Sheet
| Task | Command | |||
|---|---|---|---|---|
| Get field | curl -s URL \ | jq '.field' | ||
| Get field (no quotes) | curl -s URL \ | jq -r '.field' | ||
| Pretty print | curl -s URL \ | jq '.' | ||
| Nested field | curl -s URL \ | jq '.a.b.c' | ||
| Array index | curl -s URL \ | jq '.[0]' | ||
| Array map | curl -s URL \ | jq '.[].name' | ||
| Filter + extract | curl -s URL \ | jq '.[] \ | select(.active) \ | .id' |
| Multiple fields | curl -s URL \ | jq '{id: .id, name: .name}' | ||
| Default if null | curl -s URL \ | jq '.name // "unknown"' | ||
| Array length | curl -s URL \ | jq '.items \ | length' | |
| Validate only | curl -s URL \ | jq empty | ||
| HTTP status + body | curl -s -o body.json -w "%{http_code}" URL |
Common Mistakes
Forgetting -s: Without-s, curl's progress output mixes with the JSON and breaks parsing.
Unquoted variable expansion: jq ".$FIELD" works but if $FIELD contains special characters, it breaks. Use jq --arg f "$FIELD" '.[$f]' instead.
Windows line endings: If the JSON came from a Windows API or file, CRLF line endings can confuse some parsers. Strip them with tr -d '\r' before piping to jq.
Response is not JSON: APIs often return HTML error pages when the server is down or the URL is wrong. Check the response content type header: curl -sI URL | grep content-type. If it says text/html, fix the request first.
For one-off repair and inspection of curl responses that won't parse, the AI JSONMedic validator lets you paste the raw response and see exactly what's wrong.
FAQ
How do I parse JSON from a curl response in Bash?
Pipe the curl output to jq: curl -s https://api.example.com/data | jq '.'. For a specific field: curl -s url | jq '.fieldName'. If jq is not installed, use Python: curl -s url | python3 -m json.tool for pretty-printing, or curl -s url | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['field'])".
Why does jq say "parse error: Invalid numeric literal" on a curl response?
The response is not valid JSON. Common causes: the API returned an HTML error page (check curl -I url to see the Content-Type), the response is wrapped in a callback (JSONP format), or the body is empty. Add -v to curl to see the full response including headers: curl -sv url 2>&1 | head -50.
How do I save a curl response to a file and also validate it?
Use curl -s url | tee response.json | jq '.'. This saves to response.json while also piping through jq for validation. If jq fails, the raw response is still saved in the file for inspection. Add -f to jq to make it exit with a non-zero code on parse failure: curl -s url | tee response.json | jq -e '.'.
How do I handle curl responses that are not UTF-8?
Use iconv to convert before parsing: curl -s url | iconv -f ISO-8859-1 -t UTF-8 | jq '.'. For Windows CRLF line endings (common from Windows APIs), strip carriage returns first: curl -s url | tr -d '\r' | jq '.'. If the response has a BOM, strip it: curl -s url | sed '1s/^\xEF\xBB\xBF//' | jq '.'.
Can I validate JSON from a curl response without installing jq?
Yes — use Python's built-in json module: curl -s url | python3 -m json.tool. It validates and pretty-prints in one step, with no external dependencies. For just a pass/fail check: curl -s url | python3 -c "import json,sys; json.load(sys.stdin); print('valid')".
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles