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
json=serializa el dict y estableceContent-Type: application/json, por lo que el servidor recibe un objeto JSON en lugar de campos de formulario.timeout=10limita la espera, por lo que un endpoint inalcanzable falla rápido en lugar de bloquear la ejecución.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.response.json()devuelve la respuesta como un dict de Python. Esta API de prueba devuelve los campos enviados y añade unidasignado por el servidor; el estado201 Createdconfirma 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 Details | Lo que confirma |
|---|---|
| Un POST a jsonplaceholder.typicode.com, estado 201 | La llamada salió como POST y el servidor creó un recurso |
| La columna de método muestra POST, no GET | requests.post envió el verbo deseado |
| La duración del request en ms | Tiempo de ida y vuelta, medido por separado del parsing JSON |
| Estado bloqueado o fallido, sin código HTTP | El request no llegó al servidor (ver CORS abajo) |
| Packages lista requests | web.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.
| Argumento | Lo que requests envía | Lo que el servidor interpreta |
|---|---|---|
json={"stars": 3} | cuerpo JSON y Content-Type: application/json | un objeto JSON |
data={"stars": 3} | cuerpo form-encoded y application/x-www-form-urlencoded | campos de formulario, no JSON |
data=json.dumps({"stars": 3}) | cadena JSON sin encabezado JSON | texto 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.