curl POST Request Examples: JSON, Headers, Tokens and File Uploads

To send a POST request with curl, pass the body with -d for form data or --json for JSON (curl 7.82.0 and later), add headers with -H, and add -v to print the headers curl sends. A free test URL that lasts 48 hours shows the body as it arrived.

curl sends a POST as soon as you give it a body with -d (form data) or --json (JSON, curl 7.82.0 or newer); -H adds headers and -v prints what curl sent. The quickest way to check any curl command is to send it to a free webhooks.sh bin, which takes one curl command or a click at view.webhooks.page and needs no signup, and the viewer shows the method, path, headers and body as they arrived. Every example below posts to a bin URL written as https://<bin>.webhooks.page/; swap in your own.

Send a POST request with curl

curl switches to POST as soon as you pass data with -d, so -X POST isn't needed. -d sends Content-Type: application/x-www-form-urlencoded, the same as a browser form, and if you pass several -d options curl joins them with &.

Creating a bin is the one place you do need -X POST, because that request has no body; curl -X POST https://webhooks.sh/v1/bins returns JSON with an ingest_url and a view_url. When you send a curl post request to the ingest URL, the bin replies with HTTP 200 and a small JSON body, and the viewer shows method POST, path /signup, the application/x-www-form-urlencoded content type and the body name=ada&plan=pro.

Create a free bin

curl -X POST https://webhooks.sh/v1/bins

Post form data

curl -d "name=ada" -d "plan=pro" \
  https://<bin>.webhooks.page/signup

What the bin receives

POST /signup
Content-Type: application/x-www-form-urlencoded

name=ada&plan=pro

curl POST flags at a glance

These flags decide the method, headers and body of a POST, and the column that matters most in the table below is the Content-Type each one sets. Watch out for two classic traps: -d labels any body, JSON included, as application/x-www-form-urlencoded unless you set Content-Type yourself, and -X only changes the method word without changing how curl behaves (the curl man page notes that -X HEAD isn't a real HEAD request; -I is).

OptionWhat it doesContent-Type it setsWatch out for
-X POSTSets the method wordNoneNot needed with -d, -F or --json
-H "Name: value"Adds or replaces a headerWhatever you set-H "Name:" removes the header
-d, --dataSends a body and switches to POSTapplication/x-www-form-urlencoded@file input loses its newlines; repeats join with &
--data-rawSame as -dapplication/x-www-form-urlencodedA leading @ is sent as text
--data-binarySends the body byte for byteapplication/x-www-form-urlencodedSet your own Content-Type
--data-urlencodeURL-encodes the valueapplication/x-www-form-urlencodedname=value encodes only the value
--jsonPosts JSON as givenapplication/json, plus AcceptNeeds curl 7.82.0 or newer; no validation
-F, --formSends form fields and filesmultipart/form-datafile=@path attaches a file
-iPrints the response headersNoneCalled --include before 8.10.0
-v, --verbosePrints the request and response headersNoneShows the body size, not the body

POST JSON with curl

On curl 7.82.0 or newer, --json posts the data as given and sets both Content-Type: application/json and Accept: application/json. curl doesn't check that the body is valid JSON, so a typo goes out as a typo. If you're on an older curl (run curl --version to see which version you have), set the header yourself with -H "Content-Type: application/json" and pass the body with -d.

To send a JSON file, use --json @payload.json, or --data-binary @payload.json with -H "Content-Type: application/json". Don't use -d @payload.json if newlines matter, because -d strips carriage returns, newlines and null bytes out of the file.

curl 7.82.0 and newer

curl --json '{"event":"order.paid","amount":4200}' \
  https://<bin>.webhooks.page/orders

Older curl

curl -H "Content-Type: application/json" \
  -d '{"event":"order.paid","amount":4200}' \
  https://<bin>.webhooks.page/orders

JSON from a file

curl --json @payload.json https://<bin>.webhooks.page/orders

Add headers and a bearer token

-H "Name: value" adds a header or replaces one curl would set itself, and you repeat -H for each header you want to send. Passing -H "Name:" with nothing after the colon removes an internal header curl would otherwise send, like -H "Host:", while ending the name with a semicolon (-H "X-Custom-Header;") sends the header with an empty value. For a curl bearer token, pass -H "Authorization: Bearer <token>" or use --oauth2-bearer <token>, which builds the same header.

In the webhooks.sh viewer, headers whose names contain authorization, cookie, key, token or secret show as redacted, so you can see that Authorization or X-Api-Key arrived without the value appearing on screen. Ordinary headers like X-Request-Id show in full, and signature headers such as Stripe-Signature and X-Hub-Signature-256 stay visible. One gotcha when testing redirects: curl drops Authorization and Cookie headers when a redirect leads to another origin, unless you add --location-trusted.

Custom headers and a bearer token

curl --json '{"ping":true}' \
  -H "X-Request-Id: test-42" \
  -H "Authorization: Bearer <token>" \
  https://<bin>.webhooks.page/api

Same token with --oauth2-bearer

curl --oauth2-bearer "<token>" --json '{"ping":true}' \
  https://<bin>.webhooks.page/api

Upload a file with curl

-F sends multipart/form-data, the format of an HTML upload form: file=@report.csv attaches the file, ;type=text/csv sets that part's content type, and any more -F fields you pass travel in the same request. To send a file as the raw request body instead, use --data-binary @file with a Content-Type header, or -T to upload it with PUT. Free bins accept bodies up to 100KB and reply with 413 body_too_large above that, so test with small files.

Multipart upload

curl -F "file=@report.csv;type=text/csv" -F "note=March" \
  https://<bin>.webhooks.page/upload

File as the raw body

curl --data-binary @report.csv -H "Content-Type: text/csv" \
  https://<bin>.webhooks.page/upload

Upload with PUT

curl -T report.csv https://<bin>.webhooks.page/files/report.csv

See what curl sent with -v and -i

In curl verbose mode (-v), curl prints the exchange: lines starting with > are the request line and headers curl sent, lines starting with < are the response headers, and lines starting with * are connection details. -v only reports how many bytes of body went out (like } [13 bytes data]), not the body itself, so pass --trace-ascii <file> if you need a full dump of all incoming and outgoing data. -i (named --include before curl 8.10.0) prints the response headers above the response body.

