curl
The universal HTTP client. Every ML API can be probed with curl before you write a line of Python — it's the fastest way to check a route, a header, or a 500 error.
Table of Contents
Installation / Setup
# macOS (ships built-in) / Linux (usually preinstalled)
curl --version
# Windows 10+: curl.exe ships with Windows; in PowerShell use curl.exe
# (plain `curl` is aliased to Invoke-WebRequest — use curl.exe to avoid surprises)
# install on Debian/Ubuntu if missing
sudo apt install curl
| Platform |
Invoke |
| macOS / Linux |
curl https://api.example.com |
| Git Bash (Windows) |
curl ... |
| PowerShell |
curl.exe ... (avoid the curl alias) |
| CMD (Windows) |
curl ... |
Core Syntax
| Option |
Short |
Purpose |
Example |
--request |
-X |
HTTP method (usually unnecessary) |
-X POST |
--data |
-d |
Body data (sets POST) |
-d '{"x":1}' |
--header |
-H |
Request header |
-H "Content-Type: application/json" |
--json |
|
Shortcut: JSON body + correct headers |
--json '{"x": 1}' |
--output |
-o |
Write body to a file |
-o resp.json |
--include |
-i |
Show status + headers in output |
-i |
--silent |
-s |
No progress bar |
-s |
--fail |
-f |
No output on HTTP errors (exit code nonzero) |
-f |
--verbose |
-v |
Full request/response debug |
-v |
--user |
-u |
Basic auth |
-u user:pass |
--header |
-H |
Bearer token |
-H "Authorization: Bearer $TOKEN" |
--form |
-F |
multipart form / file upload |
-F "file=@image.jpg" |
--max-time |
-m |
Timeout seconds |
-m 30 |
--connect-timeout |
|
Connect timeout |
--connect-timeout 5 |
Method notes
curl URL alone sends GET.
-d/--json switches the method to POST automatically — no -X POST needed.
- Use
-X PUT/-X DELETE only when the method isn't implied (and mind the -X + -d interplay on redirects).
Common Operations
# GET with query string
curl "http://localhost:8000/search?q=cat&limit=5"
# POST JSON — the standard way to hit a /predict endpoint
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d '{"text": "refund request"}'
# same, modern shorthand
curl --json '{"text": "refund request"}' http://localhost:8000/predict
# read the JSON body from a file (cleaner for long payloads)
curl -X POST http://localhost:8000/predict \
-H "Content-Type: application/json" \
-d @payload.json
# see status code, headers and body together
curl -i http://localhost:8000/health
# upload a file as multipart form data (image classification)
curl -X POST http://localhost:8000/classify -F "file=@dog.jpg"
# send a raw file as the body
curl -X POST http://localhost:8000/embed -H "Content-Type: text/plain" --data-binary @doc.txt
Authentication
# Bearer token (most common for AI APIs)
curl -H "Authorization: Bearer sk-123..." https://api.openai.com/v1/chat/completions \
--json '{"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hi"}]}'
# API key header
curl -H "X-API-Key: $API_KEY" https://api.example.com/v1/models
# Basic auth
curl -u username:password https://api.example.com/private
Practical Examples
Probe a local model server end-to-end
# 1) health check — expect {"status": "ok"}
curl -i http://localhost:8000/health
# 2) real prediction
curl --json '{"tenure": 12, "monthly_charges": 89.5, "contract": "month", "support_calls": 3}' \
http://localhost:8000/predict
# 3) deliberate error — what does a bad payload return?
curl -i --json '{"tenure": "abc"}' http://localhost:8000/predict
Stream a chat completion (OpenAI-compatible)
curl https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Explain PCA in one line"}],
"stream": true}'
curl -sI https://huggingface.co # -I = HEAD request: headers only
Follow redirects and save output
curl -L -o model_card.json https://huggingface.co/api/models/gpt2
Common Patterns
Pretty-print responses
curl -s http://localhost:8000/health | python -m json.tool
# jq gives you field extraction
curl -s http://localhost:8000/predict --json '{"text":"hi"}' | jq .label
Exit codes tell you success/failure
curl -fsS --json '{"text": "hi"}' http://localhost:8000/predict > out.json \
&& echo "HTTP 2xx" || echo "HTTP error (or network failure)"
-f makes curl return a nonzero exit code on HTTP 4xx/5xx — essential in shell scripts and CI.
Timeout so scripts never hang
curl -m 10 -fsS http://localhost:8000/predict --json '{"text": "hi"}'
Test with a real token from env (never inline secrets)
curl -fsS -H "Authorization: Bearer ${API_TOKEN:?set API_TOKEN}" https://api.example.com/v1/models
Best Practices
- Always set
Content-Type: application/json (or use --json) when sending a JSON body.
- Use
-f in scripts so 4xx/5xx responses break the pipeline instead of passing silently.
- Put JSON in a file (
-d @payload.json) for anything longer than one line.
- Use
-m timeouts on every remote call.
- Keep secrets in environment variables, never in the command (shell history risk).
-v when debugging; it shows the exact request curl sends (helps find typos).
- Prefer
--json over -d + explicit header for modern curl (≥ 7.82).
Common Mistakes
| Mistake |
Fix |
curl URL -d '{"a":1}' without Content-Type |
Server sees application/x-www-form-urlencoded — add the header or use --json |
PowerShell curl = Invoke-WebRequest, which mangles output |
Use curl.exe in PowerShell |
-X POST + -d on a URL that redirects |
Redirect becomes POST too; use --data only and let it infer, or add --location-trusted |
Missing -f: scripts "succeed" on 500s |
Add -f and check exit code |
| JSON quoting breaks under nested quotes |
Write payload to a file, use -d @payload.json |
| No timeout against a hung model endpoint |
Add -m 30 |
Windows CMD escaping of " and % |
Use single-quote-free payload files (@payload.json) or Git Bash |
Troubleshooting
| Symptom |
Cause / Fix |
curl: (7) Failed to connect |
Nothing listening on that host:port — start the server, check the port |
curl: (60) SSL certificate problem |
Self-signed local cert; use -k only for local testing (never in prod) |
curl: (35) SSL connect error |
Wrong protocol (http vs https) or proxy interference |
| Empty body on a 500 |
Server crashed after headers; run server with logs, use -v to see headers |
Could not resolve host |
Typos, no DNS, or you're offline |
| Response arrives but is truncated |
Use -L for redirects or -m longer timeout |
Malformed JSON from server |
You posted with wrong Content-Type; check with -v |
- REST API — endpoints and methods you're hitting
- HTTP — status codes and headers semantics
- python-requests — the Python equivalent for scripts
- FastAPI / Flask — the servers you test with curl
- postman — GUI alternative for exploratory testing
- JSON — payload shapes and escaping
- bash — scripting curl inside pipelines
Official Documentation
Last Updated
2026-09-06 — Initial version.