Curso de Python / Consultar una API HTTP y validar su respuesta JSON
Una API HTTP recibe peticiones y devuelve un estado y datos de respuesta. Un cliente de Python debe comprobar por separado la comunicación, los límites de espera, el tamaño recibido y la estructura del JSON antes de utilizar sus valores.
Separar protocolo, dirección y formato
HTTP es el protocolo de petición y respuesta; la URL identifica un recurso; JSON es uno de los formatos posibles del cuerpo. Recibir un estado satisfactorio no garantiza que los campos cumplan las reglas de la aplicación. En un servicio externo también necesitas sus condiciones de HTTPS, autenticación y límites de uso.
Este ejercicio utiliza un servidor local para controlar tanto la respuesta válida como los errores. No representa un despliegue de producción. La conversión y validación de JSON explica por qué interpretar la sintaxis no equivale a aceptar los datos.
Preparar la respuesta y arrancar el servidor
En una carpeta nueva, guarda un archivo expenses.json con este contenido completo:
[{"id":"e1","name":"coffee","amount":3500},{"id":"e2","name":"lunch","amount":8000}]
Abre una primera terminal en esa carpeta y ejecuta:
python -m http.server 8000 --bind 127.0.0.1
La dirección 127.0.0.1 limita el servidor al propio equipo. Si el puerto 8000 está ocupado, elige otro y modifica también la URL del cliente. Mantén esta terminal abierta mientras realizas las peticiones.
Ejecutar el cliente y leer el resultado
Guarda el siguiente código como fetch_expenses.py. Solo necesita la biblioteca estándar; utiliza urllib.request para la petición y json para interpretar el contenido.
import json
import sys
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
url = sys.argv[1]
try:
with urlopen(url, timeout=5) as response:
if response.status != 200:
raise ValueError("unexpected status")
raw = response.read(1_000_001)
if len(raw) > 1_000_000:
raise ValueError("response too large")
data = json.loads(raw.decode("utf-8"))
except (HTTPError, URLError, TimeoutError, UnicodeError, ValueError) as error:
raise SystemExit(f"request failed: {type(error).__name__}")
if not isinstance(data, list):
raise SystemExit("expected a list")
total = 0
for row in data:
if not isinstance(row, dict) or type(row.get("amount")) is not int or row["amount"] < 0:
raise SystemExit("invalid record")
total += row["amount"]
print("records:", len(data))
print("total:", total)
En una segunda terminal con el entorno del proyecto seleccionado, ejecuta:
python fetch_expenses.py http://127.0.0.1:8000/expenses.json
La salida esperada es records: 2 y total: 11500, en líneas separadas. El cliente lee un byte más que el límite para detectar respuestas superiores a un millón de bytes. Después exige una lista de diccionarios y comprueba que cada importe sea un entero no negativo; excluye los booleanos mediante la comprobación exacta de tipo.
Diferenciar fallos de comunicación y datos inválidos
HTTPError representa una respuesta HTTP de error, mientras que URLError puede indicar problemas de conexión o resolución de nombres. También pueden fallar el tamaño, la codificación UTF-8, la sintaxis JSON o un registro. Conserva esas diferencias en vez de reemplazarlas por datos que parezcan un resultado correcto.
El parámetro timeout limita esperas de operaciones bloqueantes; no garantiza un único plazo total para toda la descarga. La referencia de urllib.request describe ese alcance. Los reintentos necesitan un máximo, una demora y una política de idempotencia: repetir una operación con efectos podría duplicar trabajo.
- Pide un nombre de archivo inexistente y comprueba el error HTTP.
- Cambia un importe a una cadena y observa el rechazo del registro.
- Detén el servidor con Ctrl+C y distingue el fallo de conexión.
Restaura la respuesta válida y detén el servidor al terminar. Mantén las credenciales fuera del código, las URL y los registros. El proyecto de recopilación de una API añade almacenamiento transaccional a este flujo de petición y validación.




