POST JSON Data with cURL — Complete Examples Guide
Everything you need to POST JSON data with cURL — from simple single-field objects to nested arrays with authentication headers. Each example is tested and ready to use. Includes debugging tips, common errors, and Windows-specific syntax.
-X POST
flag to set HTTP method to POST
-H
"Content-Type: application/json" required
-d
or --data-raw for the request body
@file.json
read body from a file with -d @filename
Simple POST Examples
The three required pieces
Every JSON POST needs three things: (1) -X POST to set the method, (2) -H "Content-Type: application/json" to tell the server what format you're sending, and (3) -d with the JSON body. Miss any one and the API will likely return 400 or 415.
# Minimal JSON POST
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
# With API key authentication
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key-here" \
-d '{"name":"Alice"}'
# With Bearer token (JWT) authentication
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..." \
-d '{"name":"Alice"}'
# Show HTTP status code at the end of the response
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' \
-w "\nHTTP Status: %{http_code}\n"
# Pretty-print the JSON response
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' | python3 -m json.tool
# Or with jq (more powerful)
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' | jq '.'Nested and Complex JSON
# Nested object with array
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{
"customer": {
"name": "Alice",
"email": "alice@example.com"
},
"items": [
{"productId": "P001", "qty": 2, "price": 29.99},
{"productId": "P002", "qty": 1, "price": 79.99}
],
"total": 139.97,
"paid": true,
"metadata": null
}'
# From a JSON file — cleanest approach for complex payloads
cat > order.json << '''EOF'''
{
"customer": {"name": "Alice"},
"items": [
{"productId": "P001", "qty": 2},
{"productId": "P002", "qty": 1}
]
}
EOF
curl -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d @order.jsonUse -d @file.json for complex payloads
-d @file.json. It eliminates shell quoting issues entirely and is much easier to read, version-control, and modify. The @ prefix tells cURL to read the content from the named file.PUT and PATCH Examples
# PUT — replace entire resource (all fields required)
curl -X PUT https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token" \
-d '{"name":"Alice Updated","email":"alice@example.com","status":"active"}'
# PATCH — partial update (only include fields you want to change)
curl -X PATCH https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token" \
-d '{"status":"inactive"}'
# DELETE with JSON body (some APIs require this)
curl -X DELETE https://api.example.com/users/123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token" \
-d '{"reason":"user_request"}'Real API Examples
# OpenAI Chat Completion API
curl -X POST https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}],
"max_tokens": 100,
"temperature": 0.7
}'
# Anthropic Claude API
curl -X POST https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, Claude"}]
}'
# GitHub — create an issue
curl -X POST https://api.github.com/repos/owner/repo/issues \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{"title":"Bug: login fails","body":"Steps to reproduce...","labels":["bug"]}'
# Slack — send message via incoming webhook
curl -X POST https://hooks.slack.com/services/T.../B.../xxx \
-H "Content-Type: application/json" \
-d '{"text":"Deploy complete! 🚀","username":"DeployBot"}'
# Note: Stripe uses form encoding, NOT JSON
curl -X POST https://api.stripe.com/v1/payment_intents \
-H "Authorization: Bearer sk_test_xxx" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "amount=2000¤cy=usd&automatic_payment_methods[enabled]=true"Windows-Specific Syntax
# Windows CMD — use ^ for line continuation, escape inner double quotes with backslash
curl -X POST https://api.example.com/users ^
-H "Content-Type: application/json" ^
-d "{\"name\":\"Alice\",\"age\":30}"
# PowerShell — backtick for line continuation, single quotes work here
curl -X POST https://api.example.com/users `
-H "Content-Type: application/json" `
-d '{"name":"Alice","age":30}'
# Best approach for Windows: write JSON to a file first, avoid quoting issues
'{"name":"Alice","age":30}' | Out-File -Encoding utf8 body.json
curl -X POST https://api.example.com/users `
-H "Content-Type: application/json" `
-d "@body.json"
# PowerShell with Invoke-RestMethod (alternative to curl)
$body = @{ name = "Alice"; age = 30 } | ConvertTo-Json
Invoke-RestMethod -Uri https://api.example.com/users `
-Method POST `
-ContentType "application/json" `
-Body $bodyDebugging cURL Requests
# -v (verbose): shows request headers, response headers, and SSL handshake
curl -v -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
# Output: > REQUEST HEADERS < RESPONSE HEADERS BODY
# -i: include response headers in output (less verbose than -v)
curl -i -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
# -w: custom output format — show only status code and timing
curl -s -o /dev/null \
-w "Status: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\n" \
-X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}'
# -s -S: silent (no progress bar) but still show errors
curl -s -S -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' | jq .
# Validate JSON before sending (save a broken request trip)
echo '{"name":"Alice","age":30}' | python3 -m json.tool
# If invalid, python prints the error. If valid, it pretty-prints the JSON.Common cURL POST Errors and Fixes
| Item | Error | Cause + Fix |
|---|---|---|
| 415 Unsupported Media Type | Missing Content-Type header | Add -H "Content-Type: application/json" to every JSON POST |
| 400 Bad Request | Malformed JSON in the request body | Validate JSON first: echo '...' | python3 -m json.tool |
| 401 Unauthorized | Missing or expired auth token | Check Authorization header value, refresh token if expired |
| curl: (6) Could not resolve host | DNS failure or wrong hostname | Check hostname, test with curl -v to see DNS resolution attempt |
| curl: (35) SSL connect error | TLS/certificate issue | Add -k to skip verification (test only) or specify --cacert |
| Empty response body (204) | API returns 204 No Content | Expected behavior — check status code with -w "%{http_code}" |
-d vs --data-raw
-d treats @ as a filename prefix: -d @file.json reads the file. --data-raw sends the exact string as-is, treating @ as a literal character. Use --data-raw when your JSON contains @ symbols in string values (like email addresses in the literal body string, not via file).
Silent mode for scripts
In scripts and CI pipelines, use -s (silence progress bar) with -S (still show errors) and -f (fail on HTTP errors): curl -sSf -X POST url -d @body.json. The -f flag makes curl exit with code 22 on 4xx/5xx responses.
Timing breakdown
Use -w with timing variables: %{time_namelookup} %{time_connect} %{time_starttransfer} %{time_total} to see where time is spent — DNS, TCP connect, TTFB, and total. Useful for diagnosing slow API calls.
Retries with --retry
For transient failures: curl --retry 3 --retry-delay 2 --retry-all-errors -X POST url -d @body.json. Retries on network errors and 5xx responses up to 3 times with 2 second delays between attempts.