Use curl --form when you need a fast, scriptable file upload test that behaves close to a real browser form. Use HTTPie when you want the same request to be easier to read. Use Postman, Insomnia, Bruno, or test runners when you need teams, saved collections, or repeatable checks.
TLDR: curl --form sends multipart/form-data, which is what most APIs expect for file uploads and mixed form fields. For example, curl -F "avatar=@me.png" -F "name=Rina" https://api.example.com/users uploads a file and a name in one request. In a small QA case, a team testing 60 image uploads cut manual browser testing from 25 minutes to about 4 minutes with a curl script. HTTPie is nicer for humans, but curl is still the tiny robot you can drop into CI at 2 a.m.
Table of Contents
What curl --form actually does
curl --form, also written as curl -F, builds a form request. Not a cute contact form. A real HTTP request using multipart/form-data.
That matters when an API accepts files. Think profile photos. CSV imports. PDFs. Audio clips. Bug screenshots. The API does not want plain JSON there. It wants chunks. Fields. Boundaries. Weird headers. The boring stuff curl handles for you.
curl -X POST https://api.example.com/upload \
-F "title=Office cat" \
-F "image=@cat.png"
This sends two parts:
- title: a normal text field.
- image: the contents of
cat.png.
The @ is the tiny magic sign. It tells curl, “Read this from a file.” Without it, curl sends the file name as text. That mistake is boring. It is also very common.
The useful curl form patterns
Here are the commands you will use most often.
Send text fields:
curl -F "name=Milo" -F "role=admin" https://api.example.com/users
Send one file:
curl -F "receipt=@receipt.pdf" https://api.example.com/receipts
Set a file content type:
curl -F "avatar=@me.png;type=image/png" https://api.example.com/profile
Send a field that starts with @ but is not a file:
curl --form-string "note=@not_a_file" https://api.example.com/notes
--form-string saves you when user input contains strange characters. It stops curl from treating @something as a file path. Honestly, it feels like the kind of bug that waits until Friday afternoon.
curl versus HTTPie
curl is everywhere. Linux servers have it. macOS has it. Docker images often have it. CI jobs love it. It is small, sharp, and not very cuddly.
HTTPie is friendlier. Its commands read more like plain speech. It formats responses nicely. Headers look good. JSON looks good. Your eyes complain less.
A similar HTTPie upload can look like this:
http -f POST https://api.example.com/upload \
title="Office cat" \
image@cat.png
That is clean. It is also easier to teach to a tired teammate who has had too much coffee and not enough lunch.
Pick curl when:
- You need a command for a shell script.
- You are testing inside CI.
- You want fewer tool installs.
- You need exact control over headers and body parts.
- You are copying a request from docs or logs.
Pick HTTPie when:
- You want readable commands.
- You inspect JSON responses a lot.
- You demo requests to people.
- You hate quoting rules with a fiery passion.
- You are learning an API and want less noise.
The catch is that HTTPie may not be installed on the server where your test must run. curl usually is. That one detail can cost five extra minutes during a release. It drives me a little nuts.
What about Postman?
Postman is great when you need a visual tool. You can choose method, URL, headers, body type, and files with clicks. For form uploads, pick Body, then form-data, then set a field to File.
Postman is strong for:
- Shared collections.
- Customer support debugging.
- Environment variables.
- OAuth flows.
- Manual testing by non-coders.
But it can feel heavy for one upload test. Opening the app, finding the collection, picking the right environment, and checking a token can take longer than running one curl command. Expect to waste time on tiny sync issues if your team has many collections with similar names.
Other API testing alternatives
Insomnia is a polished desktop API client. It is clean and fast. Many developers prefer it over Postman for simple request work.
Bruno stores API collections as files. That is nice for Git. You can review changes in pull requests. No mystery cloud state. That makes some teams breathe easier.
Hoppscotch runs in the browser. It is handy for quick tests. It is also good when you do not want to install another desktop app.
VS Code REST Client lets you keep requests in .http files. This is excellent for docs that also run. A request can sit next to the code it tests.
Newman runs Postman collections from the command line. Use it when your team already lives in Postman but wants automated tests.
k6 is better for load testing. It is not just “does upload work?” It asks, “Can 500 people upload at once without the server melting?” That is a different beast.
API testing by use case
- One quick file upload: use curl.
- Readable terminal test: use HTTPie.
- Shared manual testing: use Postman or Insomnia.
- Git friendly collections: use Bruno or REST Client.
- CI smoke tests: use curl, HTTPie, or Newman.
- Load tests: use k6.
For example, say your team ships a receipt scanner. The upload endpoint accepts a PDF, a user ID, and a source label. Start with curl:
curl -F "userId=4821" \
-F "source=mobile" \
-F "file=@receipt.pdf;type=application/pdf" \
https://api.example.com/scan
If that works, save it. Add a token. Put it in CI. Run it after each deploy. Now you have a tiny guard dog watching the upload endpoint.
Common curl form mistakes
Forgetting the @ sign. This sends plain text instead of file bytes.
curl -F "file=report.pdf" https://api.example.com/upload
Bad. Use this:
curl -F "file=@report.pdf" https://api.example.com/upload
Setting Content-Type yourself. Do not do this unless you really know why. curl creates the multipart boundary. If you override the header, you may break the request.
Using the wrong field name. If the API expects avatar, sending file may fail. Check the docs. Then check them again while grumbling.
Testing only happy paths. Try a huge file. Try the wrong type. Try an empty file. Try no file. APIs often act brave until a zero-byte upload walks in.
Best practice combo
Use curl as your first proof. It is direct. It is honest. It shows what the API accepts without a fancy interface hiding the mess.
Then use HTTPie when you want nicer reading. Use Postman or Insomnia when people need to share and click. Use Bruno or REST Client when requests should live beside code. Use k6 when speed and pressure matter.
The simple rule is this: curl is the pocket knife. HTTPie is the friendly pocket knife with a nicer handle. Full API clients are the toolbox. Pick the smallest tool that gets the request tested without making everyone sigh.


