Why this needs a model in the middle
A curl command and a fetch call describe the same thing: one HTTP request. They agree on almost nothing about how to write it down — curl has around two hundred flags, several of which mean nothing on the wire at all, while fetch has one options object, a dozen keys, and a browser's security policy on top of it.
curl -H 'Accept: application/json' https://api.example.com/v1/users → fetch("https://api.example.com/v1/users", { headers: { Accept: "application/json" } })
So this tool never rewrites one syntax into the other. It reads the command into a plain description of the request — method, address, query, headers, cookies, credentials, body, transfer settings — and writes that description out in whichever language you asked for, which is what stops the two directions developing separate opinions about what a flag means. The Request tab is that model rather than a second parse, and nothing is uploaded: the parsing, the writing and the export all happen in this tab.
Three shells, three ways to escape a quote
Every browser offers Copy as cURL, and on Windows it offers it three times. The same JSON body arrives with the double quotes escaped completely differently, and read with the wrong rules it becomes a different string — so the shell is detected first and the rules picked from that.
| Shell | How a quote is escaped | Line continuation | Watch for |
|---|---|---|---|
| bash / zsh | Single quotes hold everything literally; a single quote inside is written by closing, escaping it outside, and reopening. | A backslash at the end of the line. | Chrome switches to the dollar-quote form the moment a value holds a newline or a non-ASCII byte, and the escapes inside that form are C's, not the shell's. |
| Windows cmd | Double quotes, with the backslash escapes curl's own C runtime reads — and carets on top, which the shell strips first. | A caret at the end of the line. | Two layers resolve in order, which is why a copied body looks like backslash-caret-quote. cmd also cannot hold a line break inside one argument, and expands anything shaped like a percent-name-percent before curl runs. |
| PowerShell | Literal strings double the quote; expandable strings use a backtick. | A backtick at the end of the line. | PowerShell aliases curl to Invoke-WebRequest, which takes none of these flags — so anything generated for it says curl.exe, and so does the output here. |
Paste from any of the three and the detected shell is shown under the box. The shell picker on the output side is independent: you can read a command copied from Chrome on Windows and write it back out for bash.
What each language can express
curl is the superset. Going from a command to code always loses something, and the three targets lose different things — this table is the whole map. A dash means there is no way to say it, and the tool will tell you so under the output rather than quietly leaving it out.
| What it does | curl | fetch | axios | node:https |
|---|---|---|---|---|
| Method | -X | method | method | method |
| Query string | ?a=b | URL | url | path |
| Headers | -H | headers | headers | headers |
| Cookies | -b | Cookie | Cookie | Cookie |
| Basic credentials | -u | Authorization | auth | Authorization |
| Form body | -d | URLSearchParams | data | write() |
| JSON body | --json | JSON.stringify | data | write() |
| Multipart upload | -F | FormData | FormData | — |
| Follow redirects | -L | redirect | maxRedirects | — |
| Redirect limit | --max-redirs | — | maxRedirects | — |
| Whole-transfer timeout | -m | AbortSignal.timeout | timeout | timeout |
| Connect timeout | --connect-timeout | — | — | — |
| Skip certificate checks | -k | dispatcher | httpsAgent | rejectUnauthorized |
| Client certificate | -E | — | httpsAgent | cert |
| Proxy | -x | dispatcher | proxy | — |
| Unix socket | --unix-socket | — | socketPath | socketPath |
| HTTP version | --http2 | — | — | — |
| Retries | --retry | — | — | — |
Two of those rows are Node-only even where the cell is filled in. axios reads proxy, maxRedirects and httpsAgent only when it is running on Node — in a browser it is XHR underneath and quietly ignores all three. Switch Runs on to Browser and they move into the notes instead of the snippet.
Everything a browser refuses outright is in the same place. Cookie and Referer are forbidden headers: setting them from a page does nothing, so cookies become a request for the browser's own jar and Referer becomes the referrer option.
Every control, and when to reach for it
| Option | What it does | Reach for it when |
|---|---|---|
| Language | Picks the language the request is written in. fetch is also the only one that can be read back. | You want axios for its redirect limits, timeouts and TLS agent, or node:https to add no dependency at all. |
| Runs on | Decides whether the snippet may use Node-only escapes — a dispatcher, an https agent, a Cookie header. | Always set it to match where the code will run. On Browser the same flags become notes instead of lines. |
| Shape | Writes the call with await, or as a then and catch chain. | Pick the chain for code that cannot be inside an async function. |
| Headers as | Writes the headers as a plain object, or wrapped in a Headers instance. | The instance is worth it when names arrive in mixed case or repeat, since it normalises and merges them. |
| Handle the response | Adds the status check and the call that reads the body after the request. | Turn it off when you only want the call itself to paste into code you already have. |
| Indent | Spaces per level in the generated snippet. | Match whatever the file you are pasting into uses. |
| Shell | Chooses the quoting and the continuation mark for the command. | Set it to the shell you will paste into, not the one you copied from — the two are independent. |
| Long flag names | Writes --header and --location rather than -H and -L. | The command is going into a script or a README somebody else has to read. |
| One flag per line | Puts each flag on its own line with the shell's continuation mark. | Turn it off when the command has to survive being pasted into a single-line field. |
| Always write -X | Writes -X even where curl would have used that method anyway. | You want the method visible at a glance rather than implied by the presence of a body. |
Four of these apply to some targets and not others, and the ones that do not apply are disabled with the reason under them rather than left to do nothing. node:https ignores the shape, the headers style and the response handling; only fetch takes a Headers instance; and Runs on has no meaning for node:https, which is Node by definition.
The shell options only apply in the fetch to cURL direction, and the code options only in the other. The panel shows the set that matches the direction you are working in.
Where the two genuinely behave differently
Redirects are the first. curl does not follow one unless you pass -L; fetch follows by default. Converting a command with no -L therefore has to say something, and what it says depends on where the code will run: on Node it writes redirect: "manual", which behaves exactly like curl. In a browser that same line makes the response opaque — no status, no headers, no readable body — so it is left out and the difference is reported instead.
Content types are the second, and this one bites hardest. curl sends form encoding with -d unless you say otherwise. fetch sends text/plain for any string body — including one that came out of JSON.stringify. Neither is a bug and both are invisible until a server rejects the request, so the tool writes the implicit header out in full rather than leaving you to discover it.
Then there are the headers a browser will not let a page set at all. Cookie is one, so cookies from -b become a request for the browser's own jar, which is not the same cookies. Referer is another, and becomes the referrer option, which the browser's referrer policy may still shorten or drop. Neither substitution is silent.
Multipart is the fourth. A curl command names files by path and may declare a type per part; FormData in JavaScript takes a File or a Blob and reads the type off it. The path becomes a placeholder variable, the declared type is lost, and the Content-Type header is deliberately left out, because whatever sends the body picks the boundary and a hand-written header would name a different one.
Last, a flag that changes nothing on the wire is still worth naming. -v, -s, -i and -o only changed what curl printed. They cannot survive, they were never part of the request, and the notes say so rather than leaving you wondering where they went.
What people use this for
Reproducing a request the browser already made is the common one. Open the network panel, copy the failing call as cURL, paste it here, and you have the fetch that makes the same request — headers, cookies and body included — to drop into the code you were debugging.
The other direction is for support and for bug reports. A fetch call in your codebase becomes a command anyone can run in a terminal, with no build step, no project and no credentials of yours beyond what the request already carried.
Both directions are worth reading even when you already know the answer, because the notes are where the surprises are. A request that works in the browser and fails from Node is very often one of the four differences above, and the list under the output names them without you having to go looking.
