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
Two things that consistently broke:
- -u user:pass — most tools drop it completely or leave a // TODO: add auth comment
- -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"}'
)
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'),
)
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)
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" })
});
Axios:
javascriptaxios({
method: "post",
url: "https://api.example.com/v1/users",
headers: {
"Authorization": "Bearer eyJhbGci...",
"Content-Type": "application/json"
},
data: { name: "John", role: "admin" }
});
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)