Cómo enviar JSON con cURL: Guía completa sobre -d, --json y errores comunes
Specialist in Anti-Bot Strategies
Puntos Clave:
- Enviar JSON con cURL son dos cosas, no una. Adjuntas un cuerpo JSON a la solicitud y le dices al servidor que es JSON a través del encabezado
Content-Type: application/json. Omite el encabezado y muchas API rechazan o malinterpretan el cuerpo. -d/--datalleva la carga; el encabezado es responsabilidad tuya. El patrón clásico escurl -X POST -H "Content-Type: application/json" -d '{...}' URL.-dno establece ningún encabezado JSON por sí mismo.--jsones el atajo moderno. Agregado en curl 7.82.0,--json '{...}'envía el cuerpo y establece tantoContent-Type: application/jsoncomoAccept: application/jsonen una sola bandera.- La cita en la shell es donde la mayoría de la gente se quema. Envuelve el JSON en comillas simples para que la shell no consuma las comillas dobles dentro de él; en Windows
cmdlas reglas son diferentes y un archivo de carga es más seguro. @filelee el cuerpo desde el disco — pero elige la bandera de datos correcta.-d @body.jsonelimina los saltos de línea;--data-binary @body.jsony--json @body.jsonenvían el archivo byte por byte.- La misma estructura de solicitud impulsa las API reales. Una llamada JSON-RPC al punto final de Scrapeless MCP hospedado es solo un POST con un cuerpo JSON y un encabezado de autenticación — el patrón exacto que esta guía enseña.
- Gratis para empezar. Las nuevas cuentas de Scrapeless incluyen un runtime gratuito de Scraping Browser y acceso a proxy residencial — regístrate en Scrapeless.
Introducción: la solicitud con la que comienza cada integración de API
Casi cualquier API web moderna habla JSON. Te autenticas con un cuerpo JSON, envías un trabajo con un cuerpo JSON, llamas a una herramienta en un servidor MCP con un cuerpo JSON. Antes de que cualquiera de esos procesos se ejecute dentro de un script o un SDK, generalmente empieza como un único comando curl en una terminal — la forma más rápida de confirmar que un punto final se comporta como afirman los documentos.
El problema es que "enviar JSON con curl" esconde dos requisitos separados que son fáciles de confundir. Uno es adjuntar el texto JSON como el cuerpo de la solicitud. El otro es declarar, a través del encabezado Content-Type, que el cuerpo es JSON para que el servidor lo analice correctamente en lugar de tratarlo como datos de formulario. Obtén el cuerpo correcto pero olvida el encabezado y una API estricta devuelve un 400 o lee silenciosamente nada. Cita el JSON incorrectamente en tu shell y curl envía una cadena dañada que nunca fue JSON válido para empezar.
Esta guía define exactamente lo que significa "enviar JSON con curl", recorre las dos familias de banderas que lo hacen (-d más un encabezado, y el más nuevo --json), muestra ejemplos prácticos que puedes ejecutar contra un punto final de eco público, y cataloga los errores que producen los errores confusos. Termina mapeando la misma forma de solicitud en una llamada real a una API JSON — el punto final de Scrapeless MCP hospedado — para que el patrón se transfiera directamente de la terminal a la producción. Para un contexto adyacente, consulta nuestra guía sobre scraping HTTP asíncrono con aiohttp y la explicación sobre qué es un proxy SSL.
Lo que significa "Enviar JSON Con cURL"
cURL (la herramienta de línea de comandos alrededor de libcurl) transfiere datos sobre HTTP y muchos otros protocolos. "Enviar JSON con cURL" significa emitir una solicitud HTTP — casi siempre un POST, PUT o PATCH — cuyo cuerpo de solicitud es un documento JSON y cuyo encabezado Content-Type está configurado como application/json.
Esas dos partes son independientes, y ambas importan:
- El cuerpo es el texto JSON sin procesar — por ejemplo
{"product":"laptop","max_price":1200}. curl envía estos bytes tal cual como la entidad de la solicitud. - El encabezado
Content-Typele dice al servidor cómo interpretar esos bytes. Sin él, el valor predeterminado de curl para-desapplication/x-www-form-urlencoded, el formato utilizado para envíos de formularios HTML. Una API JSON que ve ese encabezado puede rechazar la solicitud o intentar (y fallar) interpretar el cuerpo como campos de formulario.
Por lo tanto, una solicitud JSON correcta siempre empareja un cuerpo JSON con el tipo de contenido JSON. La única pregunta es qué banderas de curl utilizas para producir ese emparejamiento — y esa es la diferencia entre el enfoque clásico de -d más encabezado y el atajo de bandera única --json que se cubre a continuación.
Una rápida nota terminológica: -d es la forma corta de --data, y -H es la forma corta de --header. Son intercambiables; esta guía utiliza las formas cortas en los ejemplos y nombra las formas largas donde ayuda.
Método 1: -d / --data Con un Encabezado Content-Type
Este es el enfoque portátil, que funciona en todas partes y es el que verás más en la documentación de API. Proporcionas el cuerpo con -d y el encabezado con -H:
bash
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"product":"laptop","max_price":1200}'
Están sucediendo tres cosas:
-X POSTestablece el método HTTP. Estrictamente,-dya implicaPOST, por lo que-X POSTes opcional aquí; pero declararlo hace explícita la intención y es requerido si alguna vez cambias la bandera del cuerpo de una manera que de otro modo predeterminaría aGET.-H "Content-Type: application/json"declara el formato del cuerpo.-d '{...}'adjunta el JSON. Las comillas simples evitan que el shell interprete las comillas dobles dentro del JSON.
Ejecutar eso contra httpbin.org/post — un punto final público que devuelve todo lo que recibe — retorna:
json
{
"data": "{\"product\":\"laptop\",\"max_price\":1200}",
"headers": {
"Accept": "*/*",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/8.18.0"
},
"json": {
"max_price": 1200,
"product": "laptop"
},
"origin": "203.0.113.10",
"url": "https://httpbin.org/post"
}
// Los valores de los campos son ejemplos ilustrativos; la estructura es lo que httpbin devuelve.
La señal clave de éxito es el objeto json: httpbin solo lo llena cuando el cuerpo se analiza como JSON válido y el Content-Type era application/json. La cabecera Accept es */* — el predeterminado de curl — porque -d no toca Accept. Ten en cuenta que, por sí mismo, -d establece ninguna cabecera de JSON: el Content-Type anterior está allí solo porque añadiste la línea -H. Si eliminas esa línea, httpbin informaría Content-Type: application/x-www-form-urlencoded y un campo json vacío.
Método 2: La bandera --json (curl 7.82.0+)
La bandera --json llegó en curl 7.82.0 (lanzado a principios de 2022) para colapsar el caso común en una opción. Verifica tu versión con curl --version; si reporta 7.82.0 o más reciente, --json está disponible.
bash
curl -X POST https://httpbin.org/post \
--json '{"product":"laptop","max_price":1200}'
Un solo --json realiza tres tareas a la vez. Envía el texto proporcionado como el cuerpo de la solicitud, y establece ambas de estas cabeceras por ti:
Content-Type: application/jsonAccept: application/json
Esa segunda cabecera es la diferencia práctica respecto al Método 1: --json también le dice al servidor que quieres JSON de vuelta, lo que algunas API utilizan para elegir su formato de respuesta. Repetir la solicitud a través de httpbin lo confirma:
json
{
"data": "{\"product\":\"laptop\",\"max_price\":1200}",
"headers": {
"Accept": "application/json",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/8.18.0"
},
"json": {
"max_price": 1200,
"product": "laptop"
},
"origin": "203.0.113.10",
"url": "https://httpbin.org/post"
}
// Nota que tanto Accept como Content-Type son ahora application/json.
Puedes pasar --json más de una vez y curl concatena los fragmentos en un solo cuerpo — útil para ensamblar una carga útil a partir de piezas. Si necesitas sobrescribir una de las cabeceras que establece --json (digamos, un Accept diferente), añade un -H explícito después; la última cabecera gana.
¿Cuándo deberías usar cada método? Usa --json para nuevo trabajo en un curl actual. Usa -d más -H cuando debas admitir versiones más antiguas de curl, cuando desees control total sobre qué cabeceras están presentes, o cuando la documentación que estás siguiendo esté escrita de esa manera.
| Comportamiento | -d '{...}' |
-d '{...}' -H "Content-Type: application/json" |
--json '{...}' |
|---|---|---|---|
| Envía el JSON como cuerpo | Sí | Sí | Sí |
| Método HTTP predeterminado | POST | POST | POST |
Establece Content-Type: application/json |
No (predeterminado a form-urlencoded) | Sí (tú lo estableces) | Sí (automático) |
Establece Accept: application/json |
No | No | Sí (automático) |
| Versión mínima de curl | Cualquiera | Cualquiera | 7.82.0 |
Obtén tu clave API en el plan gratuito: Scrapeless
Enviando un archivo JSON con @
El JSON en línea se vuelve poco manejable después de unos pocos campos, y las cargas grandes pertenecen a un archivo. Tanto -d como --json aceptan el prefijo @ para leer el cuerpo desde una ruta. Dado un body.json como:
json
{
"product": "laptop",
"max_price": 1200
}
Puedes enviarlo con cualquiera de las banderas:
bash
# Clásico: bandera de datos + cabecera explícita
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d @body.json
# Moderno: una bandera
curl -X POST https://httpbin.org/post \
--json @body.json
Hay una diferencia sutil pero importante en cómo se lee el archivo. -d @body.json elimina saltos de línea y retornos de carro del archivo antes de enviarlo — un remanente de que -d fue diseñado para datos de formulario. El cuerpo que llega al servidor se convierte en { "product": "laptop", "max_price": 1200}: aún es JSON válido (el espacio en blanco entre tokens está permitido), pero ya no es byte por byte lo que está en disco.
Dos banderas preservan el archivo exactamente:
bash
# --data-binary mantiene cada byte, incluidos los saltos de línea
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
--data-binary @body.json
# --json @file también envía el archivo tal cual
bash
curl -X POST https://httpbin.org/post \
--json @body.json
Para JSON ordinario, la versión sin nuevas líneas aún se analiza, por lo que -d @file generalmente funciona. Pero si la carga útil debe coincidir con el archivo byte por byte — se calcula una firma sobre los bytes exactos, o el archivo contiene un valor de cadena con nuevas líneas incrustadas significativas — usa --data-binary @file o --json @file.
También puedes canalizar un cuerpo desde stdin usando @-, lo cual es conveniente cuando otro programa genera el JSON:
bash
generate_payload | curl -X POST https://httpbin.org/post --json @-
Errores Comunes (y Cómo Evitarlos)
Estas son las fallas que convierten un curl de cinco segundos en una sesión de depuración.
1. Olvidar el encabezado Content-Type
El más común. Con -d sin encabezado, curl envía Content-Type: application/x-www-form-urlencoded. Una API JSON luego rechaza la solicitud con un 4xx o lee un cuerpo vacío. Solución: agrega -H "Content-Type: application/json", o cambia a --json, que lo establece por ti.
2. Citas del shell que rompen el JSON
JSON usa comillas dobles; la mayoría de los shells también usan comillas dobles para la interpolación. Envolver una carga útil en comillas dobles permite que el shell elimine o expanda partes antes de que curl las vea:
bash
# INCORRECTO en bash/zsh: el shell consume las comillas dobles internas
curl -X POST https://httpbin.org/post --json "{"product":"laptop"}"
# CORRECTO: usa comillas simples para toda la carga útil
curl -X POST https://httpbin.org/post --json '{"product":"laptop"}'
Solución: envuelve todo el documento JSON en comillas simples en bash/zsh. Si un valor debe contener una comilla simple literal, escápala o mueve la carga útil a un archivo y usa @file — lo cual evita por completo las citas del shell.
3. Las citas de cmd en Windows son diferentes
cmd.exe de Windows no trata las comillas simples como caracteres de cita, por lo que el truco de las comillas simples falla. Debes escapar cada comilla doble interna con una barra invertida, o — mucho más confiable — poner el JSON en un archivo y enviar @body.json. PowerShell tiene sus propias reglas de citas y su alias curl históricamente apuntó a Invoke-WebRequest; llama a curl.exe explícitamente y prefiere la forma @file para evitar sorpresas. Solución: en Windows, usa un archivo de carga útil con @body.json.
4. Permitir que -G convierta tu cuerpo en una cadena de consulta
-G/--get le dice a curl que agregue los datos de -d a la URL como parámetros de consulta en lugar de enviar un cuerpo. Esa es la herramienta correcta para solicitudes GET, pero si lo dejas activado mientras intentas enviar JSON, tu carga útil se mueve silenciosamente a la URL y el cuerpo queda vacío. Solución: no combines -G con un cuerpo JSON; usa -X POST (o deja que -d/--json predetermine POST).
5. Enviar JSON inválido
curl no valida el cuerpo — envía cualquier texto que le des. Una coma final, una clave no citada, o una cadena entre comillas simples es algo que el servidor rechazará, a menudo con un error de análisis opaco. Solución: valida la carga útil antes de enviar. Una rápida verificación local con un analizador JSON atrapa la mayor parte de esto:
bash
# Falla rápidamente con JSON mal formado antes de que curl se ejecute
echo '{"product":"laptop","max_price":1200}' | python -c "import sys, json; json.load(sys.stdin); print('valid')"
6. Olvidar Accept cuando la API negocia contenido
Algunas APIs devuelven XML o HTML a menos que pidas JSON. Con -d solo estableces Content-Type, no Accept, así que la respuesta puede no ser JSON a pesar de que tu solicitud lo fue. Solución: agrega -H "Accept: application/json", o usa --json, que establece Accept por ti.
Ejemplo Práctico: Llamando a una API JSON
Juntando todo, aquí está la forma de una llamada real a una API JSON. El endpoint hospedado de Scrapeless MCP habla JSON-RPC sobre HTTP — lo que significa que es exactamente la solicitud que has estado construyendo: un POST con un cuerpo JSON y un encabezado de autenticación. Lee la clave de API de una variable de entorno para que nunca aparezca en tu historial de shell:
bash
# El cuerpo vive en init.json; la clave proviene del entorno, no de la línea de comandos
curl -X POST "https://api.scrapeless.com/mcp" \
-H "x-api-token: ${SCRAPELESS_API_KEY}" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
--data-binary @init.json
con init.json conteniendo el apretón de manos JSON-RPC:
json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "curl-demo", "version": "1.0" }
}
}
Cada concepto de esta guía está presente: un cuerpo JSON (aquí de un archivo, enviado textualmente con --data-binary), el encabezado Content-Type: application/json que lo marca como JSON, un encabezado Accept que nombra los formatos que aceptarás de vuelta, y un encabezado de autenticación que lleva las credenciales. El endpoint alojado expone aproximadamente una veintena de herramientas — google_search, scrape_html, scrape_markdown, el conjunto de automatización browser_* y más — cada una invocada con el mismo patrón de POST de un cuerpo JSON, solo cambian el method y los params. Los detalles de configuración se encuentran en la documentación de Scrapeless.
No tienes que seguir hablando con el endpoint en curl sin procesar, por supuesto — pero probar un endpoint primero con curl y luego portar la solicitud verificada a tu lenguaje de elección es el flujo de trabajo que ahorra más tiempo. Para el catálogo completo de herramientas del servidor MCP y los avisos de agentes trabajados, consulta 5 casos de uso de Scrapeless MCP.
Cómo encaja Scrapeless
Una vez que un comando curl funciona, el siguiente paso suele ser hacerlo a gran escala — muchas solicitudes, contra sitios que renderizan contenido con JavaScript o pantalla de tráfico automatizado. Ahí es donde la forma de solicitud que acabas de aprender se encuentra con la infraestructura gestionada.
Scrapeless proporciona un navegador de nube anti-detección — el Navegador de Scraping de Scrapeless — y proxies residenciales en más de 195 países, accesibles a través del endpoint MCP alojado, un SDK y una CLI. El navegador renderiza páginas pesadas en JavaScript del lado de la nube y gestiona las huellas digitales, para que la solicitud JSON limpia que prototipaste en curl devuelva datos estructurados en lugar de una página de desafío. El detalle del transporte — fijar un egreso residencial, persistir una sesión — se maneja por ti; tu lado sigue siendo el mismo simple bucle de "POST un cuerpo JSON, leer JSON de vuelta".
Explora el producto del Navegador de Scraping, revisa los planes en la página de precios, y encuentra la referencia de la API y MCP en la documentación.
Conclusión
Enviar JSON con curl se reduce a dos requisitos hechos juntos: adjuntar el JSON como el cuerpo de la solicitud y declararlo como JSON con el encabezado Content-Type. La forma clásica es -d '{...}' más -H "Content-Type: application/json"; la forma moderna de una sola bandera es --json '{...}', que establece tanto Content-Type como Accept por ti en curl 7.82.0 y versiones más nuevas. Mueve cargas grandes o firmadas a un archivo y envíalas con --data-binary @file o --json @file para preservar cada byte, usa comillas simples para el JSON en línea en bash para sobrevivir a las comillas del shell, y busca un archivo de carga en Windows. La misma solicitud — cuerpo más tipo de contenido más un encabezado de autenticación — es exactamente como se ve la llamada a una API JSON real como el endpoint MCP de Scrapeless, por lo que un curl que funciona en tu terminal se porta limpiamente a producción. Para lecturas relacionadas, consulta scraping HTTP asincrónico con aiohttp y qué es un proxy SSL.
FAQ
Q: ¿Cuál es la forma más simple de enviar JSON con curl?
En un curl actual (7.82.0 o más nuevo), curl --json '{"key":"value"}' URL es la forma más corta correcta — envía el cuerpo y establece tanto el encabezado Content-Type como el Accept en application/json. En curl más antiguo, usa curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' URL.
Q: ¿Por qué mi API JSON dice que el cuerpo falta o es inválido aunque lo envié?
Dos causas habituales. O bien enviaste -d sin un encabezado Content-Type: application/json, así que el servidor lo leyó como datos de formulario — añade el encabezado o usa --json. O tu shell dañó el JSON porque estaba envuelto en comillas dobles; usa comillas simples para la carga o muévelo a un archivo y envía @file.
Q: ¿Cuál es la diferencia entre -d, --data-binary, y --json?
-d (--data) envía el cuerpo y, para @file, elimina las nuevas líneas; no establece encabezados JSON por sí mismo. --data-binary envía el cuerpo exactamente como se proporciona, nuevas líneas y todo. --json envía el cuerpo textualmente y establece Content-Type y Accept en application/json; requiere curl 7.82.0 o más.
Q: ¿Cómo envío un archivo JSON en lugar de texto en línea?
Prefija la ruta con @: curl --json @body.json URL, o curl -H "Content-Type: application/json" --data-binary @body.json URL. Prefiere --json @file o --data-binary @file sobre -d @file cuando los bytes deben coincidir exactamente con el archivo, porque -d @file elimina las nuevas líneas.
Q: ¿Cómo envío JSON con curl en Windows?
cmd.exe no respeta las comillas simples, así que el camino más fácil y fiable es poner el JSON en un archivo y enviarlo con @body.json. Si debes incrustarlo, escapa cada comilla doble interna con una barra invertida. En PowerShell, llama a curl.exe explícitamente para no golpear el alias Invoke-WebRequest, y aún así prefiere la forma @file.
Q: ¿Necesito configurar el encabezado Content-Type si uso --json?
No. --json configura automáticamente Content-Type: application/json, junto con Accept: application/json. Solo agregarías un encabezado explícito para anular uno de esos — por ejemplo, un Accept diferente — en cuyo caso coloca el -H después de --json para que tenga prioridad.
¿Listo para construir tu canalización de datos impulsada por IA?
Únete a nuestra comunidad para reclamar un plan gratuito y conectar con desarrolladores que construyen canalizaciones de recolección de datos basadas en JSON: Discord · Telegram.
Regístrate en Scrapeless para acceder gratuitamente al tiempo de ejecución del navegador de raspado y a proxies residenciales, y convierte la solicitud curl que prototipaste en una canalización de datos de producción.
En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.



