---
url: https://findutils.com/blog/curl-data-options-explained-d-data-raw-data-urlencode-form-json
title: "curl -d vs --data-raw vs --data-urlencode vs -F vs --json: How curl Sends a Request Body"
description: "What each curl body option really sends, measured: the Content-Type it adds, how @ and newlines are handled, how repeated -d joins, when -G moves data to the URL, and how to match it in fetch and Python requests."
category: developer
content_type: blog
cluster: developer
locale: en
read_time: 9
status: published
author: "olgunozoktas"
published_at: 2026-10-25T15:08:00Z
excerpt: "curl has six ways to send a body, and they differ in small ways that decide whether a server reads your data: -d adds a form Content-Type and strips newlines from files, --data-raw ignores @, --data-binary keeps every byte, --data-urlencode encodes the value, -F builds multipart, and --json sets two JSON headers. Here is what each one put on the wire in our tests, and how to send the same thing from fetch and Python."
tag_ids: ["curl", "http", "api", "forms", "developer-tools"]
tags: ["cURL", "HTTP", "API", "Forms", "Developer Tools"]
primary_keyword: "curl data options"
secondary_keywords: ["curl -d vs --data-raw", "curl --data-binary vs -d", "curl --data-urlencode example", "curl -F vs -d", "curl --json", "curl post form data", "curl -G query string"]
tool_tag: "curl-to-code"
related_tool: "curl-to-code"
related_tools: ["curl-to-code", "url-encoder-decoder", "http-request-builder", "har-to-curl", "json-formatter", "postman-to-curl"]
updated_at: "2026-10-25T15:08:00Z"
og_image: "/images/content/blog/curl-data-options-explained-d-data-raw-data-urlencode-form-json-cover-20261025.webp"
image_alt: "Six labelled lanes (-d, --data-raw, --data-binary, --data-urlencode, -F and --json) each carry a coloured square or round parcel, and their lines merge into one connector that runs into a server rack, on a dark background."
---

curl sends a request body with six options, and they are not interchangeable. `-d` (`--data`) sends the text as `application/x-www-form-urlencoded`, reads a file when the value starts with `@`, and strips newlines from that file. `--data-raw` is the same but treats `@` as a normal character. `--data-binary` sends a file byte for byte. `--data-urlencode` percent-encodes the value first. `-F` (`--form`) builds a `multipart/form-data` body that can carry files. `--json` sends the data with `Content-Type: application/json` and `Accept: application/json`. All of them switch the method to POST unless you set another one, and `-G` moves `-d` data into the URL instead.

We sent each option to a small local server that prints the raw request it receives, using curl 8.14.1. The results are below, followed by how to send the same body from JavaScript and Python.

## The Six Options at a Glance

| Option | Content-Type curl adds | `@file` | Newlines from a file | Encodes the value |
|---|---|---|---|---|
| `-d`, `--data` | `application/x-www-form-urlencoded` | Reads the file | Removed | No |
| `--data-raw` | `application/x-www-form-urlencoded` | Sent as text | (no file) | No |
| `--data-binary` | `application/x-www-form-urlencoded` | Reads the file | Kept | No |
| `--data-urlencode` | `application/x-www-form-urlencoded` | `name@file` reads and encodes | Kept, then encoded | Yes |
| `-F`, `--form` | `multipart/form-data; boundary=...` | Attaches the file | Kept | No (each part is separate) |
| `--json` (curl 7.82.0+) | `application/json`, plus `Accept: application/json` | Reads the file | Kept | No |

Every one of them sets the method to POST. Adding `-X POST` changes nothing; `-X PUT` or `-X PATCH` keeps the same body and Content-Type with the other method.

## -d: The Form Default, Even for JSON

```bash
curl https://api.example.com/login -d 'user=ada&role=admin'
```

arrived as:

```http
POST /login HTTP/1.1
Content-Length: 19
Content-Type: application/x-www-form-urlencoded

user=ada&role=admin
```

curl sends the text exactly as you typed it. It does not encode it: `-d 'name=Ada Lovelace'` arrived with the space still in it, which a strict form parser may reject. It also adds the form Content-Type to anything, including JSON. `curl https://api.example.com/x -d '{"a":1}'` arrived as `Content-Type: application/x-www-form-urlencoded` with a JSON body, so a JSON API that checks the header does not parse it. Add `-H 'Content-Type: application/json'`, or use `--json`.

Two more rules from the curl manual, both confirmed in our tests:

