Paste a curl command and get the same request as fetch, axios, Python requests, PHP cURL or Go. The usual source is DevTools: right-click a request in the Network tab, choose Copy as cURL, and paste it here. Flags the tool cannot translate are listed in a note under the output, so you can see what was left out.
curl 'https://api.example.com/v1/orders' \
-H 'content-type: application/json' \
-u 'shop:s3cret' \
--data-raw '{"sku":"TSHIRT-M","qty":2}' import requests
headers = {
"content-type": "application/json",
}
json_data = {
"sku": "TSHIRT-M",
"qty": 2,
}
response = requests.post(
"https://api.example.com/v1/orders",
headers=headers,
auth=("shop", "s3cret"),
json=json_data,
)
print(response.status_code)
print(response.text)
The JSON body became a dict passed as json=, and
-u became auth= instead of a hand-built
Authorization header. Each target uses its library's own option
where one exists.
Single quotes, double quotes, backslash line continuations and the $'...' strings Chrome uses when a body contains an apostrophe. The Windows cmd version of Copy as cURL is read on a best effort basis, so pick the bash version when you can.
-d, --data-raw, --data-binary and --data-urlencode (repeated ones joined with &), --json, and -F multipart fields including @file uploads. With -G the data moves into the query string, as curl does.
-H, -A, -e, -b cookies and -u basic auth. A -d without a Content-Type gets application/x-www-form-urlencoded written out explicitly, because curl adds that header and most clients do not.
-X, -I, -L, -k, --compressed and -m map to each client's own option. Output-only flags like -s, -v and -o are dropped without a note because they do not change the request. Any other unknown flag is named in the note under the code.
A request copied from DevTools carries headers the browser sets
itself: Cookie, Origin, Referer and the whole sec-ch-ua and
sec-fetch family. Page scripts are not allowed to set those, and
fetch drops them without an error. Pick Node.js (fetch)
when the code runs on a server, where those limits do not
apply. The JavaScript (fetch) target
keeps them as comments so you can see what the original request
sent, and adds credentials: "include" when there were
cookies.
Redirects differ too. curl stops at a 3xx unless you pass -L,
while fetch, requests and Go follow redirects by default. PHP cURL
behaves like the command line, so only that target gets
CURLOPT_FOLLOWLOCATION, and only when the command had
-L. If the command carries a live session cookie or token, the
generated code contains it too, so keep it out of commits and
shared snippets.
import shlex
cmd = """curl 'https://httpbin.org/post' \\
-H 'content-type: application/json' \\
--data-raw '{"sku":"TSHIRT-M","qty":2}'"""
# shlex does not know about line continuations, so drop them first
args = shlex.split(cmd.replace("\\\n", " "))
assert args.pop(0) == "curl"
method, url, headers, data = None, None, {}, []
it = iter(args)
for arg in it:
if arg in ("-X", "--request"):
method = next(it)
elif arg in ("-H", "--header"):
name, _, value = next(it).partition(":")
headers[name.strip()] = value.strip()
elif arg in ("-d", "--data", "--data-raw", "--data-binary"):
data.append(next(it))
elif not arg.startswith("-"):
url = arg
body = "&".join(data) or None
method = method or ("POST" if body else "GET")
print(method, url, headers, body)
# then, with requests installed:
# response = requests.request(method, url, headers=headers, data=body) shlex.split follows POSIX quoting but does not handle backslash line continuations or bash's $'...' strings. Without the replace(), each continuation turns into a stray newline argument.
// Split a shell command into words: quotes, backslashes and line continuations
function shellWords(cmd) {
const words = [];
let cur = "", inWord = false;
for (let i = 0; i < cmd.length; i++) {
const c = cmd[i];
if (c === "\\") {
if (cmd[i + 1] !== "\n") { cur += cmd[i + 1] ?? ""; inWord = true; }
i++;
} else if (c === "'") {
const end = cmd.indexOf("'", i + 1);
if (end === -1) throw new Error("Unterminated single quote");
cur += cmd.slice(i + 1, end); inWord = true; i = end;
} else if (c === '"') {
inWord = true;
for (i++; i < cmd.length && cmd[i] !== '"'; i++) {
if (cmd[i] === "\\" && '"\\$`\n'.includes(cmd[i + 1])) i++;
if (cmd[i] !== "\n" || cmd[i - 1] !== "\\") cur += cmd[i];
}
} else if (/\s/.test(c)) {
if (inWord) words.push(cur);
cur = ""; inWord = false;
} else { cur += c; inWord = true; }
}
if (inWord) words.push(cur);
return words;
}
const cmd = `curl 'https://httpbin.org/post' \\
-H 'content-type: application/json' \\
-H "x-note: say \\"hi\\"" \\
--data-raw '{"sku":"TSHIRT-M","qty":2}'`;
const [, ...args] = shellWords(cmd);
const init = { method: "GET", headers: {} };
let url;
for (let i = 0; i < args.length; i++) {
const a = args[i];
if (a === "-X" || a === "--request") init.method = args[++i];
else if (a === "-H" || a === "--header") {
const [name, ...rest] = args[++i].split(":");
init.headers[name.trim()] = rest.join(":").trim();
} else if (a === "-d" || a === "--data" || a === "--data-raw") {
init.body = init.body ? init.body + "&" + args[++i] : args[++i];
if (init.method === "GET") init.method = "POST";
} else if (!a.startsWith("-")) url = a;
}
console.log(url, init);
// const response = await fetch(url, init); This covers commands you wrote yourself. It does not handle $'...' strings, combined short flags like -sSL, or --data-urlencode, and those cases account for most of the extra code in the tool above.
curl joins repeated -d values with & and sends them as application/x-www-form-urlencoded unless you set another Content-Type. Most HTTP clients send a bare string as text/plain, so the tool writes the Content-Type header out explicitly whenever curl would have added it. --json is shorthand for the same data plus Content-Type and Accept headers set to application/json.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Paste the command, pick Python (requests) from the Target dropdown and copy the result. JSON bodies become a dict passed as json=, -u becomes auth=, and -k becomes verify=False.
Yes, that is the main use case, including the $'...' strings Chrome writes when a body contains an apostrophe. On Windows, DevTools offers a cmd and a bash version, and we recommend bash. The tool reads the cmd version too, but only on a best effort basis.
Browsers do not let page scripts set Cookie, Origin, Referer, Host or any sec- header, and fetch drops them without an error. The tool keeps them as comments so you can see what the original request sent. The Node.js (fetch) target keeps them as real headers.
They are listed in a note under the generated code. Options that only change curl's own output, like -s, -v, -i and -o, are ignored without a note because they do not change the request.
The command is parsed by JavaScript in this page and is not sent anywhere. The generated code still contains the token, though, so keep it out of commits and rotate the token if the code gets shared.
Tidy the JSON that comes back with the JSON formatter, generate types for it with JSON to TypeScript, pull apart a long URL in the URL parser, or check a bearer token in the JWT decoder.