Enviar un POST JSON con Python requests

requests.post(url, json=payload) envía un cuerpo JSON a un servidor en una sola línea. El argumento json= serializa el diccionario, establece el encabezado Content-Type: application/json y escribe el cuerpo del request. La respuesta devuelve un código de estado y, en la mayoría de las APIs, los datos que el servidor almacenó, lo que permite confirmar que la llamada hizo lo esperado. Este es el patrón detrás de las escrituras en REST APIs, webhooks y cualquier endpoint que espera una carga JSON.

Ejemplo Python requests.post para JSON

Salida:

La salida aparecerá aquí...

Salida:

Status: 201
Server stored: {'language': 'Python', 'stars': 3, 'id': 101}

Cómo Funciona Este Ejemplo

  1. json= serializa el dict y establece Content-Type: application/json, por lo que el servidor recibe un objeto JSON en lugar de campos de formulario.
  2. timeout=10 limita la espera, por lo que un endpoint inalcanzable falla rápido en lugar de bloquear la ejecución.
  3. raise_for_status() convierte una respuesta 4xx o 5xx en excepción, para que una respuesta fallida nunca fluya silenciosamente a la siguiente línea.
  4. response.json() devuelve la respuesta como un dict de Python. Esta API de prueba devuelve los campos enviados y añade un id asignado por el servidor; el estado 201 Created confirma que la llamada fue tratada como un nuevo recurso.

Verificar Lo que el Servidor Recibió Realmente

Dos señales independientes confirman que el POST funcionó; una verificación confiable usa ambas. El cuerpo de la respuesta demuestra que el contenido llegó: los campos devueltos más un nuevo id significan que el servidor interpretó el JSON. Run Details demuestra el transporte: registra el request que el navegador realmente envió.

Señal de Run DetailsLo que confirma
Un POST a jsonplaceholder.typicode.com, estado 201La llamada salió como POST y el servidor creó un recurso
La columna de método muestra POST, no GETrequests.post envió el verbo deseado
La duración del request en msTiempo de ida y vuelta, medido por separado del parsing JSON
Estado bloqueado o fallido, sin código HTTPEl request no llegó al servidor (ver CORS abajo)
Packages lista requestsweb.run cargó el paquete requests al importar

Cuando el código de estado es 2xx pero el cuerpo devuelto no tiene sus campos, el transporte funcionó y el contenido no. Esa diferencia es el fallo que la mayoría de los tutoriales de POST no pueden mostrar, porque nunca ponen el request enviado y la respuesta del servidor uno al lado del otro.

json vs data: El Bug Silencioso de Pérdida de Datos

Enviar el mismo payload con data= en lugar de json= hace que el servidor responda igual con 2xx, pero el cuerpo devuelto no tendrá sus campos. El request estuvo bien; la codificación fue incorrecta.

ArgumentoLo que requests envíaLo que el servidor interpreta
json={"stars": 3}cuerpo JSON y Content-Type: application/jsonun objeto JSON
data={"stars": 3}cuerpo form-encoded y application/x-www-form-urlencodedcampos de formulario, no JSON
data=json.dumps({"stars": 3})cadena JSON sin encabezado JSONtexto que puede negarse a interpretar como JSON

Regla: pase un dict a json= para cualquier API que espere un cuerpo JSON, y use data= solo al enviar un formulario HTML. Una API JSON que recibe form encoding normalmente responde 2xx y descarta los datos silenciosamente, por eso verificar el código de estado nunca es suficiente.

Cuando un POST del Navegador Es Bloqueado, no Rechazado

Un POST cross-origin se comporta diferente en el navegador que en un script de servidor. Como el cuerpo es application/json, el navegador envía un preflight CORS OPTIONS antes del POST. Si el endpoint no devuelve los encabezados Access-Control-Allow-Origin y Access-Control-Allow-Headers correctos, el navegador bloquea el POST antes de que salga.

Cuando eso ocurre, requests lanza un error de conexión como requests.exceptions.ConnectionError, no un HTTPError, y Run Details marca el request como bloqueado o fallido sin código HTTP. Un 403 o 422 es la señal contraria: el request llegó al servidor, que decidió rechazarlo.

Regla: ningún código HTTP significa un problema de red o CORS antes del servidor; un código HTTP significa que el servidor respondió, aunque respondiera con error. jsonplaceholder tiene CORS habilitado, por lo que el ejemplo envía limpiamente. Muchas APIs públicas no lo tienen, razón por la que el mismo código requests.post puede funcionar en un terminal pero fallar en este runtime o en cualquier Python en navegador.

Errores Comunes

Error: enviar un dict con data= cuando la API espera JSON.

Incorrecto:

requests.post(url, data={"stars": 3})

Correcto:

requests.post(url, json={"stars": 3})

Por qué ocurre: data= form-codifica el dict y establece un Content-Type de formulario, por lo que una API JSON no recibe objeto JSON y sus campos desaparecen del resultado.

Error: serializar el cuerpo dos veces.

Incorrecto:

requests.post(url, json=json.dumps({"stars": 3}))

Correcto:

requests.post(url, json={"stars": 3})

Por qué ocurre: json= ya serializa el valor. Pasar una cadena ya construida la codifica doblemente como cadena JSON en lugar de un objeto.

Enviar Encabezados y Auth con el POST

La mayoría de las APIs de escritura requieren autenticación, que viaja en los encabezados del request junto al cuerpo:

response = requests.post(
    url,
    json=payload,
    headers={"Authorization": "Bearer TOKEN"},
    timeout=10,
)

requests combina sus encabezados con el Content-Type que establece para json=, así solo proporciona los específicos de la API. Nunca codifique un token real en código compartido; léalo de una variable de entorno. Un encabezado personalizado como Authorization ya es suficiente para activar el preflight CORS descrito arriba. Para el lado de lectura correspondiente, consulte leer JSON desde una URL, y envuelva la llamada en try/except para manejar errores de conexión.

FAQ

¿Cómo se envía un POST en Python?

Llame a requests.post(url, json=payload) con un diccionario como carga. El argumento json= serializa el dict, establece el encabezado Content-Type: application/json y lo envía en una sola llamada. Lea la respuesta con response.json().

¿Por qué mi POST devuelve 200 pero el servidor ignora los datos?

El cuerpo fue enviado en el formato incorrecto, normalmente como campos de formulario con data= en lugar de JSON con json=. El servidor responde 2xx al request en sí pero nunca interpreta un objeto JSON. Cambie a json= y confirme que el servidor devolvió sus campos.

¿Por qué mi POST con requests está bloqueado en el navegador?

Un POST JSON cross-origin activa un preflight CORS. Si el endpoint no devuelve Access-Control-Allow-Origin y Access-Control-Allow-Headers, el navegador bloquea el request antes de que salga; requests lanza un error de conexión sin código HTTP en lugar de un 4xx. El mismo código puede funcionar en un terminal.

¿Cuál es la diferencia entre json y data en requests.post?

json= serializa un dict en un cuerpo JSON y establece Content-Type: application/json. data= envía campos form-encoded o una cadena sin el encabezado JSON. Use json= para APIs JSON y data= para envíos de formularios HTML.