Moving from reading to writing: sending data with POST, setting PUT, PATCH, and DELETE methods, auto-encoding form values, and uploading files via multipart form data.

Up to episode 4, you were mostly reading: fetching pages, viewing headers, following redirects. Now it's time for the more interesting side — writing: sending data to a server with POST, replacing resources with PUT, deleting with DELETE, and uploading files. This is the gateway to real API testing.
Why is this episode important? Because in the real world, you rarely only do GET. Creating new users, sending messages, uploading reports, deleting data — all of these are requests that carry a payload. You can't rely on the browser's Developer Tools forever; with curl, every request can be repeated, scripted, and analyzed precisely.
From episode 0, we already know the HTTP methods. Now see how curl maps them:
| Method | Function | Curl |
|---|---|---|
| GET | Read data | Default without a data option |
| POST | Create a new resource | -X POST -d "..." |
| PUT | Replace a resource entirely | -X PUT -d "..." |
| PATCH | Modify part of a resource | -X PATCH -d "..." |
| DELETE | Delete a resource | -X DELETE |
One important fact that will save you from confusion: the -d option already automatically changes the method to POST. That means curl -d "a=1" URL and curl -X POST -d "a=1" URL produce the same result. Writing -X POST explicitly is more for clarity — a practice many people prefer.
-d: Form-URL-EncodedThe most basic way to send data is with -d (--data). The data is sent as application/x-www-form-urlencoded — the same format as a regular HTML form: name=value pairs separated by &.
curl -X POST -d "name=Arman&role=devops" https://httpbin.org/postLet's dissect it: -d "name=Arman&role=devops" defines two field pairs. Curl sends them as the body with Content-Type: application/x-www-form-urlencoded. As an exercise, run it and notice in the output: httpbin.org/post will return JSON containing form with the name and role fields — proof that the data arrived correctly.
Tip
Type -d "name=Arman&role=devops" exactly as in the example, then note that spaces and special characters inside values must be encoded manually. If you're too lazy to count manual encodes, don't worry — the solution is in the next section.
--data-urlencode: Let Curl Do the EncodingThe problem arises when values contain spaces, & signs, or special characters — for example the name "Arman Dwi Pangestu". If written raw in -d, the data will be parsed incorrectly by the server.
The solution: the --data-urlencode option, which automatically encodes values according to URL rules:
curl -X POST \
--data-urlencode "name=Arman Dwi Pangestu" \
--data-urlencode "notes=suka 100% dengan curl" \
https://httpbin.org/postNotice: spaces will be sent as %20, % signs as %25, and so on. The server will receive complete, correct data. The rule of thumb: always use --data-urlencode for values that come from human input — names, messages, query parameters — and -d for data that's already safe.
After POST, three other methods complete the CRUD operations. The pattern is the same — set the method with -X, send data with -d if needed:
curl -X PUT -d "title=Episod Kelima&content=lorem" https://httpbin.org/putcurl -X DELETE https://httpbin.org/deletecurl -X PATCH -d "title=Judul Diperbarui" https://httpbin.org/patchThe semantics: PUT replaces a resource entirely, PATCH only the parts sent, DELETE removes it. Many APIs follow these semantics strictly, so get used to choosing the right method from the start — not just -X POST for everything.
-FWhen the data being sent is a file — or a combination of fields and files, like a registration form with a photo — the application/x-www-form-urlencoded format is no longer adequate. That's where multipart form data (multipart/form-data) comes in: the same format browsers use when a <form> uploads files.
The curl option for this is -F (--form). Its format is similar to -d, with one big difference: values prefixed with @ mean contents of a file.
curl -X POST \
-F "name=Arman" \
-F "avatar=@/tmp/foto.jpg" \
https://httpbin.org/postIn the example above, -F "name=Arman" sends a plain text field, while -F "avatar=@/tmp/foto.jpg" sends the contents of the file /tmp/foto.jpg as a file named foto.jpg. The server receives both in one multipart body — see in the httpbin.org/post output: the form section contains name, and the files section contains avatar.
Important
The direction of @ determines behavior: -F "file=@path" sends the file contents from the path, while -F "file=path" sends the string path as-is as the field value. This @ prefix is also used by -d and -T — it's the "read from file" symbol in the curl world. To change the sent file name, use ;filename= like -F "avatar=@/tmp/foto.jpg;filename=profil.jpg".
-d vs -F: When to Use Which?| Need | Use | Content-Type |
|---|---|---|
| Simple text fields | -d | application/x-www-form-urlencoded |
| Values that need auto-encoding | --data-urlencode | Same as -d |
| File upload / mix of fields + files | -F | multipart/form-data |
The analogy: -d is like filling a form with a pen — fast for short data. -F is like sending a package with attachments — needed when there are files. Both are valid; just match them to what the server needs.
Combining -d and -F in one request. Both set Content-Type differently and conflict with each other. Choose one — don't mix them.
Forgetting @ when uploading a file. -F "file=path" sends the text path, not the file contents. The @ prefix is the main differentiator.
Writing -X DELETE without understanding the server. Some frameworks and legacy servers require a POST method with the _method=delete field. Always read the documentation of the API you're targeting.
Expecting -d to automatically send JSON. -d sends form-url-encoded. For JSON, you need to set the Content-Type: application/json header — a topic we'll cover in episode 6.
Note
If you want to send data from a file as the body, curl also supports -d @file — the @ prefix means "read from this file". This is very useful when the request body is stored as a template file, for example -d @payload.json. This pattern will come up again in the JSON & REST API testing episode.
In this episode 5, you've moved from just reading to writing: sending form data with -d, making sure values are safe with --data-urlencode, choosing PUT/PATCH/DELETE methods with -X, and uploading files with multipart -F.
Key takeaways:
-d automatically makes the request a POST; -X chooses the method explicitly.--data-urlencode handles value encoding — a must for human input.-F = multipart for file uploads; @ means "contents of a file".-d and -F in a single request.In the next episode 6, we'll combine everything for JSON & REST API testing — sending JSON bodies with the Content-Type: application/json header, reading JSON responses with jq, and building a complete CRUD flow against an API. See you in episode 6!