Skip to content

cURL to JavaScript Fetch Converter

Paste a cURL command and get clean fetch() code with headers, body, auth and method preserved. Nothing is sent to a server.

cURL command

bash syntax

JavaScript (fetch)

request.js

Paste a command to see the generated code.

Parsing and code generation happen in your browser. Tokens and credentials inside the command are never transmitted.

About the cURL to JavaScript Fetch Converter

The fastest way to reproduce an HTTP request in JavaScript is to steal it from the browser. Open DevTools, find the request in the Network tab, right-click and choose Copy as cURL — then paste it here to convert curl to fetch and get the equivalent fetch() call, headers and body included.

The converter parses the command the way a shell would: single quotes are literal, double quotes honour escapes, and backslash line continuations are handled, so a multi-line paste works untouched. Flags that have no meaning in a code snippet (--compressed, -s, -v) are dropped, and anything that cannot be translated faithfully — a proxy, a client certificate, --insecure — is reported instead of silently ignored.

When the body is JSON, it is emitted as a real JavaScript object passed through JSON.stringify rather than a wall of escaped string, so you can edit it immediately. A urlencoded body — the curl default when -d is used without an explicit Content-Type — becomes a URLSearchParams object instead of a raw string, for the same reason.

fetch also has a few behaviours curl does not, and the generated code is written around them rather than against them. Headers become a plain object, which is simpler to read but means a header curl sent twice collapses to whichever occurrence is written last. And where curl sends cookies from its own jar and follows redirects by default, fetch omits credentials on a cross-origin request and does not follow a redirect unless told to — both of which the generated code states explicitly rather than assumes.

  • Handles -X, -H, -d/--data-raw, -F, -u, -b, -A, -e, -G, -L, -I and -k
  • JSON bodies become editable object literals; urlencoded bodies become a URLSearchParams object
  • Multipart -F fields become a FormData object, with file fields marked for you to wire up
  • Basic auth converted to an Authorization header built with btoa()
  • Picks response.json() or response.text() automatically, based on the Accept or Content-Type header
  • Includes the response.ok check that fetch famously does not do for you
  • -G moves -d data into the query string instead of the body, matching curl's own behaviour
  • Unsupported flags — proxies, client certificates, --insecure — are listed rather than dropped silently

How to use it

  1. In Chrome or Firefox DevTools, open the Network tab, right-click the request and choose Copy → Copy as cURL (bash).
  2. Paste it into the left pane.
  3. Read the generated fetch() code on the right — it appears as you type.
  4. Check the Not translated panel if it appears; those flags need a decision from you.
  5. Copy the snippet into your project. Remember that a request from a browser page is subject to CORS, which curl ignores.

Real-world use cases

Frontend & full-stack developers

Turn a request captured from DevTools' Network tab into a fetch() call ready to drop into a component, instead of rebuilding the headers and body by hand from what the Network panel shows.

Backend & API developers

Convert a cURL example from another team's API documentation into working JavaScript, to sanity-check what the endpoint actually expects before wiring up a real integration.

QA & test engineers

Reproduce a request pulled from a bug report or a HAR export as a small fetch() script for a repro case, without setting up a full HTTP client just to replay one call.

Technical writers & DevRel engineers

Generate the JavaScript variant of a cURL snippet for API documentation, so both code samples describe the exact same request instead of drifting apart when one is updated by hand.

Students & developers learning fetch

See a real request rendered in fetch's actual shape — the options object, the response.ok check, the JSON.stringify call — as a working reference rather than a stripped-down tutorial example.

Examples

A JSON POST

Input
curl -X POST https://api.example.com/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Ada"}'
Output
const payload = {
  "name": "Ada"
};

const response = await fetch("https://api.example.com/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

A urlencoded form body

Input
curl -X POST https://api.example.com/login \
  -d 'username=ada&password=s3cret'
Output
const body = new URLSearchParams({
  "username": "ada",
  "password": "s3cret",
});

const response = await fetch("https://api.example.com/login", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
  },
  body,
});

if (!response.ok) {
  throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}

const data = await response.text();
console.log(data);

curl defaults -d to application/x-www-form-urlencoded when no Content-Type is set, so the converter adds the same header and builds a URLSearchParams object rather than treating the body as raw text.