The bin answers every captured request with HTTP 200 and a small JSON body holding a delivery_id, and the viewer shows the body that arrived, so curl's view and the receiver's view sit side by side.

Verbose POST

curl -v --json '{"ping":true}' https://<bin>.webhooks.page/v

Output (trimmed)

> POST /v HTTP/2
> Host: <bin>.webhooks.page
> User-Agent: curl/8.14.1
> Content-Type: application/json
> Accept: application/json
> Content-Length: 13
>
< HTTP/2 200
< content-type: application/json
<
{"ok":true,"delivery_id":"<bin>.1"}

Response headers only

curl -i -d "a=1" https://<bin>.webhooks.page/

FAQ

How do I send a POST request with curl?

Run curl -d "key=value" https://<bin>.webhooks.page/. -d alone makes it a POST with Content-Type: application/x-www-form-urlencoded, so you don't need -X POST. Point the command at a free webhook URL on webhooks.sh to see the request that arrived.

How do I send JSON with curl?

Use curl --json '{"key":"value"}' https://<bin>.webhooks.page/ on curl 7.82.0 or newer. On older versions, add -H "Content-Type: application/json" and pass the body with -d. Run curl --version to check which version you have installed.

How do I add a bearer token to a curl request?

Pass -H "Authorization: Bearer <token>" or --oauth2-bearer <token>. In the webhooks.sh viewer the Authorization value shows as redacted, so you can confirm the header arrived without exposing the token on screen.

Do I need -X POST when I use curl -d?

No, -d, -F and --json already send a POST request. -X only swaps the method word in the request line, and the curl man page warns that it doesn't change how curl behaves. Save -X POST for a POST with no body, like curl -X POST https://webhooks.sh/v1/bins when creating a bin.

Why does curl send my JSON as application/x-www-form-urlencoded?

-d and --data-binary default to Content-Type: application/x-www-form-urlencoded even when the body looks like JSON. Use --json (curl 7.82.0+) or add -H "Content-Type: application/json" yourself.

How can I see the exact request curl sends?

Add -v to print the request line and headers curl sends, though -v only prints the byte count of the body. Send the same command to a free webhooks.sh bin and the viewer shows the method, path, headers and full body as they arrived; free bins last 48 hours and take 100 requests a day, and you can claim a bin with a free Google sign-in to keep it.

How do I upload a file with curl?

Use -F "file=@path" for a multipart/form-data upload, --data-binary @path with a Content-Type header to send the file as a raw POST body, or -T path to upload it with PUT. Free webhooks.sh bins accept request bodies up to 100KB.

Last updated

Try it in 10 seconds.

Create a free bin at view.webhooks.page — no signup, no install. Point your sender at the URL and watch requests land live. Bins expire after 48 hours.