Developer Guides

cURL POST Request: JSON, Forms and Files

Send a cURL POST request with JSON, encoded forms or multipart files. Check body options, save responses and avoid uncertain duplicate submissions.

Download example files
Comparison of inline JSON, JSON file input and multipart file upload in cURL POST requests.

To send a cURL POST request, choose the body format the endpoint accepts. --json sends JSON with suitable default headers, --data-urlencode encodes form values and --form creates a multipart upload. You usually do not need -X POST when a body option already selects POST.

The examples use a local echo server to show what arrived. Start the supplied demo_server.py with python3 demo_server.py, then run the commands below. They use Bash syntax; on Windows, call curl.exe and adapt quoting for your shell.

Workflow at a glance

  1. 1. Send JSON with a cURL POST request

    The receiver reports method: POST, a JSON content type and the object in its json field. --json adds JSON Content-Type and Accept headers unless you replace them. It does not validate arbitrary text as JSON before sending it.

  2. 2. Send form fields without breaking special characters

    The echoed query value is desk & lamp. Encoding prevents the ampersand inside the value from being interpreted as another form field separator.

  3. 3. Send a JSON file or multipart upload

    Save the following fictional object as payload.json:

  4. 4. Choose the option that preserves the intended bytes

    Avoid mixing body modes in the same command unless the manual explicitly documents the combination. -X POST changes the method string; it does not turn an incorrectly encoded body into valid JSON or multipart data.

  5. 5. Save the response body and status separately

    The body goes into response.json, headers into their own file and the status into the terminal. --fail-with-body makes HTTP error responses fail while retaining the body for inspection. Check the command's exit code as well as the HTTP code; a network failure may have no usable HTTP response.

  6. 6. Check redirects and retry behavior

    Adding --location changes the workflow. A redirect can lead to a different method depending on its status and the options used. Do not add -X POST as a universal fix: it can force a method string in ways that differ from normal body handling.

1. Send JSON with a cURL POST request

curl --fail-with-body -sS --max-time 30 \
  --json '{"category":"office","limit":3}' \
  'http://127.0.0.1:8766/echo'

The receiver reports method: POST, a JSON content type and the object in its json field. --json adds JSON Content-Type and Accept headers unless you replace them. It does not validate arbitrary text as JSON before sending it.

Check your installed version with curl --version. --json is available from cURL 7.82.0. On an older version, send the body and header explicitly:

curl --fail-with-body -sS --max-time 30 \
  -H 'Content-Type: application/json' \
  --data-binary '{"category":"office","limit":3}' \
  'http://127.0.0.1:8766/echo'

The current cURL manual documents these options. The local server validates JSON, so malformed JSON produces a 400 response you can inspect.

2. Send form fields without breaking special characters

curl --fail-with-body -sS --max-time 30 \
  --data-urlencode 'category=office' \
  --data-urlencode 'query=desk & lamp' \
  'http://127.0.0.1:8766/echo'

The echoed query value is desk & lamp. Encoding prevents the ampersand inside the value from being interpreted as another form field separator.

Plain -d 'a=1&b=2' is useful when you intentionally provide an already structured form body. It does not automatically encode every character inside a value. Choose encoding based on the data, not just on which command looks shorter.

3. Send a JSON file or multipart upload

Save the following fictional object as payload.json:

{"category":"office","limit":3}

Send it as the request body:

curl --fail-with-body -sS --max-time 30 \
  --json @payload.json \
  'http://127.0.0.1:8766/echo'

For a file-upload endpoint, create sample.csv and use multipart syntax instead:

curl --fail-with-body -sS --max-time 30 \
  --form 'source=tutorial' \
  --form 'file=@sample.csv;type=text/csv' \
  'http://127.0.0.1:8766/echo'

These are different requests. JSON file input sends the file's content as the JSON body. Multipart upload sends a named file part alongside other parts. Let cURL construct the multipart boundary rather than forcing an incomplete Content-Type header.

4. Choose the option that preserves the intended bytes

Task

Option

Detail to check

Inline JSON

--json

JSON syntax and endpoint schema

JSON from a file

--json @payload.json

File content is the body

Encode a form value

--data-urlencode 'key=value'

Reserved characters stay in the value

Send a file's bytes as the body

--data-binary @file

Set the required Content-Type

Send text beginning with @ literally

--data-raw

No file interpretation for that argument

Upload a named file part

--form 'file=@file'

Field name, filename and upload rules

Avoid mixing body modes in the same command unless the manual explicitly documents the combination. -X POST changes the method string; it does not turn an incorrectly encoded body into valid JSON or multipart data.

Comparison of inline JSON, JSON file input and multipart file upload in cURL POST requests.
Sending JSON from a file differs from uploading a file as a multipart part.

5. Save the response body and status separately

curl --fail-with-body -sS --max-time 30 \
  --json '{"category":"office","limit":3}' \
  --dump-header response-headers.txt \
  --output response.json \
  --write-out 'HTTP %{http_code}\n' \
  'http://127.0.0.1:8766/echo'

The body goes into response.json, headers into their own file and the status into the terminal. --fail-with-body makes HTTP error responses fail while retaining the body for inspection. Check the command's exit code as well as the HTTP code; a network failure may have no usable HTTP response.

Verbose output can reveal authentication headers. Use example credentials and avoid sharing raw logs that contain private values.

6. Check redirects and retry behavior

Adding --location changes the workflow. A redirect can lead to a different method depending on its status and the options used. Do not add -X POST as a universal fix: it can force a method string in ways that differ from normal body handling.

For an operation that creates a task, a timeout can leave the outcome uncertain. Check the service's task lookup or call log before sending the creation request again. MIYAIP's Crawler API guide explains that distinction between submitting a task and retrieving it.

If you need to carry a working request into Python, use the converter for its supported inline options. File uploads and complex redirect combinations require manual review rather than silent conversion.

Frequently asked questions

Why is my server receiving form data instead of JSON?

Check which body option you used and the actual Content-Type. -d with a JSON-looking string does not automatically make the request JSON.

Why does --json still send malformed JSON?

It configures request behavior; it is not a JSON validator. Validate the input locally or inspect the API's validation response.

Does a successful POST mean a crawler task is finished?

Not for an asynchronous API. Inspect the documented creation response, keep the task ID and retrieve its result later.

Confirm the method, body format and response in a small request. Then apply the real service's authentication, parameters and task lifecycle.

Move from a test request to the documented API workflow