DEV Community

Issam
Issam

Posted on

Why most cURL converters fail on real-world requests — so I built my own

I wasted more time than I'd like to admit translating cURL commands into JavaScript and Python.

Not because it's hard — it's not. But because every converter I tried worked fine on toy examples and quietly broke on anything real.

Here's what actually happened when I tested the most popular ones:

Tool Auth (-u) JSON body → json= Axios output Python output
Tool A ❌ silently dropped ❌ passed as string ❌ no ❌ no
Tool B ⚠️ hardcoded comment ❌ passed as string ❌ no ❌ no
Tool C ❌ silently dropped ❌ no ❌ no
apidevtools auth= tuple ✅ detects Content-Type

So I built my own.

The real-world curl command that breaks most converters

This is what actual API integration looks like — not curl https://api.example.com:

bashcurl -X POST "https://api.example.com/v1/users" \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{"name": "John", "role": "admin"}' \
  -u myuser:mypassword
Enter fullscreen mode Exit fullscreen mode

Two things that consistently broke:

  1. -u user:pass — most tools drop it completely or leave a // TODO: add auth comment
  2. -d with JSON body — almost nobody maps this to json= in Python, so you get a raw string instead of a properly serialized request

Before / after: the Python case

What most converters output:

import requests

headers = {
    'Authorization': 'Bearer eyJhbGci...',
    'Content-Type': 'application/json',
}

# ❌ Auth dropped
# ❌ body sent as raw string, not serialized JSON
response = requests.post(
    'https://api.example.com/v1/users',
    headers=headers,
    data='{"name": "John", "role": "admin"}'
)
Enter fullscreen mode Exit fullscreen mode

What the converter I built outputs:

import requests

headers = {
    'Authorization': 'Bearer eyJhbGci...',
    'Content-Type': 'application/json',
}

json_data = {
    'name': 'John',
    'role': 'admin',
}

# ✅ auth= tuple from -u flag
# ✅ json= because Content-Type is application/json
response = requests.post(
    'https://api.example.com/v1/users',
    headers=headers,
    json=json_data,
    auth=('myuser', 'mypassword'),
)
Enter fullscreen mode Exit fullscreen mode

The json= vs data= distinction matters: when you use json=, requests handles serialization and sets the Content-Type header correctly. When you pass data= with a raw string, you're doing it manually and it's easy to get wrong.

The conversion logic (briefly)

The rule for Python output:

if Content-Type header contains "application/json"
  AND body can be parsed as valid JSON
→ use json= (pass dict, let requests serialize)

else
→ use data= (pass raw string)
Enter fullscreen mode Exit fullscreen mode

For auth: parse -u/--user, split on the first : only — handles passwords that contain colons.
Python literals are converted properly: true → True, false → False, null → None.

All three outputs, same input

The converter supports three output targets via tab switcher — fetch, Axios, and Python requests — from a single cURL command.
fetch:

javascriptfetch("https://api.example.com/v1/users", {
  method: "POST",
  headers: {
    "Authorization": "Bearer eyJhbGci...",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ name: "John", role: "admin" })
});
Enter fullscreen mode Exit fullscreen mode

Axios:

javascriptaxios({
  method: "post",
  url: "https://api.example.com/v1/users",
  headers: {
    "Authorization": "Bearer eyJhbGci...",
    "Content-Type": "application/json"
  },
  data: { name: "John", role: "admin" }
});
Enter fullscreen mode Exit fullscreen mode

Python: (see above)

A few known limitations

Some edge cases like multipart/form-data, cookies (-b), and --data-binary are still being improved. The parser handles the vast majority of real API calls, but if something breaks, I'd genuinely like to know.

Try it

The tool is at: apidevtools.dev/tools/curl-to-fetch
It also has a JWT decoder, JSON formatter, Base64/URL/HTML encode-decode, and a few other things I use daily. All client-side, no signup.
If you hit a cURL command it gets wrong, drop it in the comments — that's genuinely useful feedback.

Top comments (0)