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).
| Option | What it does | Content-Type it sets | Watch out for |
|---|---|---|---|
-X POST | Sets the method word | None | Not needed with -d, -F or --json |
-H "Name: value" | Adds or replaces a header | Whatever you set | -H "Name:" removes the header |
-d, --data | Sends a body and switches to POST | application/x-www-form-urlencoded | @file input loses its newlines; repeats join with & |
--data-raw | Same as -d | application/x-www-form-urlencoded | A leading @ is sent as text |
--data-binary | Sends the body byte for byte | application/x-www-form-urlencoded | Set your own Content-Type |
--data-urlencode | URL-encodes the value | application/x-www-form-urlencoded | name=value encodes only the value |
--json | Posts JSON as given | application/json, plus Accept | Needs curl 7.82.0 or newer; no validation |
-F, --form | Sends form fields and files | multipart/form-data | file=@path attaches a file |
-i | Prints the response headers | None | Called --include before 8.10.0 |
-v, --verbose | Prints the request and response headers | None | Shows 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.