Convert cURL to Fetch, Axios, Python, Go and Java

2026-04-07

A cURL command is an excellent reproducible description of an HTTP request. Converting it to application code means preserving method, URL, headers, body bytes, redirect behavior, timeout, and authentication—while adapting to each client's defaults.

Paste a sanitized command into the local cURL to Code converter for a quick starting point. Remove real tokens, cookies, personal data, and signed URLs first.

Consider this example:

curl --request POST 'https://api.example.test/v1/widgets?dry_run=true' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer example-placeholder' \
  --data '{"name":"demo","enabled":true}'

The following translations intentionally use a placeholder credential. In real code, read it from approved secret storage rather than source control.

Browser or Node.js Fetch

const response = await fetch(
  'https://api.example.test/v1/widgets?dry_run=true',
  {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.API_TOKEN}`,
    },
    body: JSON.stringify({ name: 'demo', enabled: true }),
  }
);

if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const data = await response.json();

Browser Fetch applies CORS and forbidden-header rules that cURL does not. Node.js Fetch has no browser CORS enforcement. Fetch also resolves normally for HTTP 404 or 500, so check response.ok.

Axios

import axios from 'axios';

const { data } = await axios.post(
  'https://api.example.test/v1/widgets',
  { name: 'demo', enabled: true },
  {
    params: { dry_run: true },
    headers: {
      Accept: 'application/json',
      Authorization: `Bearer ${process.env.API_TOKEN}`,
    },
    timeout: 10_000,
  }
);

Axios serializes the object as JSON and rejects non-2xx responses by default. That differs from Fetch. Be explicit about timeout, expected status behavior, and redirects when parity matters.

Python Requests

import os
import requests

response = requests.post(
    "https://api.example.test/v1/widgets",
    params={"dry_run": "true"},
    headers={
        "Accept": "application/json",
        "Authorization": f"Bearer {os.environ['API_TOKEN']}",
    },
    json={"name": "demo", "enabled": True},
    timeout=(3.05, 10),
)
response.raise_for_status()
data = response.json()

Use json= for JSON; data= usually sends form data or raw bytes depending on its value. Always provide a timeout. The tuple above separates connection and read timeouts.

Go

payload := strings.NewReader(`{"name":"demo","enabled":true}`)
req, err := http.NewRequestWithContext(
    ctx,
    http.MethodPost,
    "https://api.example.test/v1/widgets?dry_run=true",
    payload,
)
if err != nil { return err }
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("API_TOKEN"))

client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
    return fmt.Errorf("unexpected HTTP status: %s", resp.Status)
}

Set a client timeout, close the response body, and bound any body you read into memory.

Java HttpClient

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.test/v1/widgets?dry_run=true"))
    .timeout(Duration.ofSeconds(10))
    .header("Accept", "application/json")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer " + System.getenv("API_TOKEN"))
    .POST(HttpRequest.BodyPublishers.ofString(
        "{\"name\":\"demo\",\"enabled\":true}"
    ))
    .build();

var response = HttpClient.newHttpClient().send(
    request, HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() / 100 != 2) {
    throw new IOException("HTTP " + response.statusCode());
}

Use a JSON library for dynamic payloads rather than string concatenation.

Verify the conversion

Compare requests in the API Workbench and generate a minimal command with the cURL builder. Check query encoding, duplicate headers, multipart boundaries, compressed responses, redirects, proxy settings, and TLS verification.

Start with a harmless test account and compare one property at a time. Confirm the effective URL after redirects, status code, response content type, and server-side request ID. If you control the server, log header names and body size—not authorization values or full payloads—to compare the two clients. A packet capture is rarely the first tool because HTTPS intentionally hides application bytes and captures can contain sensitive traffic.

Pay special attention to cURL options that have no one-line translation. --user creates an Authorization header, --cookie participates in cookie state, --compressed negotiates response encoding, and --max-time limits the whole transfer. Client libraries differ in whether they retain credentials across redirects, use environment proxies, decompress automatically, or retry. Document those decisions instead of assuming defaults are equivalent.

Multipart requests need the library's form-data abstraction so it can generate a boundary:

const form = new FormData();
form.append('metadata', JSON.stringify({ name: 'demo' }));
form.append('file', file);

await fetch('https://api.example.test/v1/imports', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}` },
  body: form,
});

Do not manually set Content-Type here; the client must add the boundary. In Node.js, the exact File, Blob, or stream API depends on the runtime and library version. Apply file-size limits and avoid loading large uploads fully into memory.

Common mistakes include converting --data-urlencode to JSON, dropping --form file uploads, preserving cURL's generated Content-Length, and disabling certificate checks because the original used -k. Never carry -k into production code; repair trust configuration instead. Also avoid logging the generated request when it includes authorization or customer data.

A conversion is correct when the server receives equivalent semantics—not merely when the code compiles. Keep the original sanitized command as a test fixture, and rerun both clients after library upgrades to catch changed defaults.