How to send a POST request with curl: complete reference

How to send a POST request with curl: complete reference

Roughly twenty billion installations of curl run somewhere in the world right now. That number comes from the tool's creator, Daniel Stenberg, who maintains it almost single-handedly. curl ships inside routers, cars, satellites, smart TVs, the Linux servers that hold up most of the public web, and every major LLM runtime. Of all the HTTP verbs those installations push around, POST does the heavy lifting. A curl POST is how most developers test, debug, or integrate with an API for the first time.

Postman's 2025 State of the API Report puts REST at 93% adoption. 82% of organisations now operate at least partly API-first. POST is the verb you reach for whenever you create, submit, or transmit data to a server. AI workloads accelerated that trend further. API traffic attributed to AI usage grew 73% in 2024 (Postman, 2024), and every LLM provider's documentation now opens with a curl POST snippet as the canonical "first call."

This reference walks through every shape a POST request using curl can take, from the one-line minimum to a working call against a real crypto payment API. Aim: something you can paste from, not just read. Sending data to a server for the first time or rebuilding a webhook handler at 2 a.m., the patterns below cover what you actually need.

The table below is the short version. Flag-and-purpose cheat sheet covering the curl command-line options used most often when sending POST requests. Each is unpacked in the sections that follow.

Flag What it does When you reach for it
`-X POST`, `--request POST` Forces the HTTP request method to POST Explicit method, or unusual verbs
`-d`, `--data` Sends data in the request body, sets POST automatically Form fields, inline JSON, simple payloads
`--data-binary` Sends file or binary data without stripping newlines File uploads, large JSON, raw binary data
`--data-urlencode` URL-encodes the value before sending Values with spaces or special characters
`--json` Sends data with `Content-Type: application/json` and `Accept: application/json` curl 7.82.0 or newer, JSON APIs
`-H`, `--header` Adds a custom HTTP header Content-Type, Authorization, API keys
`-F`, `--form` Sends multipart/form-data with form fields or files File uploads, HTML-style forms
`-u`, `--user` Sends HTTP Basic authentication credentials Legacy APIs, simple username-password auth
`-i`, `--include` Includes response headers in the output Inspecting server responses
`-v`, `--verbose` Prints the full request and response, headers included Debugging a failing POST request

Understanding the curl POST request and HTTP method

A curl POST request is an HTTP POST request, dispatched with the POST method, fired from the command line. The curl tool itself describes its job as "transferring data" across more than two dozen protocols, of which HTTP is just one. POST means: here is some data, do something with it. Payload goes in the request body, never in the URL. That property is why POST handles resource creation, form submissions, and anything carrying credentials. GET parks data in the query string, visible to every proxy and every browser history. POST does not.

Most tutorials skip a small but useful detail. Pass `-d` and curl auto-switches to POST. The explicit `-X POST`, which specifies a custom request method to use, is optional. Many examples still include it because reading `-X POST` next to a payload makes the intent obvious at a glance.

PUT and POST get confused often enough that it is worth being concrete. PUT replaces a resource at a known location. POST creates a new one or sends data to be processed at a generic endpoint.

POST Request With curl

Basic curl POST syntax and the -d flag in practice

The minimum curl POST is one line:

```

curl -d "username=arya&age=16" https://api.example.com/users

```

That is it. POST request, form-urlencoded body, no fuss. `-d` is doing triple duty: it stuffs the payload into the request body, flips the request method to POST, and tacks on `Content-Type: application/x-www-form-urlencoded` as the default header. I usually paste the slightly more verbose form anyway, because the POST intent reads better in a code review:

```

curl -X POST -d "username=arya&age=16" https://api.example.com/users

```

Same bytes on the wire. Same request arguments. Use whichever your team finds easier to scan. The `-d` flag is the workhorse used to send data to a server from the command line, and it handles ninety percent of what a backend developer ever throws at curl: shell calls, webhook tests, Makefile targets, GitHub Actions steps.

Three close cousins of `-d`. `--data-binary` keeps your bytes intact; vanilla `-d` strips newlines and will mangle a binary payload. `--data-urlencode` percent-encodes for you: `--data-urlencode "name=I am Daniel"` arrives as `name=I%20am%20Daniel`. `--data-raw` is the escape hatch when a value starts with `@` and curl should not read a file. Multiple `-d` flags join with `&`. Fine for forms. Never for JSON.

One last gotcha: quoting. Single quotes around the payload stop the shell interpreting `$` or backslashes. Forget them and you spend 2 a.m. asking why half your POST data is missing.

These details matter because curl itself is one of the most heavily tested HTTP clients on the planet. Stenberg's December 2025 retrospective tallied eight releases that year. Nine CVEs, all rated low or medium. 2,179 active test cases, 232 more than twelve months earlier. Release 8.11.1, December 2024, patched CVE-2024-11053 (a netrc-and-redirect credential leak). The current stable as of late April 2026 is curl 8.20.0. Anyone stuck below 7.82.0 still has no `--json` flag and falls back to the older three-flag pattern.

Sending JSON data with curl using --json or -H

