Free curl command converter
curl converter for nine languages
This curl converter is free and needs no signup: paste the command and read the same request back as JavaScript fetch, Python requests, Go, PHP, Node axios, Java, C#, Ruby or PowerShell. It parses the shell properly — single quotes, double quotes, the $'…' form Chrome DevTools emits, and backslash line continuations — then handles -X, -H, every -d variant, -F multipart, -u basic auth, cookies, -G, --compressed and -k. Where a flag has no equivalent in the target language, the snippet says so in a comment rather than dropping it.
- 100% free
- No signup
- 9 languages
- Reads DevTools copies
- Multipart and basic auth
What the parser read
- Method
- POST (from -X)
- URL
- https://api.example.com/v1/orders?dry_run=true
- Headers
- Content-Type: application/json Authorization: Bearer sk_live_51H8xQ2
- Body
- raw bytes · 52 bytes {"sku":"A-1024","quantity":2,"note":"leave at door"}
- Flags
- none that change the request
Native in every browser and in Node 18 or newer — no package needed.
// curl follows no redirect without -L, while fetch always follows. Pass
// redirect: "manual" if a 3xx here needs to be handled rather than chased.
const response = await fetch("https://api.example.com/v1/orders?dry_run=true", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_live_51H8xQ2"
},
body: "{\"sku\":\"A-1024\",\"quantity\":2,\"note\":\"leave at door\"}",
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log(data);16 lines · nothing here was sent anywhere
Every flag, and what it becomes
curl has more than 250 command-line options. These are the ones that describe an HTTP request rather than curl’s own behaviour, which is the set worth translating.
| Flag | What curl does | What comes out |
|---|---|---|
| -X, --request | Overrides the method | The method on the request object, marked as explicit |
| -H, --header | Adds one header | A header entry, in the order you wrote them |
| -d, --data | Sends a body and makes the request a POST | A raw string body, or a key/value map when the data is a=1&b=2 |
| --data-raw | Same as -d but never treats a leading @ as a filename | Identical output to -d; the difference only matters in the shell |
| --data-binary | Sends the bytes untouched, keeping newlines | A raw body — the newline-stripping of plain -d is not reproduced |
| --data-urlencode | Percent-encodes the value before sending | The encoded value, decoded again where the target has a native form helper |
| --json | Body plus Content-Type and Accept of application/json (curl 7.82+) | A raw body with both headers set for you |
| -F, --form | One multipart/form-data part; @path attaches a file | FormData, files=, MultipartFormDataContent, CURLFile or writer.CreateFormFile |
| --form-string | A literal part, so @ and < stay literal | A text part with no file handling |
| -u, --user | Basic credentials on the first request | The native auth option where one exists, otherwise an Authorization header |
| --oauth2-bearer | Adds Authorization: Bearer | A plain Authorization header |
| -b, --cookie | Sends a Cookie header | A Cookie header; a cookie file is reported, not read |
| -A, --user-agent | Overrides User-Agent | A User-Agent header |
| -e, --referer | Sets Referer | A Referer header |
| -G, --get | Moves -d data into the query string | A GET with the data appended to the URL and no body |
| -I, --head | Sends HEAD | HEAD, or CURLOPT_NOBODY in the PHP output |
| -L, --location | Follows redirects, downgrading POST to GET on 301/302/303 | The follow-redirect setting, plus a warning when the method would change |
| -k, --insecure | Skips certificate verification | verify=False, InsecureSkipVerify, rejectUnauthorized, -SkipCertificateCheck — or a comment where the runtime refuses |
| --compressed | Advertises gzip, deflate, br and decompresses | An Accept-Encoding header, or nothing where the client already does it |
| -m, --max-time | Caps the whole transfer | AbortSignal.timeout, timeout=, client.Timeout, TimeoutSec and friends |
| --connect-timeout | Caps the handshake only | The connect timeout where the client has one |
| -s -S -v -i -f -o | Change curl's own output, not the request | Nothing — they are recognised and dropped |
| -T, -x, --cert, --resolve | Upload a file, use a proxy, present a client certificate, override DNS | Nothing, but each one is listed under the summary so it cannot pass unnoticed |
What curl fills in when you say nothing
Half the surprises in porting a command come from defaults that were never written down in it. A generated snippet has to reproduce these, not improve on them.
| Method | GET, unless -d or -F is present (POST), -I is present (HEAD), or -X says otherwise |
|---|---|
| Content-Type with -d | application/x-www-form-urlencoded — the single most common cause of a 415 from a JSON API |
| Content-Type with -F | multipart/form-data with a boundary curl generates per request |
| Accept | */* — curl never asks for JSON unless you tell it to, or use --json |
| User-Agent | curl/<version>, e.g. curl/8.7.1 — many WAFs treat that string as a bot |
| Redirects | Not followed at all without -L; a 301 is printed, not chased |
| TLS verification | On. -k turns it off for both the certificate chain and the hostname |
| Expect: 100-continue | Added automatically for bodies over 1 KB on HTTP/1.1, which is why some proxies pause for a second |
How to convert a curl command
Paste, check what the parser read, then take the language you need.
Paste the command
Drop it into the box, or hit Ctrl+V anywhere on the page — a clipboard that starts with curl goes straight into the editor. In Chrome DevTools the command comes from the Network panel: right-click the request, Copy, Copy as cURL (bash), which produces the $'…' quoting and the sec-ch-ua headers this parser expects.
Check the summary before you trust the code
The panel under the editor shows exactly what was understood: the method and whether it came from -X or was inferred, the final URL after -G folds data into the query string, every header in order, the body with its byte count, and any flag that could not be carried across. A wrong snippet is almost always a misread command, and this is where you see it.
Pick a language and take the code
Nine targets sit above the output — JavaScript fetch, Python requests, Go net/http, PHP cURL, Node axios, Java HttpClient, C# HttpClient, Ruby net/http, PowerShell — and each renders from the same parsed request, so they cannot disagree about what the command does. Copy puts the snippet on the clipboard; Download saves it with the right extension, from .py to .ps1.
Technical specifications
| Target languages | 9: JavaScript fetch, Python requests, Go net/http, PHP cURL, Node axios, Java HttpClient, C# HttpClient, Ruby net/http, PowerShell |
|---|---|
| Flags parsed | 23 groups covering method, headers, all five data forms, multipart, basic auth, cookies, redirects, compression, TLS verification and timeouts |
| Shell grammar | Single quotes, double quotes with \" \\ \$ \` escapes, $'…' ANSI-C quoting with \n \t \xHH \uHHHH, backslash and caret line continuations, and bundled short flags such as -sSL |
| Method resolution | -X wins, then -I means HEAD, then any body means POST, otherwise GET — and -G leaves it a GET while moving the data into the query string |
| Content-Type inference | Copies curl exactly: application/x-www-form-urlencoded for -d, multipart/form-data for -F, application/json only for --json or an explicit header |
| Dependencies in the output | Only requests and axios need a package; fetch, Go, Java, C#, Ruby and PowerShell use the standard library, and the PHP output uses the bundled ext-curl |
| Not converted | Proxies, client certificates, --resolve, --unix-socket, -T uploads and SigV4 signing — each one is named under the summary rather than dropped in silence |
| Processing location | Your browser — the command is parsed in this tab, no request is ever made, and tokens in the command are never transmitted or stored |
Frequently asked questions
Why did my POST turn into a GET?
Three things do it, and two of them are in your own command. -G moves everything you passed with -d into the query string and leaves the method as GET, which is the whole point of the flag. -X GET with -d keeps the body but many servers and client libraries drop the body of a GET, so the request arrives looking empty. The third is -L: when curl follows a 301, 302 or 303 it rewrites the method to GET and discards the body, exactly as browsers do, unless you add --post301, --post302 or --post303. The summary panel flags all three cases.
Why does the API answer 415 when my curl command works?
Because -d without a Content-Type header makes curl label the body application/x-www-form-urlencoded, whatever it actually contains. Send a JSON object that way and a strict API rejects the media type before it ever looks at the payload. Either add -H 'Content-Type: application/json' or use --json, which sets both Content-Type and Accept in one flag from curl 7.82 onwards. This converter reproduces curl's behaviour rather than quietly correcting it, and it puts a warning under the summary when a JSON-looking body has no Content-Type.
Is it safe to paste a command that contains a live token?
Yes, in the sense that matters: the command never leaves your browser. Parsing and code generation are JavaScript running in this tab, there is no request to any API, and nothing is logged — you can disconnect from the network and the page keeps converting. The token is still a token, so the usual care applies to the snippet you paste into a chat afterwards.
Which curl flags does the converter not handle?
Anything that describes the transport rather than the HTTP request: -x proxies, --cert and --key client certificates, --resolve DNS overrides, --unix-socket, -T file uploads and --aws-sigv4 signing. They are recognised and listed under the summary as not carried over, rather than silently dropped, because a snippet that quietly omits a client certificate is worse than one that says it did. Flags that only affect curl's own console output — -s, -v, -i, -o, -w — are dropped without comment, since they change nothing about the request.
Why does the Python output use data= rather than json=?
Because data= sends the exact bytes curl would have sent. Passing json= hands the object to Python's serialiser, which re-orders nothing but does change whitespace, escaping and float formatting, and it sets Content-Type itself — so the request on the wire is no longer the request you tested. Where an API signs the request body, or where a webhook receiver compares an HMAC over the raw bytes, that difference is the entire bug. The same reasoning is why the Go output uses strings.NewReader over a struct literal.
Does the parser understand a command copied from Chrome DevTools?
That is the case it was built for. DevTools writes bash-style output with single-quoted values, escapes an embedded apostrophe as '\'' and switches to $'…' ANSI-C quoting when the body contains a newline or a control character — all three are handled, along with backslash line continuations and the caret continuations of the Windows cmd variant. A trailing shell pipeline such as | jq . is dropped, with a note, because it is shell rather than part of the request.
Can fetch reproduce -k?
No, and no browser API can. Certificate validation is enforced by the network stack, not by JavaScript, so there is no fetch option to skip it — in Node you would pass an undici Agent with connect.rejectUnauthorized set to false, which is a server-side decision. Seven of the remaining eight targets express it directly: verify=False in requests, InsecureSkipVerify in Go, CURLOPT_SSL_VERIFYPEER in PHP, an https.Agent in axios, DangerousAcceptAnyServerCertificateValidator in C#, VERIFY_NONE in Ruby and -SkipCertificateCheck in PowerShell 6 or newer. Java is the eighth and needs a hand-built SSLContext, so the snippet says so instead of pretending.
About porting curl commands
The hard part of converting a curl command is not HTTP, it is the shell. By the time curl sees its arguments the shell has already removed one layer of quoting, so -d '{"a":1}' and -d "{\"a\":1}" are the same eight bytes, while $'line\nbreak' contains a real newline and 'line\nbreak' contains a backslash and an n. A converter that splits on spaces and strips quotes gets all three wrong. This one runs a proper lexer first: single quotes are literal, double quotes honour four backslash escapes, $'…' decodes C-style escapes including \xHH and \uHHHH, and an unquoted backslash before a newline joins the lines.
The second trap is curl’s defaults, because they are decisions, not absences. -d implies POST and implies application/x-www-form-urlencoded; -F implies POST and a multipart boundary that curl generates per run; -u sends Basic credentials on the first request rather than waiting for a challenge, unlike most HTTP libraries; and no redirect is followed at all until you pass -L. Reproducing those faithfully is the difference between a snippet that behaves like the command and one that merely resembles it — and when the response comes back wrong, the status code reference usually names which of these assumptions broke.
Most commands that arrive here were never typed by hand. They come from Copy as cURL in Chrome, Firefox or Safari DevTools, which serialises a request the browser already made — cookies, sec-ch-ua client hints, a full user-agent and all. That is worth knowing before you paste the result into a server, because a script that carries a browser’s session cookie and pretends to be Chrome is a different thing from an API client. If you are unpicking what those copied headers say about the browser, the user agent parser takes the same strings apart, and any Authorization: Bearer value in the command can be read with the JWT decoder.
Where the command is parsed
In this tab, and nowhere else. The lexer, the parser and all nine generators are JavaScript that shipped with the page, so a command carrying an API key, a session cookie or a bearer token is read locally and never transmitted, logged or stored. Load the page once, pull the network cable, and it still converts.