Convertir un comando curl a código: JavaScript, Python, Go, PHP y Node.js

Pega un comando curl y la herramienta escribe el código que hace la misma petición en JavaScript (fetch), Python (requests), Go, PHP y Node.js. Sirve el que copias de la documentación de una API y el de «Copiar como cURL» de las herramientas de desarrollo del navegador, en bash, en cmd de Windows o en PowerShell. Se convierte en tu navegador: tus cabeceras y tokens no se envían a ningún lado.

Cuidado con lo que copias del navegador. «Copiar como cURL» incluye tus cookies de sesión y, a menudo, un token en la cabecera Authorization: con ellos, cualquiera puede entrar en tu cuenta mientras sigan vigentes. La herramienta te avisa cuando los encuentra. Quítalos antes de pegar el código en un foro, un chat con una IA o un repositorio.

Qué opciones de curl entiende

  • Método y dirección: -X, -I (solo cabeceras, HEAD), -G (los datos van a la URL), --url y la URL suelta, con o sin http://.
  • Cabeceras: -H, -A (User-Agent), -e (Referer), -b con cookies escritas y --oauth2-bearer. Como curl, -H 'Nombre:' quita una cabecera y -H 'Nombre;' la envía vacía.
  • Cuerpo: -d, --data-raw, --data-binary, --data-urlencode y --json. Con -d, curl pone por su cuenta Content-Type: application/x-www-form-urlencoded, y el código lo pone también para que el servidor reciba lo mismo. Si el cuerpo es JSON, se escribe como objeto del lenguaje.
  • Formularios con archivos: -F campo=valor, -F archivo=@foto.png con ;type= y ;filename=, y --form-string.
  • Usuario y contraseña: -u usuario:contraseña, con la autenticación básica de cada biblioteca.
  • Comportamiento: -L (seguir redirecciones), -k (aceptar certificados no válidos), -m (tiempo máximo) y --compressed.

Lo que no cambia la petición, como -s, -v u -o, se ignora, y lo que no se puede convertir se avisa. Las comillas se interpretan como lo haría la shell: comillas simples y dobles, $'…' con \n o \xHH, el ^ de cmd y el acento grave de PowerShell.

Diferencias entre curl y cada lenguaje

  • Redirecciones: curl no las sigue salvo con -L; fetch, requests y Go sí. Por eso el código de Python, Go y Node las desactiva cuando el comando no lleva -L. En el navegador, fetch siempre las sigue. Y al seguir una, Go añade por su cuenta la cabecera Referer.
  • Cabeceras prohibidas en el navegador: fetch no deja poner Cookie, Host, Origin, Referer, Accept-Encoding, Content-Length ni las Sec-*, que pone el propio navegador. El código de JavaScript las deja fuera y lo explica en un comentario. Además, la página tiene que ser del mismo sitio que la API, o la API debe permitirlo con CORS. El código de Node.js no tiene estas limitaciones, salvo la de Host.
  • Cabeceras repetidas: requests no las admite y las une separadas por comas, que para HTTP significa lo mismo.
  • Campos de texto con tipo (-F 'nota=hola;type=text/plain'): FormData y PHP solo pueden ponerle tipo a un archivo, así que el campo viaja como archivo. Y PHP no admite dos campos con el mismo nombre en el mismo formulario.
  • Cabeceras por defecto: cada biblioteca manda su propio User-Agent y su Accept-Encoding. Si una API se comporta distinto con curl, suele ser por eso.

Leer un comando curl desde Python

Si necesitas procesar comandos curl en un programa, shlex, de la biblioteca estándar, los parte en palabras con las reglas de la shell:

import shlex
from urllib.parse import urlsplit

comando = """curl 'https://api.ejemplo.com/v1/pedidos?estado=pendiente' \\
  -H 'Authorization: Bearer TU_TOKEN' \\
  -H 'Content-Type: application/json' \\
  --data-raw '{"cliente":"Ana","urgente":true}'"""

# shlex parte el texto como lo haría la shell y respeta las comillas, pero no entiende la barra del final
# de línea: hay que quitar antes cada «\» seguida de un salto
palabras = shlex.split(comando.replace("\\\n", " "))
print(palabras[:4], "…")

metodo, url, cabeceras, datos = None, None, {}, []
i = 1
while i < len(palabras):
    p = palabras[i]
    if p in ("-X", "--request"):
        metodo = palabras[i + 1]; i += 1
    elif p in ("-H", "--header"):
        nombre, _, valor = palabras[i + 1].partition(":")
        cabeceras[nombre.strip()] = valor.strip(); i += 1
    elif p in ("-d", "--data", "--data-raw", "--data-binary"):
        datos.append(palabras[i + 1]); i += 1
    elif not p.startswith("-"):
        url = p
    i += 1

metodo = metodo or ("POST" if datos else "GET")            # como curl: con -d, POST
print(metodo, urlsplit(url).netloc, urlsplit(url).path, urlsplit(url).query)
for nombre, valor in cabeceras.items():
    print(f"  {nombre}: {valor}")
print("  cuerpo:", "&".join(datos))
$ python3 curl_a_python.py['curl', 'https://api.ejemplo.com/v1/pedidos?estado=pendiente', '-H', 'Authorization: Bearer TU_TOKEN'] …
POST api.ejemplo.com /v1/pedidos estado=pendiente
  Authorization: Bearer TU_TOKEN
  Content-Type: application/json
  cuerpo: {"cliente":"Ana","urgente":true}

Otras herramientas para desarrolladores: formateador de JSON, decodificador de JWT, codificar Base64 y URL y leer un certificado. Todas, en decodigo.com/herramientas.

Opciones de curl: manual de curl. Cabeceras que el navegador no deja cambiar: estándar Fetch (WHATWG). Bibliotecas: requests, net/http de Go y cURL de PHP. Para probarla, 45 comandos se enviaron a un servidor de prueba con curl 7.88 y con el código generado en Python 3.13 (requests), Node.js 24, Go 1.24 y PHP 8.3: llegaron el mismo método, la misma ruta, las mismas cabeceras y el mismo cuerpo, salvo en los casos que se explican arriba.