JSON is the lingua franca of REST APIs in 2026. Two ways to POST it with curl. Which one to use depends entirely on the curl version sitting on the machine.

Classic three-flag pattern, works everywhere from CentOS 6 onwards:

```

curl -X POST \

-H "Content-Type: application/json" \

-d '{"title":"Tea","quantity":2}' \

https://api.example.com/orders

```

Modern shorthand, available since curl 7.82.0 (March 2022):

```

curl --json '{"title":"Tea","quantity":2}' https://api.example.com/orders

```

The `--json` flag does three things. Sets `Content-Type: application/json`. Sets `Accept: application/json`. Sends the body. It composes too: pass `--json` more than once, the payloads concatenate. Useful when piping JSON chunks into a streaming endpoint.

For JSON payloads stored in a file, prefix the file path with `@`. Posting data from a file becomes the standard pattern once the JSON object is too large to paste inline, or once it lives in version control, or once another script generates it:

```

curl -X POST -H "Content-Type: application/json" -d @order.json https://api.example.com/orders

curl --json @order.json https://api.example.com/orders

```

The second pattern reads the data from a file named `order.json` and sends it with all the right headers. For large or potentially-binary JSON content, `--data-binary @file.json` is the safer option. It does not strip newlines, does not interpret special characters, and sends raw binary data to an endpoint that expects bytes rather than form fields. A third pattern reads the data from stdin via `-d @-`. Handy when piping output from another command straight into a curl POST.

The table below maps the four common JSON shapes against a single endpoint:

Pattern Command When to use
Inline, classic `-X POST -H "Content-Type: application/json" -d '{"k":"v"}'` Maximum compatibility, any curl version
Inline, modern `--json '{"k":"v"}'` curl 7.82.0 or newer, cleanest syntax
From file, safe `-X POST -H "..." -d @payload.json` Payload kept in version control
From file, binary-safe `-X POST -H "..." --data-binary @payload.json` Large files, payloads with newlines

This matters more than it used to. Every major LLM provider — OpenAI, Anthropic, Mistral, Google — opens its API docs with a curl POST example using exactly this shape. AI-driven traffic added 73% to overall API call volume in 2024 (Postman). curl is now the canonical reference for "how do I call this endpoint."

POST Request With curl

Content-Type, headers, and what curl assumes

Most "415 Unsupported Media Type" replies come down to one missing header. With `-d`, curl quietly stamps your request `Content-Type: application/x-www-form-urlencoded`. Send a JSON body without `-H "Content-Type: application/json"` or `--json`, and you get back 415. No warning, just the rejection.

Other metadata rides along with the body too: Content-Length, the Authorization header, X-Request-Id for tracing. Getting that metadata right is half of any API integration. The JSON object itself is the easy half.

`-H` adds custom headers. Repeat as needed. Header names are case-insensitive; values are not. Fastest way to debug a misconfigured POST: run it with `-v` once, read the request line, diff against the API docs. Eight times out of ten the bug surfaces in seconds.

Two more notes. Content-Length is auto-set by curl from the body size; you rarely override it. `Accept: application/json` is what tells the server to reply in JSON rather than HTML. Add another `-H` for that, or use `--json`, which sets Content-Type and Accept together.

Form data and file uploads with curl POST -F

For HTML-style form data with files, the right flag is `-F`. It produces a `multipart/form-data` request. Different content type from the form-urlencoded one `-d` defaults to. Different mental model. One-paragraph tutorials conflate the two often enough that it causes real bugs.

`-F` once per field. Plain form field:

```

curl -F "name=Arya" https://api.example.com/submit

```

A file upload:

```

curl -F "file=@/path/to/image.png" https://api.example.com/upload

```

The `@` prefix tells curl to read the file from disk and include its contents in the request body. curl auto-detects the MIME type from the file name; override it explicitly when needed:

```

curl -F "[email protected];type=image/png" https://api.example.com/upload

```

Multiple `-F` flags pack multiple fields or files into one request:

```

curl -F "title=Holiday" -F "[email protected]" -F "[email protected]" https://api.example.com/album

```

For raw file contents without the multipart wrapper, a common need when posting a JSON file or a binary blob to a streaming endpoint, use `--data-binary @file.bin` instead. That sends the file bytes as the request body verbatim, with whatever Content-Type you set explicitly.

Authentication in curl POST: basic, bearer, API keys

Three auth shapes cover almost every REST API in 2026. Basic auth (username plus password, base64-encoded into a header):

```

curl -u "myuser:mypass" -X POST -d "..." https://api.example.com/login

```

Bearer tokens, used by most modern APIs including every OAuth-issued credential, ride on a custom Authorization header:

```

curl -H "Authorization: Bearer $TOKEN" --json '{"q":"hello"}' https://api.example.com/query

```

API keys typically live in a service-specific header:

```

curl -H "X-API-Key: $PLISIO_KEY" --json @invoice.json https://api.plisio.net/api/v1/invoices/new

```

Credential from an environment variable or a secret manager, never typed inline. Why? GitGuardian's 2025 State of Secrets Sprawl counted 23.8 million new secrets leaked to public GitHub during 2024. A 25% jump over 2023. More than 90% of them stayed valid five days after exposure. Inline credentials in curl scripts are a leading source of those leaks. Security section below covers the workflow.