- **Repeated `-d` options are joined with `&`.** `-d user=ada -d role=admin` sent `user=ada&role=admin`.
- **`-d @file` reads a file and removes carriage returns and newlines.** A file with `line1` and `line2` on two lines was sent as `line1line2` (10 bytes). That is fine for a one-line form body and breaks anything where line breaks matter, such as pretty-printed JSON with a string that spans lines, CSV or XML.

## --data-raw: When the Value Starts With @

`--data-raw` behaves like `-d` but never reads a file. `--data-raw '@notafile'` sent the 9 characters `@notafile`, where `-d '@notafile'` would look for a file of that name. Use it for bodies you did not write yourself, such as user input or a value that can start with `@` (a handle, an email-like string). It is also what Chrome and Firefox use when you choose **Copy as cURL** in the Network panel.

## --data-binary: Send a File Unchanged

```bash
curl https://api.example.com/import --data-binary @body.txt
```

sent `line1\nline2\n` (12 bytes), newlines included. Use `--data-binary @file` for JSON, CSV, XML or any file whose bytes must arrive as they are. The Content-Type is still the form type, so set the real one: `-H 'Content-Type: text/csv'`, or `application/octet-stream` for arbitrary bytes, as the curl manual suggests.

## --data-urlencode: Let curl Encode the Value

```bash
curl https://api.example.com/search \
  --data-urlencode 'q=hello world & more' \
  --data-urlencode 'lang=en'
```

sent `q=hello+world+%26+more&lang=en`. The value after the first `=` is encoded (the space became `+`, the `&` became `%26`), so a literal `&` no longer splits the field. The name before the `=` is not encoded, so keep names plain. The manual lists the other forms: `=content` encodes the content without a name, `name@file` encodes the contents of a file, and a value with no `=` or `@` is encoded as a whole. To see how a value is encoded before you send it, paste it into the [URL Encoder/Decoder](/network/url-encoder-decoder/); the post on [URL encoding and query strings](/blog/url-encoding-explained-percent-encoding-and-query-strings/) explains why a space can be `+` in a form and `%20` in a path.

## -F: Multipart, the Only One That Uploads Files

```bash
curl https://api.example.com/upload -F name=Ada -F file=@body.txt
```

sent `Content-Type: multipart/form-data; boundary=...` and two parts: a field `name` with the value `Ada`, and a part with `filename="body.txt"` and `Content-Type: text/plain` holding the file. This is what an HTML form with a file input sends. Two details matter:

- With `-F`, `@` attaches a file as an upload, while `<` puts the file's contents into a normal text field (`-F 'notes=<notes.txt'`).
- Do not set the Content-Type header yourself for multipart. The boundary in the header must match the one in the body, and curl (or your HTTP library) generates both.

`-F` and the `-d` family are two different body types, so do not mix them in one command; curl refuses that combination.

## --json: The Shortcut for JSON APIs

```bash
curl https://api.example.com/items --json '{"name":"Ada"}'
```

sent:

```http
POST /items HTTP/1.1
Content-Type: application/json
Accept: application/json
Content-Length: 14

{"name":"Ada"}
```

According to the manual, `--json` is the same as `--data-binary` plus the two headers, so `--json @payload.json` keeps the newlines in the file. It does not check that the data is valid JSON, so run the body through the [JSON Formatter](/developers/json-formatter/) when a request fails. `--json` was added in curl 7.82.0 (March 2022); on older systems, use `--data-binary` with the two `-H` headers.

## -G: Put the Data in the URL Instead

```bash
curl -G https://api.example.com/search -d q=test -d page=2
```

sent `GET /search?q=test&page=2` with no body and no Content-Type. `-G` (`--get`) turns any `-d` or `--data-urlencode` data into a query string, which is handy for search endpoints. Without `-G`, `-X GET -d q=test` sends a GET with a body, which most servers and proxies ignore.

## Sending the Same Body From fetch and Python

The defaults differ between curl and HTTP libraries, which is the usual reason a request that works in the terminal fails in code. We sent the same form body from each and compared the headers:

| What you send | curl `-d 'user=ada&role=admin'` | `fetch` with the same string | Python requests `data='user=ada&role=admin'` |
|---|---|---|---|
| Content-Type | `application/x-www-form-urlencoded` | `text/plain;charset=UTF-8` | none |

To match curl:

