From a terminal command to fetch()
API docs, support tickets and DevTools all hand you requests as curl commands, but your front end or Node script needs fetch. Translating by hand is where bugs creep in: a forgotten header, a body that should have been JSON-stringified, or a redirect that curl did not follow but fetch does. This converter parses the command the way curl does and emits code that sends the same request.
It reads commands copied from bash or zsh (with \ line continuations and $'…' strings), from Windows cmd (^ escapes, as in Chrome’s “Copy as cURL (cmd)”) and from PowerShell (backtick continuations).
How curl options become fetch options
-Xsetsmethod; without it the method is inferred just as curl does (POST when there is a body, HEAD for-I, PUT for-T). GET is left implicit.-Hheaders go into aheadersobject. A header repeated with the same name is folded into one comma-separated value.-d,--data-rawand--data-binarybecomebody. When the content type is JSON and the body is a JSON object or array, the code usesJSON.stringify({...})with a readable object literal; otherwise the exact string is sent. Several-dflags are joined with&, and curl’s impliedapplication/x-www-form-urlencodedheader is written out explicitly.--jsonsets the body plusContent-TypeandAccept: application/json.-Gmoves the data into the query string;--data-urlencodeand--url-queryare encoded the way curl encodes them.-Fbuilds aFormData; file fields (-F file=@photo.jpg) get a placeholderBloband a TODO comment, since a web page cannot read your disk.-u user:passbecomes anAuthorization: Basicheader built withbtoa;--oauth2-bearerbecomes a Bearer header.-m/--max-timebecomessignal: AbortSignal.timeout(ms).
Redirects and other differences
curl does not follow redirects unless you pass -L, while fetch follows them by default. To keep the behaviour identical, the generated code sets redirect: 'manual' without -L (with a comment explaining why) and redirect: 'follow' with it.
Some curl features have no fetch equivalent, and the converter says so instead of silently dropping them:
-k/--insecure: fetch always verifies TLS certificates; a warning and a comment are added.--connect-timeoutand-xproxies: noted in comments (in Node.js, an undiciProxyAgenthandles proxies).- A GET or HEAD with a body: fetch refuses to send it, so the body is left out with a warning — you probably meant
-G. - Browsers block scripts from setting
CookieandReferer; a comment notes that they only take effect in server-side runtimes.
Flags that only affect curl’s own output (-s, -v, -i) are ignored, and options such as -o or --retry produce an info note. The result is formatted with Prettier and prints the status and response text.
Credentials stay on your machine
Copied commands nearly always contain secrets: session cookies, API keys, bearer tokens. That is why the conversion runs entirely client-side. For a promise-based alternative with automatic JSON handling, see cURL to Axios; to tidy the generated code further, use the JavaScript formatter.
Examples
JSON POST with a bearer token
The JSON body becomes JSON.stringify of a JavaScript object literal, and redirect is set to manual because there is no -L.
curl -X POST 'https://api.example.com/v1/orders?expand=items' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc' \
-d '{"customer":"cus_42","items":[{"sku":"KB-104","qty":1}]}'const response = await fetch('https://api.example.com/v1/orders?expand=items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc',
},
body: JSON.stringify({
customer: 'cus_42',
items: [{ sku: 'KB-104', qty: 1 }],
}),
redirect: 'manual', // curl follows redirects only with -L
});
console.log(response.status);
console.log(await response.text());
Form login with basic auth and a timeout
The two -d values are joined with &, -u becomes a Basic Authorization header, and --max-time becomes AbortSignal.timeout(10000).
curl -L -u admin:s3cret --max-time 10 https://intranet.example.com/login \
-d 'user=aisha' -d 'remember=1'const response = await fetch('https://intranet.example.com/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic ' + btoa('admin:s3cret'),
},
body: 'user=aisha&remember=1',
redirect: 'follow',
signal: AbortSignal.timeout(10000),
});
console.log(response.status);
console.log(await response.text());
Chrome "Copy as cURL (cmd)"
Windows caret escaping is decoded, and the cookie header comes with a reminder that browsers do not allow fetch to set it.
curl ^"https://api.example.com/v1/me^" ^
-H ^"accept: application/json^" ^
-H ^"cookie: session=abc123; theme=dark^"// Browsers do not let fetch set Cookie or Referer headers; they are sent as written in Node.js.
const response = await fetch('https://api.example.com/v1/me', {
headers: {
accept: 'application/json',
cookie: 'session=abc123; theme=dark',
},
redirect: 'manual', // curl follows redirects only with -L
});
console.log(response.status);
console.log(await response.text());
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
"-Uri" is PowerShell's Invoke-WebRequest syntax, not curl | In Windows PowerShell, curl is an alias for Invoke-WebRequest, and the command uses its parameters. | Copy the request as cURL (bash) or (cmd) instead, or rewrite it with -H, -d and -X. |
fetch cannot send a body with GET; the body is left out | The curl command sends data with GET (-X GET -d …), which fetch does not allow. | Use -G so the data goes into the query string, or switch the method to POST. |
fetch has no option to skip TLS certificate checks (-k); the generated code verifies certificates | The command uses -k/–insecure, typically against a self-signed development server. | Trust the certificate in your OS or Node (NODE_EXTRA_CA_CERTS) rather than disabling verification. |
This single-quoted string is never closed | A quote in the pasted command is unbalanced, often because the command was cut off. | Copy the full command again, including the closing quote. |
Shell variable $TOKEN cannot be expanded here and is kept as literal text | A double-quoted argument refers to an environment variable. | Replace the variable in the generated code with the real value or a process.env lookup. |
Frequently asked questions
Does the generated code work in Node.js?
Yes. Node 18 and later ship fetch globally, and the code uses top-level await, so run it as an ES module (.mjs) or wrap it in an async function.
Why is redirect set to manual?
curl only follows redirects with -L, while fetch follows them by default. Setting manual keeps the generated request behaving like your command.
How are file uploads with -F handled?
A FormData is built with a placeholder Blob for each file and a TODO comment. Replace it with a File from an input element or a Blob read in Node.
Is my API token sent to PasteKit?
No. The command is parsed and converted in your browser, and nothing is transmitted.