Basic authentication

Input
curl -u demo:s3cret https://api.example.com/me
Output
headers: {
  "Authorization": "Basic " + btoa("demo:s3cret"),
}

btoa is fine for ASCII credentials. In Node, use Buffer.from("user:pass").toString("base64") instead.

A file upload

Input
curl -F "[email protected]" https://api.example.com/upload
Output
const form = new FormData();
// form.append("avatar", fileInput.files[0]); // was @photo.png

A browser cannot read an arbitrary path from disk, so the file has to come from a file input or a Blob. The Content-Type header is deliberately omitted — fetch must set the multipart boundary itself.

Common errors

MessageCauseFix
Failed to fetchUsually CORS. curl is not a browser and ignores the same-origin policy entirely; fetch does not.The API must send Access-Control-Allow-Origin for your origin. If you do not control it, call it from your own server instead.
Request has a body but no Content-Typecurl defaults -d to application/x-www-form-urlencoded; fetch sends text/plain for a string body.Set the header explicitly. The converter adds it for you when curl would have.
Multipart request rejected by the serverA manually set Content-Type overrides the boundary parameter that FormData generates.Do not set Content-Type when the body is FormData. The converter strips it for this reason.
fetch does not throw on a 404 or 500fetch only rejects on network failure; any HTTP status resolves successfully.Check response.ok, as the generated snippet does.
Cookies are not sentfetch omits credentials for cross-origin requests by default, where curl sends whatever -b specified.Add credentials: "include", and make sure the server allows it.
A header from the original command seems to have disappearedcurl allows the same -H flag more than once, but the generated object literal does not — a duplicate key silently keeps only the last value written.Combine repeated values into one header yourself, or build the headers with a Headers instance and its append() method instead of an object literal.
Unexpected token < in JSON at position 0The generated code calls response.json() because the request's Accept or Content-Type header implied JSON, but the response body was actually HTML — an error page from a proxy, or a login redirect.Log response.text() first to see what actually came back, then switch to .json() once the endpoint returns what you expect. The choice the converter makes is a guess based on the request, not the real response.

Frequently asked questions

Is my cURL command sent to a server?

No. Parsing and code generation run entirely in your browser. That matters here more than for most tools, because a copied cURL command usually contains session cookies and bearer tokens.

Why does the generated code fail with a CORS error when curl works?

curl is not bound by the same-origin policy; browsers are. If the API does not return the right Access-Control-Allow-Origin header for your page's origin, the browser blocks the response. This is enforced by the browser, not the server, and cannot be worked around from client code.

Does it work with Copy as cURL from Chrome DevTools?

Yes — that is the main use case, including the multi-line bash format with backslash continuations. Choose the bash variant rather than cmd, since the Windows cmd form uses ^ continuations and different quoting.

Does the output work in Node.js?

Yes on Node 18 and later, where fetch is global. The one exception is btoa for basic auth — prefer Buffer.from("user:pass").toString("base64") on the server.

What happens to flags like -k or --proxy?

They are listed in a Not translated panel rather than dropped quietly. Browsers cannot disable TLS verification or set a proxy from JavaScript, so there is no honest equivalent to generate.

Does it handle PUT, PATCH and DELETE, or just GET and POST?

Any method. The generated method: string comes directly from curl's -X flag (or is inferred as POST when a body is present and GET otherwise), so PUT, PATCH, DELETE and any custom verb pass through exactly as given.

How does it decide between response.json() and response.text()?

It looks at the request's own Accept and Content-Type headers — if either mentions JSON, the snippet calls response.json(), otherwise .text(). That is a reasonable guess for most APIs but it is still a guess about the request, not a read of the actual response, so an endpoint that returns something unexpected needs the call changed by hand.

What if my command uses --data-urlencode?

The value is carried through as-is rather than encoded, and the tool adds a note saying so — curl performs its own percent-encoding on that flag's value, which this converter does not replicate. If the value contains reserved characters, encode it yourself before it goes into the generated code.

Can I convert to other languages?

The same parser drives the Python requests converter, and you can switch target with the tabs above the input without re-pasting the command.

Last updated