DevToys Web Pro iconDevToys Web Proబ్లాగ్
అనువాదం LocalePack logoLocalePack
మాకు రేటింగ్ ఇవ్వండి:
బ్రౌజర్ ఎక్స్‌టెన్షన్‌ను ప్రయత్నించండి:
← Back to Blog

cURL to Code: Convert curl Commands to JavaScript, Python, and More

10 min read

You copy a curl command from API documentation, a browser DevTools Network tab, or a colleague's Slack message. Now you need to run that same request from JavaScript, Python, or Go — inside real application code. The flags are terse, the quoting is shell-specific, and translating them by hand is error-prone. Use the cURL to Code Converter to automate the translation, and read this guide to understand exactly what each flag means so you can verify the output and handle edge cases in production.

See also: API Debugging Workflow and Web Payload Workflow for how curl fits into a broader development process.

Anatomy of a curl Command

A curl command is a thin wrapper around an HTTP request. Every flag maps to exactly one property of that request: the method, a header, the body, authentication, or transport behavior. Once you know the flag-to-property mapping, translation to any HTTP client library becomes mechanical.

Here is a representative curl command that uses most common flags:

curl -X POST https://api.example.com/v1/orders \
  -H "Authorization: Bearer eyJhbGciOi..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"item":"widget","qty":3}' \
  -b "session=abc123" \
  -L \
  --compressed \
  --http2

Breaking it down flag by flag:

  • -X POST — sets the HTTP method. Without -X, curl uses GET for requests without a body and POST when -d is present.
  • -H "Name: Value" — adds a request header. Repeat for each header. Header names are case-insensitive in HTTP/1.1, but libraries typically preserve the case you provide.
  • -d '...' or --data '...' — sets the request body. Also implicitly sets the method to POST and the Content-Type to application/x-www-form-urlencoded unless you override it with -H.
  • --data-binary '...' — same as -d but does not strip newlines or carriage returns. Use this for JSON bodies with embedded newlines, binary files, or anything that must not be modified in transit.
  • -u user:pass — HTTP Basic authentication. curl encodes it as a base64 Authorization: Basic ... header.
  • -b "name=value" — sends a Cookie header. If you pass a filename instead of a string, curl reads cookies from that file (the cookie jar format).
  • -c cookies.txt — writes received Set-Cookie headers to a file (saving cookies for subsequent requests).
  • -L or --location — follow HTTP 3xx redirects automatically. Without this flag curl stops at the first redirect response.
  • --compressed — sends Accept-Encoding: gzip, deflate, br and decompresses the response automatically. Most HTTP clients do this by default.
  • --http2 — negotiates HTTP/2 via ALPN. Useful for performance testing; application-layer semantics are identical.

Flag Mapping Table

The table below maps common curl flags to their equivalents in the five most-used HTTP clients. Use the cURL to Code Converter to generate the full snippet automatically.

curl flagfetch (JS)axios (JS)requests (Python)Go net/httpPHP (cURL)
-X METHODmethod: "METHOD"method: "method"requests.method()req.Method = "METHOD"CURLOPT_CUSTOMREQUEST
-H "K: V"headers: {"K":"V"}headers: {"K":"V"}headers={"K":"V"}req.Header.Set("K","V")CURLOPT_HTTPHEADER
-d 'body'body: "body"data: "body"data="body"strings.NewReader("body")CURLOPT_POSTFIELDS
-d '{}' + JSON headerbody: JSON.stringify(obj)data: objjson=objjson.Marshal(obj)json_encode($obj)
-u user:passbtoa headerauth: [user, pass]auth=(user, pass)base64 headerCURLOPT_USERPWD
-b "k=v"headers: {"Cookie":"k=v"}headers: {"Cookie":"k=v"}cookies={"k":"v"}req.AddCookie()CURLOPT_COOKIE
-Lredirect: "follow"default (on)default (on)CheckRedirectCURLOPT_FOLLOWLOCATION
--compresseddefault (on)default (on)default (on)default (on)CURLOPT_ENCODING ""
-F "f=@path"FormDataFormDatafiles={'f':open()}multipart.WriterCURLFile
--retry Nmanual loopaxios-retryHTTPAdaptermanual loopmanual loop

JavaScript fetch Conversion

Walk through translating the representative POST from the Anatomy section into a fetch call.

The original curl:

curl -X POST https://api.example.com/v1/orders \
  -H "Authorization: Bearer eyJhbGciOi..." \
  -H "Content-Type: application/json" \
  -d '{"item":"widget","qty":3}'

The fetch equivalent:

const response = await fetch("https://api.example.com/v1/orders", {
  method: "POST",
  headers: {
    "Authorization": "Bearer eyJhbGciOi...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ item: "widget", qty: 3 }),
});

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

const data = await response.json();

Key translation decisions:

  • -d '{}' with Content-Type: application/json maps to body: JSON.stringify(obj). Do not pass the object directly — fetch does not serialize it.
  • fetch does not throw on 4xx/5xx status codes. Check response.ok or response.status explicitly.
  • fetch follows redirects by default (redirect: "follow"), matching curl -L behavior.
  • For the axios equivalent, pass the object directly as data: obj — axios serializes it and sets the Content-Type header automatically.

Python requests Conversion

The same POST in Python requests:

import requests

response = requests.post(
    "https://api.example.com/v1/orders",
    headers={
        "Authorization": "Bearer eyJhbGciOi...",
    },
    json={"item": "widget", "qty": 3},
)

response.raise_for_status()  # raises HTTPError on 4xx/5xx
data = response.json()

Using the json= parameter instead of data= is the key difference: it serializes the dict, sets Content-Type: application/json automatically, and is the idiomatic Python way. You do not need to pass the Content-Type header separately.

If your curl uses -d "field=value&other=123" (form-encoded, no JSON Content-Type), use data={"field": "value", "other": "123"} instead of json=.

Common Pitfalls

Content-Type mismatch with -d

This is the most common mistake when translating curl commands:

# This sends JSON body with application/x-www-form-urlencoded Content-Type
# because -d sets that header by default
curl -X POST https://api.example.com/data \
  -d '{"key":"value"}'

# Correct: explicit Content-Type header
curl -X POST https://api.example.com/data \
  -H "Content-Type: application/json" \
  -d '{"key":"value"}'

Without -H "Content-Type: application/json", the server receives a JSON string with a form-encoded content type and will likely reject it or misparse the body. When translating to code, always check whether the original curl sets the Content-Type explicitly.

Browser "Copy as cURL" includes cookies you do not want in code

When you right-click a network request in Chrome DevTools and choose "Copy as cURL", the output includes every cookie present in the browser for that domain — session tokens, analytics cookies, A/B test assignments, and more:

curl 'https://api.example.com/user/profile' \
  -H 'Cookie: _ga=GA1.2.1234567890.1234567890; session=eyJhbGci...; _fbp=fb.1.123...'

In code, you want only the cookies your application owns — typically just the session token. Strip analytics cookies (_ga, _fbp, _gid) before pasting the translated snippet into your codebase. Never commit session tokens or auth cookies to source control.

--data-binary @file vs --data

# --data strips newlines — breaks JSON with embedded newlines and binary content
curl -X POST https://api.example.com/upload --data @payload.json

# --data-binary preserves the file exactly as-is
curl -X POST https://api.example.com/upload --data-binary @payload.json

When translating --data-binary @filename to code, read the file as bytes and pass it directly as the request body. Do not decode it to a string if it may contain binary data.

curl has a cookie engine for multi-request flows: -b cookies.txt loads cookies from a file, and -c cookies.txt saves Set-Cookie responses to the same file. This is the curl equivalent of a browser session.

In code, use the library's session abstraction instead:

# Python requests: Session persists cookies across requests automatically
import requests

session = requests.Session()

# Login — Set-Cookie headers are stored in session
session.post("https://api.example.com/auth/login", json={
    "username": "alice",
    "password": "secret",
})

# Subsequent requests send the stored session cookie
profile = session.get("https://api.example.com/user/profile")
orders = session.get("https://api.example.com/user/orders")
// axios: use withCredentials for cross-origin cookie sending
import axios from "axios";

const client = axios.create({
  baseURL: "https://api.example.com",
  withCredentials: true,  // sends cookies cross-origin
});

await client.post("/auth/login", { username: "alice", password: "secret" });
const profile = await client.get("/user/profile");

For Node.js fetch (or browser fetch to cross-origin endpoints), set credentials: "include" in the options object. Without this option, cookies are not sent to cross-origin URLs.

Authentication

Basic authentication

curl -u alice:s3cr3t https://api.example.com/resource

curl encodes the credentials as base64(user:pass) and sends Authorization: Basic <encoded>. The equivalent in each language:

// fetch — manual base64 encoding in Node.js
const credentials = Buffer.from("alice:s3cr3t").toString("base64");
const response = await fetch("https://api.example.com/resource", {
  headers: { "Authorization": `Basic ${credentials}` },
});

// axios — built-in auth option
const response = await axios.get("https://api.example.com/resource", {
  auth: { username: "alice", password: "s3cr3t" },
});
import requests

response = requests.get(
    "https://api.example.com/resource",
    auth=("alice", "s3cr3t"),  # HTTPBasicAuth shorthand
)

Bearer token

curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." https://api.example.com/resource

Bearer tokens are just a header value — pass them directly. There is no special library option; set the header string explicitly in all languages.

HMAC-signed requests

APIs like AWS S3, Stripe webhooks, and GitHub require you to compute a signature over the request body and include it in the Authorization or X-Signature header. curl commands for these services typically have pre-computed signatures (valid only at the moment of capture) and do not translate directly to reusable code. Use the provider's official SDK or a signing library instead of hard-coding the signature value from curl.

Retries and Timeouts

curl has --retry N, --retry-delay S, and --retry-max-time T. These retry on connection failures and 5xx responses with exponential backoff. Production code needs the same behavior, but most HTTP client libraries do not implement it natively.

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry_strategy = Retry(
    total=3,
    backoff_factor=1,          # wait 1s, 2s, 4s between retries
    status_forcelist=[500, 502, 503, 504],
    allowed_methods=["GET", "POST"],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("https://", adapter)

# Also set a timeout — requests has no default timeout
response = session.get("https://api.example.com/data", timeout=(5, 30))

The tuple timeout=(5, 30) means 5 seconds to establish the connection and 30 seconds to read the response. curl's --connect-timeout and --max-time map to these two values respectively.

// fetch with AbortController timeout
async function fetchWithTimeout(url, options = {}, timeoutMs = 30000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeoutMs);
  try {
    return await fetch(url, { ...options, signal: controller.signal });
  } finally {
    clearTimeout(id);
  }
}

// axios has a built-in timeout option
const response = await axios.get("https://api.example.com/data", {
  timeout: 30000, // milliseconds
});

Always set a timeout in production code. curl defaults to waiting indefinitely without --max-time, and most HTTP client libraries do the same. A single hung request can exhaust your connection pool or delay an entire Lambda function.

Multipart and Binary Uploads

curl's -F "file=@/path/to/file" sends a multipart/form-data request — the same encoding used by HTML file input elements.

curl -X POST https://api.example.com/upload \
  -F "file=@/tmp/report.pdf" \
  -F "description=Monthly report" \
  -H "Authorization: Bearer eyJhbGciOi..."

JavaScript fetch translation:

import { readFile } from "node:fs/promises";

const fileBuffer = await readFile("/tmp/report.pdf");
const form = new FormData();
form.append("file", new Blob([fileBuffer], { type: "application/pdf" }), "report.pdf");
form.append("description", "Monthly report");

const response = await fetch("https://api.example.com/upload", {
  method: "POST",
  headers: { "Authorization": "Bearer eyJhbGciOi..." },
  body: form,
  // Do NOT set Content-Type manually — fetch sets it with the boundary parameter
});

Python requests translation:

import requests

with open("/tmp/report.pdf", "rb") as f:
    response = requests.post(
        "https://api.example.com/upload",
        headers={"Authorization": "Bearer eyJhbGciOi..."},
        files={"file": ("report.pdf", f, "application/pdf")},
        data={"description": "Monthly report"},
    )
response.raise_for_status()

Do not set Content-Type: multipart/form-data manually in either case. The library must generate the boundary parameter and include it in the header. Setting the Content-Type yourself will strip the boundary, and the server will be unable to parse the body.

For non-UTF-8 binary responses (images, PDFs, archives), read the response as bytes rather than text:

# Python
response = requests.get("https://api.example.com/file.pdf")
with open("output.pdf", "wb") as f:
    f.write(response.content)  # .content is bytes; .text would corrupt binary data
// fetch
const response = await fetch("https://api.example.com/file.pdf");
const buffer = await response.arrayBuffer();
await fs.writeFile("output.pdf", Buffer.from(buffer));

Using Translated Code in CI/CD

When moving curl commands into CI/CD pipelines, the translation pattern matters less than secret management. Replace literal tokens:

# Shell — read token from environment variable
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/deploy
import os, requests

response = requests.post(
    "https://api.example.com/deploy",
    headers={"Authorization": f"Bearer {os.environ['API_TOKEN']}"},
    json={"version": os.environ["DEPLOY_VERSION"]},
    timeout=(5, 60),
)

Store tokens in your CI provider's secret store (GitHub Actions Secrets, GitLab CI/CD Variables, Vault). Log the response status and body for debugging, but filter authorization headers from any log output.


Paste any curl command — from documentation, browser DevTools, or a terminal session — into the cURL to Code Converter to get ready-to-run code in JavaScript, Python, Go, PHP, and more. Everything runs locally in your browser; no request data is sent to any server.