The maintainable curl command has four visible parts: method, URL, headers, and body. Build those first. Flags like --retry, --compressed, and --resolve solve specific problems; piling them on makes a request impossible to audit.
A request worth keeping
curl --fail-with-body --silent --show-error \
--request POST 'https://api.example.test/v1/widgets' \
--header 'Authorization: Bearer '"$API_TOKEN" \
--header 'Content-Type: application/json' \
--data @widget.json
--fail-with-body gives CI a non-zero exit on a 4xx/5xx while retaining the error response. --data @file is better than an inline JSON quote maze, and avoids accidentally recording a secret in shell history.
Flags by purpose
| Need | Flag | Caveat |
|---|---|---|
| See request/response headers | -i or -v | -v can expose credentials in logs |
| Follow a documented redirect | -L | Do not hide an unexpected redirect during debugging |
| Set a timeout | --max-time 20 | Pair with a connect timeout for remote calls |
| Save response headers | -D headers.txt | Useful for rate-limit and pagination inspection |
Do not put a bearer token directly in a shell command copied into tickets or docs. Environment variables are not perfect secret storage, but they are easier to keep out of history and screenshots.
Debug protocol failures, not just JSON
An API returning HTML often means you hit a proxy, login page, or incorrect host—not that JSON parsing broke. Start with:
curl -sv --output /dev/null https://api.example.test/health
Inspect DNS, the negotiated TLS name, status code, and response headers. Once the transport is right, add the endpoint-specific method and payload. A command builder is useful as a scratchpad, but the final command should still explain itself to the next developer.