- **fetch, form body:** pass `new URLSearchParams({ user: 'ada', role: 'admin' })`. fetch then sets `application/x-www-form-urlencoded;charset=UTF-8` and encodes the values (`Ada Lovelace` became `Ada+Lovelace`).
- **fetch, JSON:** `body: JSON.stringify(obj)` plus `headers: { 'Content-Type': 'application/json' }`. fetch never sets the JSON type for you.
- **Python requests, form body:** pass a dict, `data={'user': 'ada', 'role': 'admin'}`. requests then sets the form type and encodes the values. A string is sent as it is, with no Content-Type.
- **Python requests, JSON:** `json=obj` sets `application/json` and serialises the object.
- **Multipart:** `FormData` in fetch, `files=` in requests. Neither needs a Content-Type header from you.

The FindUtils [cURL to Code](/developers/curl-to-code/) converter writes these calls from a pasted command, in JavaScript, Python, PHP, Go, Ruby and Node.js. It reads `-d`, `--data-raw`, `--data-binary` and simple `-F name=value` fields; the [cURL to Code guide](/guides/curl-to-code-guide/) lists what it does not read yet (`--json`, `--data-urlencode`, `-G`) and what to check in the output, such as the form Content-Type above. To send a request and inspect the response without writing any code, use the [HTTP Request Builder](/developers/http-request-builder/).

## Which Option Should You Use?

| You want to send | Use |
|---|---|
| A short form body you typed | `-d 'a=1&b=2'` |
| Form fields with spaces, `&` or non-ASCII text | `--data-urlencode 'q=...'` (one per field) |
| A value that may start with `@` | `--data-raw` |
| A JSON body | `--json '{...}'` (curl 7.82.0+), or `-H 'Content-Type: application/json' --data-raw '{...}'` |
| A file exactly as it is | `--data-binary @file` with the right `-H 'Content-Type: ...'` |
| A file upload, as an HTML form does it | `-F 'file=@photo.png'` |
| Query parameters on a GET | `-G` with `-d` or `--data-urlencode` |

Need help with a project? The team behind FindUtils takes on [freelance work](https://www.upwork.com/freelancers/~010a5875eab37b4bd7), such as [a fast React front end for your web app, dashboard or admin panel](https://www.upwork.com/services/product/development-it-a-fast-react-front-end-for-your-web-app-dashboard-or-admin-panel-2112446421681618043).

## FAQ

### What is the difference between curl -d and --data-raw?

Only the `@` character. `-d @name` reads the file `name` (and removes its newlines), while `--data-raw @name` sends the text `@name`. Both send the form Content-Type.

### Does curl -d URL-encode the data?

No. `-d` sends the text exactly as given. Use `--data-urlencode` for each field whose value has spaces, `&`, `=` or non-ASCII characters.

### Why does my JSON sent with curl -d fail?

curl labels `-d` bodies `application/x-www-form-urlencoded`. Add `-H 'Content-Type: application/json'`, or use `--json`, which also adds `Accept: application/json`.

### When should I use -F instead of -d?

When the server expects `multipart/form-data`, which is always the case for file uploads from an HTML form. Use `-d` or `--data-urlencode` for plain form fields without files.

### How do I send curl data as query parameters?

Add `-G`. `curl -G https://api.example.com/search -d q=test` sends `GET /search?q=test`.

## Related Reading

- [cURL to Code guide](/guides/curl-to-code-guide/)
- [URL encoding explained: percent-encoding and query strings](/blog/url-encoding-explained-percent-encoding-and-query-strings/)
- [Postman to cURL: portable API workflows](/blog/postman-to-curl-portable-api-workflows/)
- [HTTP Request Builder guide](/guides/http-request-builder-guide/)

## Sources

- [curl man page](https://curl.se/docs/manpage.html) (`-d`, `--data-raw`, `--data-binary`, `--data-urlencode`, `-F`, `--json`, `-G`)
- [Everything curl: Simple POST](https://everything.curl.dev/http/post/simple.html), [URL encoding](https://everything.curl.dev/http/post/url-encode.html), [JSON](https://everything.curl.dev/http/post/json.html) and [Multipart formposts](https://everything.curl.dev/http/post/multipart.html)
- [MDN: RequestInit (body)](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit)
- [Requests: Quickstart, More complicated POST requests](https://requests.readthedocs.io/en/latest/user/quickstart/)
- [WHATWG URL Standard: application/x-www-form-urlencoded](https://url.spec.whatwg.org/#application/x-www-form-urlencoded)
- [RFC 7578: Returning Values from Forms: multipart/form-data](https://www.rfc-editor.org/rfc/rfc7578)
