---
title: "cURL en JavaScript: fetch, Axios y proxy"
description: "El equivalente en JavaScript de cURL son fetch y Axios. Explicamos cabeceras, cuerpo, autenticación y cómo configurar un proxy en Node.js con ejemplos."
url: https://proxynet.io/es/blog/curl-in-javascript
date: 2026-09-13
author: "Acar Diveroli"
category: "Tutoriales"
lang: es
---

# cURL en JavaScript: fetch, Axios y proxy

La documentación de las API y las herramientas de desarrollador del navegador suelen dar el ejemplo de solicitud como un **comando cURL**. Es ideal para una prueba rápida en la terminal, pero cuando necesitas llevar esa misma solicitud a una aplicación Node.js o a un script de scraping, tienes que traducir el comando a JavaScript. En este artículo explicamos los equivalentes en JavaScript de las opciones de cURL más usadas, ejemplos de conversión, cuatro formas de usar un proxy en Node.js, la gestión de errores y los errores más habituales.

Todos los ejemplos se han probado con Node.js 24, undici 8 y Axios 1.20.

> **Nota: Respuesta breve**
>
> El comando `curl URL` equivale a `fetch(url)` o a `axios.get(url)`; las cabeceras de `-H` pasan al objeto `headers`, el cuerpo de `-d` pasa a `body` o al segundo parámetro, y `-x proxy` se convierte en el `ProxyAgent` de undici en Node.js o en el agente `https-proxy-agent` para Axios. En el JavaScript que se ejecuta en el navegador, el proxy no se puede definir desde el código.

## ¿Cuál es el equivalente de cURL en JavaScript?

Hay dos opciones principales:

- **fetch:** viene integrado en los navegadores y, desde Node.js 18, también en Node.js. No requiere paquetes adicionales. La implementación de fetch de Node.js usa por debajo la librería [undici](https://undici.nodejs.org/).
- **Axios:** una librería popular usada desde hace tiempo, que convierte a JSON de forma automática y ofrece soporte de interceptores de solicitud y respuesta.

También es posible reutilizar un comando cURL sin salir del código (con `child_process`), pero este método complica la gestión de errores y la portabilidad, y depende de que cURL esté instalado en la máquina. Casi siempre es mejor escribirlo con un cliente nativo.

## Equivalentes en JavaScript de las opciones de cURL

| cURL | fetch | Axios |
|---|---|---|
| `curl URL` | `fetch(url)` | `axios.get(url)` |
| `-X POST` | `method: "POST"` | `axios.post(url, datos)` |
| `-H "Nombre: Valor"` | `headers: { Nombre: "Valor" }` | `headers: { Nombre: "Valor" }` |
| `-d '{"a":1}'` | `body: JSON.stringify({ a: 1 })` | objeto como segundo parámetro |
| `-d "a=1&b=2"` | `body: new URLSearchParams({ a: 1, b: 2 })` | `new URLSearchParams(...)` como segundo parámetro |
| `-F "archivo=@a.png"` | `body: FormData` | `FormData` como segundo parámetro |
| `-u usuario:contraseña` | cabecera `Authorization: "Basic ..."` | `auth: { username, password }` |
| `-b "nombre=valor"` (cookie) | `headers: { Cookie: "nombre=valor" }` | `headers: { Cookie: "nombre=valor" }` |
| `-A "UA"` | `headers: { "User-Agent": "UA" }` | `headers: { "User-Agent": "UA" }` |
| `-L` (seguir redirecciones) | Las sigue por defecto | Las sigue por defecto |
| `-x proxy` | `dispatcher: new ProxyAgent(...)` | `httpsAgent` o `proxy` |
| `-m 20` (tiempo de espera) | `signal: AbortSignal.timeout(20000)` | `timeout: 20000` |
| `-i` (cabeceras de respuesta) | `response.headers` | `response.headers` |
| `-o archivo` | escribe el cuerpo con `fs.writeFile` | `responseType: "stream"` |

## Ejemplo: traducir una solicitud POST

Tomemos el siguiente comando cURL:

```bash
curl -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer TOKEN" \
  -d '{"q": 1}'
```

### Con fetch

```javascript
const response = await fetch("https://httpbin.org/post", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer TOKEN",
  },
  body: JSON.stringify({ q: 1 }),
  signal: AbortSignal.timeout(20000),
});

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

fetch tiene una particularidad: cuando el servidor devuelve 404 o 500, **no lanza un error**. Debes comprobar tú mismo si la respuesta fue exitosa con `response.ok`. Un error de red (DNS, conexión rechazada, tiempo de espera) sí llega como una excepción.

### Con Axios

```bash
npm install axios
```

```javascript
import axios from "axios";

const { data } = await axios.post(
  "https://httpbin.org/post",
  { q: 1 },
  { headers: { Authorization: "Bearer TOKEN" }, timeout: 20000 },
);

console.log(data.json);
```

Axios convierte el objeto a JSON por sí mismo, añade la cabecera `Content-Type` y lanza un error en las respuestas que no sean 2xx.

## Ejemplo: envío de formularios y subida de archivos

Un envío de formulario con el formato `-d "a=1&b=2"` de cURL se corresponde en JavaScript con `URLSearchParams`:

```javascript
const response = await fetch("https://httpbin.org/post", {
  method: "POST",
  body: new URLSearchParams({ a: "1", b: "2" }),
});
console.log((await response.json()).form);
```

Cuando se le pasa un cuerpo `URLSearchParams`, fetch establece por sí mismo la cabecera `Content-Type` como `application/x-www-form-urlencoded`. Para la subida de archivos (`-F`) se usa `FormData`, y el valor del límite (boundary) también se añade de forma automática. Un detalle: si importas la función `fetch` del paquete undici, importa también la clase `FormData` del mismo paquete; el `FormData` integrado no combina bien con el `fetch` de undici, y el cuerpo se envía como texto plano.

```javascript
import { openAsBlob } from "node:fs";

const form = new FormData();
form.append("dosya", await openAsBlob("./a.png"), "a.png");

const response = await fetch("https://httpbin.org/post", { method: "POST", body: form });
console.log(Object.keys((await response.json()).files));
```

## Cuatro formas de usar un proxy en Node.js

En cURL, el proxy es una única opción `-x`. En Node.js, el método cambia según el cliente que uses.

### 1. fetch y el ProxyAgent de undici

```bash
npm install undici
```

```javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

const response = await fetch("https://httpbin.org/ip", { dispatcher });
console.log(await response.text());
```

Fíjate en que también importamos la función `fetch` del paquete undici. El fetch integrado de Node.js también está basado en undici, pero como el paquete instalado por separado puede tener una versión distinta, tomar ambos del mismo paquete es lo más seguro.

### 2. Ajuste global para todas las llamadas fetch

Si quieres que todas las llamadas fetch de la aplicación usen el mismo proxy, puedes definir un dispatcher global:

```javascript
import { ProxyAgent, setGlobalDispatcher } from "undici";

setGlobalDispatcher(new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000"));

const response = await fetch("https://httpbin.org/ip");
console.log(await response.text());
```

Este método también cubre las llamadas fetch que se hacen desde dentro de una librería de terceros; así haces pasar su tráfico por el proxy sin tocar el código fuente de la librería.

### 3. Con una variable de entorno, sin tocar el código

Las versiones recientes de Node.js pueden leer las variables de entorno estándar de proxy para el fetch integrado cuando se activa la variable `NODE_USE_ENV_PROXY`:

```bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://kullanici:parola@pr.proxynet.io:8000" node uygulama.mjs
```

Este método es útil para hacer pasar un script existente por el proxy sin tocar el código fuente. También se lee la variable `NO_PROXY`; puedes añadir a esta lista las direcciones de tu red interna para mantenerlas fuera del proxy. Comprueba en la [documentación de Node.js](https://nodejs.org/api/cli.html) si tu versión incluye esta función.

### 4. Axios y destinos HTTPS

La opción `proxy` integrada de Axios funciona sin problemas para direcciones HTTP sin cifrar:

```javascript
const { data } = await axios.get("http://httpbin.org/ip", {
  proxy: {
    protocol: "http",
    host: "pr.proxynet.io",
    port: 8000,
    auth: { username: "kullanici", password: "parola" },
  },
});
```

Para direcciones HTTPS, en cambio, es más fiable usar un agente de proxy:

```bash
npm install https-proxy-agent
```

```javascript
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const httpsAgent = new HttpsProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

const { data } = await axios.get("https://httpbin.org/ip", { httpsAgent, proxy: false });
console.log(data);
```

La línea `proxy: false` es importante: desactiva la lógica de proxy propia de Axios y deja el túnel entero en manos del agente. Si creas el agente una sola vez y lo asocias a una instancia con `axios.create({ httpsAgent, proxy: false })`, no tienes que repetirlo en cada llamada.

> **Consejo**
>
> Aunque la dirección del proxy empiece por `http://`, puedes acceder con seguridad a sitios HTTPS. El proxy establece un túnel a través de HTTP, y el tráfico HTTPS del sitio viaja cifrado dentro de ese túnel. Puedes ver el detalle de las diferencias en nuestro artículo [SOCKS frente a HTTP Proxy](/es/blog/socks-vs-http-proxy).

## Comparación de los cuatro métodos

| Método | Alcance | Cambio de código | Cuándo conviene |
|---|---|---|---|
| `ProxyAgent` de undici | Una sola llamada | `dispatcher` en cada llamada | Proxy distinto para solicitudes distintas |
| `setGlobalDispatcher` | Todas las llamadas fetch | Una línea, al inicio | Un único proxy para toda la aplicación |
| `NODE_USE_ENV_PROXY` | Todas las llamadas fetch integradas | Ninguno | Script existente, entorno de despliegue |
| Axios + agente | Instancia de Axios | `httpsAgent` + `proxy: false` | Cuando el proyecto ya usa Axios |

## Reintentos y concurrencia

En un script de scraping real, las solicitudes a veces fallan: el proxy puede no llegar al destino o el sitio puede devolver un 429. La siguiente función auxiliar reintenta la solicitud fallida con espera exponencial y limita cuántas solicitudes están abiertas al mismo tiempo:

```javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://kullanici:parola@pr.proxynet.io:8000");

async function getir(url, deneme = 3) {
  for (let i = 0; i < deneme; i++) {
    try {
      const r = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
      if (r.status === 429 || r.status >= 500) throw new Error(`HTTP ${r.status}`);
      return await r.text();
    } catch (hata) {
      if (i === deneme - 1) throw hata;
      await new Promise((coz) => setTimeout(coz, 1000 * 2 ** i));
    }
  }
}

async function topluGetir(urls, esZamanli = 5) {
  const sonuclar = [];
  for (let i = 0; i < urls.length; i += esZamanli) {
    const grup = urls.slice(i, i + esZamanli);
    sonuclar.push(...(await Promise.allSettled(grup.map(getir))));
  }
  return sonuclar;
}

const urls = Array.from({ length: 20 }, (_, i) => `https://httpbin.org/get?i=${i}`);
const sonuclar = await topluGetir(urls);
console.log(sonuclar.filter((s) => s.status === "fulfilled").length, "başarılı");
```

`Promise.allSettled` evita que el error de una solicitud tire abajo todo el grupo; puedes examinar cada resultado por separado. El tiempo de espera se duplica en cada intento; insistir con un intervalo fijo solo hace que un sitio con límite de velocidad se vuelva aún más estricto.

## Errores frecuentes

### Caracteres especiales en la contraseña

Si la contraseña de tu proxy contiene caracteres como `@`, `:` o `/`, codifícalos dentro de la dirección con `encodeURIComponent`. De lo contrario, la dirección se interpreta mal y recibirás un error «407 Proxy Authentication Required».

### Solicitudes sin tiempo de espera

Con la configuración por defecto, una solicitud puede quedarse esperando respuesta durante mucho tiempo y tu script se bloquea. Pon un límite de tiempo a cada solicitud: `AbortSignal.timeout` en fetch, `timeout` en Axios.

### Demasiadas solicitudes a la vez

Lanzar cientos de solicitudes a la vez con `Promise.all` fuerza tus propios límites de conexión y hace que te topes con el límite de velocidad del sitio de destino. Limita la concurrencia en grupos pequeños, como en el ejemplo anterior. En trabajos que envían muchas solicitudes, un [Proxies rotativos](https://proxynet.io/es/rotating-proxy) reduce el riesgo de bloqueo repartiendo el tráfico entre distintas IP.

### Pasar `agent` al fetch integrado

En la antigua API `http.request` de Node.js, el proxy se pasa con la opción `agent`. El fetch integrado no reconoce esta opción y la ignora en silencio; la solicitud sale sin proxy. La clave correcta para fetch es `dispatcher`.

### Copiar tal cual las cabeceras del navegador

Las solicitudes copiadas de las herramientas de desarrollador llevan cabeceras propias del navegador, como `sec-ch-ua` o `sec-fetch-*`. En Node.js no hace falta enviarlas, y algunas pueden interpretarse como una incoherencia en el sitio de destino; deja solo las cabeceras que realmente necesites.

### Páginas para las que no basta con una solicitud HTTP

fetch y Axios solo obtienen el HTML que devuelve el servidor. Si el contenido se carga después con JavaScript en el navegador, no encontrarás lo que buscas en la página. En ese caso, o localizas la solicitud a la API que ocurre en segundo plano, o usas automatización de navegador. Comparamos las opciones en nuestro artículo [Web scraping: ¿JavaScript o Python?](/es/blog/web-scraping-javascript-vs-python); mostramos el uso de proxy con Puppeteer en [Puppeteer y CAPTCHA](/es/blog/puppeteer-captcha).

## ¿fetch o Axios?

- Elige **fetch** si no quieres una dependencia adicional, si el código debe funcionar tanto en el navegador como en Node.js, o si quieres aprovechar las funciones de gestión de conexiones de undici.
- Elige **Axios** si necesitas comodidades como la conversión automática a JSON, los interceptores y el lanzamiento de errores en respuestas que no son 2xx, o si el proyecto ya lo usa.

La diferencia entre ambos es más una cuestión de costumbre que de funcionalidad. Empezar un proyecto nuevo con fetch mantiene bajo el número de dependencias; en un proyecto grande que ya usa Axios no hay motivo para cambiar a fetch.

## Preguntas frecuentes

### ¿Puedo convertir automáticamente un comando cURL a JavaScript?

En la pestaña de Red de las herramientas de desarrollador del navegador, puedes hacer clic derecho sobre una solicitud y copiarla directamente como código fetch con la opción «Copy as fetch». Aun así, revisa el código resultante; las cabeceras propias del navegador pueden ser innecesarias en Node.js. Si vas a llevar el mismo comando a Python, en [POST con JSON en Python Requests: equivalencias de cURL](/es/blog/python-requests-post-json) reunimos en una tabla las equivalencias de las opciones de cURL con `json=`, `data=` y `params=`.

### ¿Puedo usar un proxy SOCKS5 en Node.js?

El fetch integrado y el ProxyAgent de undici son para proxies HTTP. Para SOCKS5 necesitas definir un agente con un paquete adicional, como `socks-proxy-agent`. En cURL, en cambio, el esquema `socks5h://` es compatible de forma directa; puedes ver los detalles en nuestro artículo [¿Cómo usar un proxy con cURL?](/es/blog/curl-proxy).

### ¿Se puede definir un proxy en el JavaScript que corre en el navegador?

No. El fetch del navegador no te permite cambiar el ajuste de proxy desde el código; el proxy se define en la configuración del navegador o del sistema operativo. Gestionar el proxy desde el código es propio de entornos de servidor como Node.js. Para el proxy a nivel de navegador se usan extensiones como [SwitchyOmega](/es/blog/switchy-omega).

### ¿Cómo conservo las cookies entre solicitudes?

El fetch integrado no guarda cookies. Tienes que leer la cabecera `Set-Cookie` de la respuesta y añadirla a la siguiente solicitud como cabecera `Cookie`; o bien usar con Axios un almacén de cookies basado en `tough-cookie`. No olvides que en los flujos con inicio de sesión la IP también debe mantenerse fija; el [Proxies de sesión fija](https://proxynet.io/es/sticky-proxy) está diseñado para esto.

### ¿Puedo usar un proxy distinto en cada solicitud?

Sí. Con undici puedes dar un `ProxyAgent` distinto a cada llamada. Pero en lugar de gestionar tú mismo la lista, es más sencillo usar un proxy rotativo: una sola dirección, con una IP de salida distinta en cada conexión.

### ¿El mismo código funciona en TypeScript?

Sí. undici y Axios incluyen definiciones de tipos; para la opción `dispatcher` hay que usar el propio tipo `fetch` de undici, porque la definición de tipos del fetch integrado no reconoce esa opción.

## En resumen

Los comandos cURL tienen un equivalente directo en JavaScript con fetch o Axios. En Node.js hay cuatro formas de usar un proxy: el ProxyAgent de undici, un dispatcher global, la variable de entorno `NODE_USE_ENV_PROXY` y un agente de proxy para Axios. Añadir desde el principio tiempo de espera, control de errores, reintentos con espera exponencial y un límite de concurrencia hace tus scripts mucho más fiables. Puedes encontrar los planes adecuados para tu tráfico web en nuestra página de [Proxies HTTPS](https://proxynet.io/es/https-proxy).