Real-world curl POST example: Plisio payment API

Reference docs without a real example are theory. The curl POST below mints a crypto payment invoice via Plisio's REST API. Plisio charges 0.5% flat; card processors typically 2-4%. Plisio supports thirty-plus cryptocurrencies and ships integrations for nineteen e-commerce platforms.

Why crypto APIs work as practice targets. Stablecoins moved roughly $28 trillion in real economic volume during 2025. Chainalysis pegged the growth at 133% CAGR since 2023. The crypto payment gateway market hovered near $2 billion in 2025, on track for $2.39 billion in 2026 (per The Business Research Company). BitPay, Coinbase Commerce, Plisio: pick any one. The first integration step with any modern gateway is almost always a curl POST.

The minimal call to create a new invoice:

```

curl -X POST https://api.plisio.net/api/v1/invoices/new \

-H "Content-Type: application/json" \

-H "Authorization: Bearer $PLISIO_API_KEY" \

-d '{

"source_currency": "USD",

"source_amount": 49.99,

"order_number": "INV-1042",

"currency": "BTC",

"email": "[email protected]",

"order_name": "Annual subscription"

}'

```

A successful call returns HTTP 201. The response body is a JSON object: a generated invoice ID, an `invoice_url` the customer pays at, a destination wallet address, an expiration timestamp. Pipe it through `jq` to pull out whichever field you need next:

```

... | jq '.data.invoice_url'

```

That is the whole pattern. Swap the endpoint, swap the payload, and the shape carries straight over to a BitPay charge, a Coinbase Commerce checkout, or a Stripe payment intent. The flags do not change; only the JSON does.

Debugging a curl POST request: -v, --trace, errors

A failed POST is a debugging puzzle. Retry-blindly is the worst move. Four options on the command line cover most of what you need.

`-v` prints the request line, every request header, and the response status. That is the first flag you reach for. `--trace-ascii -` is the bigger hammer: it dumps the whole conversation, body of a POST included, to standard output. Use `curl -i` to inline the response headers above the body. And `-w "%{http_code}\n"` writes just the HTTP status code, useful when scripting CI checks against an API.

About the status codes you will see most often. 400: the server parsed your request but rejected the payload. 401: missing or invalid auth header. 415: wrong Content-Type. 500: the server crashed on your input. Each one rules out a specific layer, which is half the value of running `-v` on the first failure.

curl POST security: API keys, secrets, and leaks

Every reference skips this section. Every incident postmortem mentions it. December 2024: the U.S. Treasury Department breach traced back to a leaked BeyondTrust API key. Exactly the kind of credential that lives inside `-H "Authorization: Bearer ..."` headers in production scripts.

The defense is unglamorous. Read tokens from environment variables. Store them in a secret manager such as AWS Secrets Manager, HashiCorp Vault, or the 1Password CLI. Keep a pre-commit hook running `gitleaks` or `trufflehog`. Rotate any token that ever lived in shell history. None of this is exciting. All of it works. Applying it across every curl POST request you ship is the single most consequential habit a backend developer can build in 2026.

Any questions?

Two shapes. Multipart upload (HTML form wire format): `curl -F "file=@/path/to/file.png" https://api.example.com/upload`. Raw binary, no wrapper: `curl --data-binary @file.bin -H "Content-Type: application/octet-stream" https://api.example.com/raw`. The `@` is what makes curl read from disk.

curl 7.82.0 (March 2022) and newer: `curl --json `{"key":"value"}` https://api.example.com`. On older versions, three-flag pattern: `curl -X POST -H "Content-Type: application/json" -d `{"key":"value"}` https://api.example.com`. JSON already in a file? Prefix with `@`: `--json @payload.json` or `-d @payload.json`.

I would not call either one "better." curl wins anywhere a shell beats a GUI: one-off calls, CI jobs, anything you reach over SSH. Postman wins on saved collections, side-by-side diffing, and team workflows. I keep both around.

Open the shell, paste `curl -X POST -d "field=value" https://api.example.com/endpoint`, hit return. Endpoint wants JSON instead? Add `-H "Content-Type: application/json"`, or use `--json` if you are on curl 7.82.0 or newer. The output prints to stdout, ready for `jq`.

GET, on its own. POST kicks in the second `-d`, `--data`, `--data-binary`, `--data-urlencode`, `-F`, or `--json` enters the command. Anything else (PATCH, DELETE, PROPFIND) needs `-X` because none of those have a "shortcut" flag.

What you get from `curl -X POST` (or any data-sending flag) in a terminal. The verb tells the server "take this and do something." I reach for it to test a REST endpoint, replay a webhook that died in prod, or smoke a Lambda right after deploy.

Ready to Get Started?

Create an account and start accepting payments – no contracts or KYC required. Or, contact us to design a custom package for your business.

Make first step

Always know what you pay

Integrated per-transaction pricing with no hidden fees

Start your integration

Set up Plisio swiftly in just 10 minutes.