What a cURL converter is really doing
A curl command looks like a request but is not one. It is a line of shell, and before curl
ever sees it the shell has already split it into words, removed quotes, expanded escapes and joined
continuation lines. Any tool that claims to convert curl to code has to redo that work faithfully, because
the difference between a correct conversion and a subtly wrong one usually lives inside the quoting rather
than inside the HTTP.
That is why so many converters break on the same input. Given
-H "Authorization: Bearer ab'cd", a converter that finds the outermost quote characters and
trims them produces a header value that is either truncated at the apostrophe or carries a stray quote.
Given a JSON body with an escaped double quote inside a single-quoted argument, a naive parser resolves the
backslash that the shell was supposed to leave alone. Both mistakes are invisible until the generated code
runs and the API answers 400 Bad Request or
401 Unauthorized for no obvious reason.
This converter runs a proper quoting state machine over the input first. It understands single quotes
(everything literal until the next quote), double quotes (backslash escapes only before
", \, $ and a backtick), unquoted backslash escapes,
$'…' ANSI-C strings with their \n, \t, \xHH and
\uXXXX forms, and a backslash before a newline as a line continuation rather than an escape.
Only once the argument vector is correct does it start interpreting flags.
The flags that change the request
curl has hundreds of options. A small subset determines what goes on the wire, and those are the ones a converter must get right. The rest — output formatting, connection tuning, TLS behaviour — affect the client, not the request, and are reported in the breakdown rather than translated.
| Flag | Effect on the request |
|---|---|
-X, --request | Sets the method explicitly. Overrides every inference below. |
-H, --header | Adds one header. Repeatable. A trailing ; instead of : sends the header with an empty value. |
-d, --data | Adds body content and implies POST. A leading @ means "read this file", and newlines are stripped out of the file as it is read. |
--data-raw | As -d but @ is literal. The right choice for JSON. |
--data-binary | Sends bytes verbatim, preserving newlines. Use it when whitespace matters. |
--data-urlencode | Percent-encodes the value before adding it to the body. |
-G, --get | Moves all accumulated data into the query string and makes the request a GET. |
-F, --form | Builds a multipart/form-data body. @file attaches a file, <file reads a value from one. |
-u, --user | HTTP Basic credentials, base64-encoded into an Authorization header. |
--url | Sets the target URL explicitly instead of relying on the bare argument. |
-A, -e, -b | Shorthand for User-Agent, Referer and Cookie headers. |
--compressed | Advertises Accept-Encoding and decompresses transparently. |
-L, --location | Follows redirects. Affects behaviour, not the first request. |
Method inference deserves its own note, because it is where a converted request most often diverges from
the original. curl uses GET by default, switches to POST the moment any data or
form option appears, and switches back to GET if -G is present — but an explicit
-X always wins, even when it contradicts everything else. A command with both -d
and -X GET really does send a GET with a body, which some servers accept and many
intermediaries silently drop. The breakdown above tells you which rule produced the method it chose.
For the semantics behind each verb, see the HTTP methods reference.
There is one implicit header worth watching. If a command carries data but no Content-Type,
curl adds application/x-www-form-urlencoded on your behalf. Copy that command into
fetch without the header and the browser sends
text/plain;charset=UTF-8 instead, and an API that dispatches on content type will reject it.
The converter surfaces that implied header explicitly and writes it into the generated code, so the
behaviour is preserved rather than inherited by accident. The
HTTP headers guide covers the rest of the set that clients add for you.
Escaping, per target
Once the request is parsed, each output language needs its own escaping. Getting this wrong is the second classic failure mode, and it is easy to miss because the generated code usually still looks plausible.
- JavaScript, Python, Go and Java all accept a double-quoted literal with backslash
escapes for
",\and the control characters, so a JSON-style encoding is correct for all four. An apostrophe needs no escaping at all inside those. - PHP is the exception. Double-quoted PHP strings interpolate
$variableand{$expr}, so a body containing a dollar sign would be silently mangled. The generated PHP therefore uses single-quoted strings, where only\and'need escaping and nothing is interpolated. - PowerShell has the same hazard as PHP, from a different direction. A double-quoted
PowerShell string expands
$var,$(...)subexpressions and backtick escapes, so a JSON body containing$— or a token that happens to start with one — would be rewritten before the request is ever built. The generated code therefore uses single-quoted strings throughout, which interpolate nothing at all. The one escape that exists inside them is a doubled quote:'Bearer ab''cd'is the literalBearer ab'cd. Note that this is not the backslash escaping every other language on this list uses, which is why a converter that reaches forJSON.stringifyhere produces something that looks right and is not. - wget is shell, so it uses the same
'\''idiom as curl itself — the interesting part is the flag mapping rather than the escaping, and it is covered below. - Shell, for the reverse builder, uses the single-quote idiom: wrap in single quotes and
replace every embedded quote with
'\''— close, escaped quote, reopen. It is ugly and it is the only form that is correct for arbitrary bytes. - Postman needs no language escaping because the output is JSON; the values are carried as data rather than as source code, which is exactly why importing a collection is more reliable than pasting a command into a form.
If you are converting a request whose credentials are base64-encoded — Basic auth, most obviously — the Base64 encoder is useful for checking that the value the converter produced decodes back to the username and password you expected. For bearer tokens, decoding the JWT tells you whether the token in your command has already expired, which is a far more common cause of a failing conversion than the conversion itself.
curl to PowerShell, and curl to wget
These two are the most common "same request, different machine" conversions. A curl command copied from a Linux README, a colleague's terminal or a vendor's docs has to run on Windows; or wget is what happens to be installed on the box you are standing in front of. Neither is a straight rename, and both have a default that differs from curl in a way that changes what you observe.
PowerShell
The idiomatic target is Invoke-RestMethod, which sends the request and deserialises a JSON
response into a PowerShell object. Use Invoke-WebRequest instead when you care about the
status code, the response headers or the raw body — the generated output includes it commented out, since
that is normally what you want when you are debugging rather than scripting. Both take
-Uri, -Method, a -Headers hashtable and -Body.
Three things reliably go wrong. Quoting is the big one: a double-quoted PowerShell string
interpolates $variable and $(expression), so pasting a JSON body containing a
dollar sign into double quotes changes the payload silently, which is exactly the class of bug that
produces a 400 Bad Request with no obvious cause. Single quotes
interpolate nothing, and an embedded single quote is escaped by doubling it rather than with a
backslash. Content-Type is the second: it is a restricted header, and Windows PowerShell
5.1 throws "The 'Content-Type' header must be modified using the appropriate property or method" if you
put it in the -Headers hashtable, so it belongs on -ContentType. The third is
redirects — Invoke-RestMethod follows them by default and curl does not, so
the generated command adds -MaximumRedirection 0 unless the original had -L.
One further difference worth knowing rather than discovering: in Windows PowerShell 5.1 a non-2xx
response throws a terminating error instead of returning, so a script that expects to read a
404 body needs a try/catch around the call.
PowerShell 7 added -SkipHttpErrorCheck, which is the cleaner answer where it is available.
wget
wget and curl look interchangeable and are not. The flag mapping itself is simple —
-X becomes --method=, each -H becomes a --header=
argument, and the body becomes --body-data= — but two defaults are inverted.
| curl | wget | Why it matters |
|---|---|---|
-X POST | --method=POST | Straight rename. wget 1.15 and later. |
-H 'K: v' | --header='K: v' | Repeatable in both. |
-d '…' | --body-data='…' | Does not imply POST the way -d does — set the method explicitly. |
-u user:pass | --user= --password= | Or keep the Authorization header the converter already computed. |
--compressed | --compression=auto | wget 1.19.2 and later. |
-k | --no-check-certificate | Same meaning, same reasons not to. |
| writes to stdout | -O - | wget saves to a file by default. Without this you get a file on disk and an empty terminal. |
| no redirect follow | --max-redirect=0 | wget follows redirects by default; curl needs -L. Reversed defaults. |
| prints error bodies | --content-on-error | wget discards the body of a 4xx or 5xx unless you ask for it — and that body is usually the error message. |
The honest limitation is multipart/form-data: wget has no equivalent of -F and
cannot assemble a multipart body. The converter says so rather than emitting something that looks close,
because a file upload that silently posts the wrong content type is worse than a command that refuses to
be written.
curl to Postman, and back
The Postman target emits a v2.1 collection: an info block with the schema
URL, and an item array containing one request with its method, header list, structured URL
and body. Import it with Postman's Import → Raw text. A collection rather than a bare request is the
deliberate choice here, because a collection is the unit Postman can share, fork, version and run in a
collection runner; a loose request is not.
The structured URL matters more than it looks. Postman stores the raw URL and a decomposed protocol, host array, path array and query array. Tools that emit only the raw string produce a request that works but shows an empty Params tab, so variables cannot be substituted per environment and query parameters cannot be toggled. This converter fills in both representations.
Going the other way — Postman or a browser's Copy as cURL to something you can read — is the more common
direction in practice, and it is what the breakdown at the top of the page is for. A copied command from
Chrome DevTools typically carries fifteen or more headers, most of them browser noise:
sec-ch-ua, sec-fetch-mode, accept-language, a cookie jar. Seeing
them in a table makes it obvious which two or three actually matter, which is normally the authorization
header and the content type. If you are weighing the two tools generally,
Postman vs curl covers where each one earns its place, and the
Postman alternative page covers running the request without installing
anything at all.
From converted command to a request you can actually send
Converting is usually the middle step, not the goal. The goal is to find out what the endpoint returns. Once the breakdown looks right, take the method, URL, headers and body straight into the API Tester and send it. Because that tool forwards the call through our own server rather than issuing it from the page, the browser's same-origin policy never applies and you get the real response from any public endpoint — status, timing, headers and body.
That difference is worth understanding rather than just using. A request issued by JavaScript in a page is
subject to CORS; a request issued by curl, by a server, or by any non-browser client is not. Code
generated here for fetch will hit that wall if you paste it into a browser console against an
API that sends no Access-Control-Allow-Origin — while the identical Python or Go version
works first time. If that is the situation you are in, the CORS tester
shows you exactly what your browser does with the URL, and CORS explained covers
why the failure message is so unhelpful by design.
A last habit worth forming: strip credentials before you share a converted command. Tokens end up in chat logs, issue trackers and pull request descriptions constantly, usually because someone pasted a working curl line to demonstrate a bug. Replace the value with a placeholder, and if a real one has already escaped, rotate it rather than hoping. Anything in the query string is worse still, since it also lands in server access logs and browser history — see API authentication for where a secret should and should not live.
Frequently asked questions
How do I convert a curl command to Postman?
Paste the command above, choose the Postman collection target, and copy the JSON. In Postman use Import and paste it as raw text. The output is a v2.1 collection containing one request, so it imports as a small collection rather than a loose request, which is what makes it shareable and version-controllable. Postman also has its own Import from raw text feature that accepts curl directly; this tool is useful when you want to see the parsed request first, or when you want the same command in code instead.
How do I convert a curl command to PowerShell?
Paste the command and choose the PowerShell target. The output uses
Invoke-RestMethod, with headers as a hashtable and the body as a separate variable, plus a
commented Invoke-WebRequest form for when you want the status code and raw headers instead of
a deserialised object. Every string is single-quoted on purpose: PowerShell does not interpolate inside
single quotes, so a dollar sign in a token or JSON body stays literal, whereas a double-quoted string
would expand $var and $(...) and silently rewrite your payload. An embedded
single quote is escaped by doubling it. Content-Type is passed through
-ContentType rather than the hashtable, because Windows PowerShell 5.1 rejects that
restricted header when it is set on -Headers.
How do I convert a curl command to wget?
Choose the wget target. The mapping is -X to --method, each -H
to a --header argument, and the request body to --body-data. Two defaults differ
from curl and the generated command corrects for both: wget writes the response to a file rather than
stdout, so -O - is added, and wget follows redirects by default while curl does not, so
--max-redirect=0 is added unless the original command had -L.
--content-on-error makes wget print an error response body instead of discarding it. One
thing wget genuinely cannot do is build a multipart/form-data body, so a command using
-F is flagged rather than mistranslated.
Why does my curl command break when a header contains a quote?
Because most converters treat quoting as string trimming rather than shell parsing. A value such as
Bearer ab'cd cannot simply be wrapped in single quotes; the shell ends the string at the
embedded quote. The correct form closes the quote, emits an escaped quote outside it, and reopens:
'Bearer ab'\''cd'. This converter parses input with a real quoting state machine and escapes
output per target language, so a quote in a header or body value survives the round trip.
What is the difference between -d, --data-raw and --data-binary?
-d and --data treat a leading @ as a filename to read, and
strip newlines and carriage returns out of that file's content as they read it. --data-raw is
identical except that @ is taken literally, which is why it is the safe choice for JSON
containing an at sign. --data-binary sends the bytes exactly as given and never interprets
@ as a file for you in the same way. All three imply POST and, unless you set
Content-Type yourself, imply application/x-www-form-urlencoded.
Does the converted code send the same request as curl?
The method, URL, headers and body are reproduced exactly. What cannot be reproduced is curl's default
behaviour around headers each client adds for itself: User-Agent, Accept,
Accept-Encoding, Connection and Host differ between curl, fetch,
requests and net/http. If a server behaves differently under the generated code than under curl, compare
the headers actually sent before suspecting anything else.
Is my curl command sent anywhere?
No. Parsing and code generation happen entirely in this page, so a command containing an API key or bearer token never leaves your browser. If you then want to actually run the request, the API Tester on the homepage does send it through our proxy — that is a separate, deliberate step, and it is described plainly there.
Can it convert a curl command with a file upload?
It parses -F and --form fields and shows them in the breakdown, including
which are literal values and which reference a file with the @ prefix. The generated code
sketches a multipart body with FormData or its equivalent, but a file part cannot be filled
in automatically because the file lives on your machine, not in the command. Those parts are emitted with
a clearly marked placeholder.
Related tools and reference
API Tester
Send the parsed request for real and read the actual response.
CORS Tester
Find out what your browser does with a URL that curl handles fine.
JSON Formatter
Pretty-print the body before pasting it back into a command.
URL Encoder
Percent-encode the values that --data-urlencode would have handled.
User Agent Parser
Decode the User-Agent a copied command is carrying.
REST API Testing
Where converting a command fits in a wider testing loop.