Saltar al contenido
Volver al inicio 100 Días de Código en Python — Día 96: Proyecto de portafolio — Un sitio web construido sobre una API pública

100 Días de Código en Python — Día 96: Proyecto de portafolio — Un sitio web construido sobre una API pública

Nivel: Grado décimo Área: Tecnología e Informática Modalidad: Tutorial práctico Fuente: Curso "100 Days of Code: The Complete Python Pro Bootcamp" (Angela Yu, App Brewery). Contenido del Día 96, proyecto de portafolio profesional (categoría HTTP Requests & APIs).

Este tutorial adapta pedagógicamente el Día 96. La sección 📝 Contenido original del curso proviene del único archivo de la carpeta: dia96-tarea15.md (enunciado, lista de APIs sugeridas y pregunta de reflexión, en inglés; aquí traducido). El curso no ofrece guía ni solución. Este tutorial muestra las piezas con una API de ejemplo (Open-Meteo, clima) para que tú construyas tu sitio con otra API de tu elección. Todo lo que no está en ese archivo (estado actual de las APIs sugeridas, criterios para elegir una API, código de ejemplo verificado, caché, manejo de errores, pruebas con simulaciones, retos 🟢🟡🟠🔴, proyecto extra y la etapa 15 de Mi Portafolio Profesional) es creación de esta adaptación.

Nota sobre las verificaciones: el 28 de septiembre de 2026, con Python 3.14, Flask 3.1.3 y requests 2.34.2: (1) se consultaron las páginas de las 13 APIs sugeridas (resultado en la sección 2); (2) se ejecutó la aplicación de la sección 4 contra la API real de Open-Meteo: /clima/bogota respondió 200 con la temperatura actual en 0,85 s y, gracias a la caché, la segunda consulta tardó menos de 1 ms; una ciudad desconocida respondió 404; (3) con una simulación de fallo de red (unittest.mock.patch), la página respondió 503 con un mensaje amable; (4) una latitud inválida enviada a la API devolvió 400 con un mensaje explicativo.


Tabla de contenidos

  1. La tarea: tu servicio sobre una API que te interese
  2. 📖 Elegir la API: ¿sigue viva?, ¿qué pide?
  3. Analizar el problema antes de programar
  4. 📖 La pieza central: una función que consulta la API
  5. 📖 Probar sin depender de internet: simulaciones
  6. 🚀 Proyecto del día: construcción por versiones
  7. 🌟 Proyecto extra: tablero de datos abiertos del municipio
  8. 🏗️ Mi Portafolio Profesional — Etapa 15
  9. 🧩 Comprueba lo aprendido
  10. 🤖 Programar con apoyo de IA
  11. 🛠️ Observaciones sobre el material original
  12. 📋 Matriz de trazabilidad
  13. 📦 Evidencias de aprendizaje del Día 96

1. La tarea: tu servicio sobre una API que te interese

(Fuente: dia96-tarea15.md)

📝 Contenido original del curso

Usando lo que aprendiste sobre peticiones HTTP y APIs REST, en el proyecto de hoy construirás un sitio web que use datos de una API pública.

Por ejemplo, antes creamos una alerta de lluvia con una API del clima, un rastreador de la ISS y consultamos precios de Bitcoin, todo con APIs públicas.

Hoy trabajas con una API que te parezca interesante y construyes un servicio o sitio web basado en ella.

APIs de ejemplo: datos del metro de Nueva York; datos de elefantes; generación y reconocimiento de códigos de barras; mercado de valores (marketstack); datos de Harry Potter; datos de arte (Artsy); diccionario (Owlbot); datos de ESPN; información de alimentos (Food Facts); cervecerías (Open Brewery DB); corrección ortográfica y gramatical (Perfect Tense); efectos de sonido (Freesound); El Señor de los Anillos (The One API).

Pregunta de la tarea — Momento de reflexión: cómo abordaste el proyecto; qué fue difícil y qué fácil; cómo mejorarías; tu mayor aprendizaje; qué harías distinto.

Prerequisitos: requests, códigos de estado, parámetros y JSON (Día 33); claves de API y variables de entorno (Día 35); Flask y Jinja (Días 54–57); excepciones (Día 30); publicar una aplicación (Día 71).


2. 📖 Elegir la API: ¿sigue viva?, ¿qué pide?

(Sección creada para esta adaptación.)

Estado de las APIs sugeridas (comprobado el 28 de septiembre de 2026)

La lista del curso es de 2020. Las APIs mueren, cambian o empiezan a cobrar. Resultado de consultar sus páginas:

API sugerida Resultado de la consulta Comentario
Metro de Nueva York (herokuapp) 404 Muchas API gratuitas alojadas en Heroku desaparecieron cuando Heroku eliminó su plan gratuito (2022)
Elefantes (herokuapp) 404 Igual que la anterior
Harry Potter (potterapi.com) Sin respuesta No disponible
ESPN (página de desarrolladores) 404 No disponible en esa dirección
Food Facts (documentación en Postman) 404 No disponible en esa dirección
Owlbot (diccionario) 404 en la página enlazada La API pide un token; verifica su estado actual
Código de barras (Cloudmersive), marketstack, Artsy, Perfect Tense, Freesound, The One API Las páginas responden (200) Que la página cargue no garantiza que la API siga gratuita o funcionando: lee su documentación actual; varias piden registro y clave
Open Brewery DB Página y API responden (200) Sin clave

Criterios para elegir

Criterio Pregunta
¿Está viva? ¿Responde una petición de prueba hoy? ¿Su documentación tiene fecha reciente?
¿Qué pide? ¿Sin clave, con clave gratuita, con tarjeta de crédito?
¿Qué límites tiene? ¿Cuántas peticiones por minuto o por día?
¿Qué permiten sus términos? ¿Puedo mostrar los datos en un sitio público? ¿Debo citar la fuente?
¿Edad mínima? Algunas exigen ser mayor de edad para registrarse (para el aula: la cuenta la gestiona el docente)
¿Me interesa de verdad? Un proyecto sobre algo que te gusta se termina con más entusiasmo

APIs sin clave que funcionaban en la fecha de verificación (útiles para el aula): Open-Meteo (clima, usada en este tutorial), REST Countries (países), Open Brewery DB y los conjuntos de datos del portal colombiano datos.gov.co (API Socrata). Otras con clave gratuita: TMDB (películas), NASA APIs.


3. Analizar el problema antes de programar

(Sección creada para esta adaptación.)

🔍 Antes de programar

  1. Explora la API sin programar: haz 3 peticiones desde el navegador (si son GET) o con Postman (Día 66), y guarda las respuestas JSON.
  2. Dibuja el JSON: ¿qué claves hay?, ¿dónde está el dato que te interesa? (En Open-Meteo, la temperatura está en respuesta["current"]["temperature_2m"].)
  3. ¿Qué aporta tu sitio? Mostrar el JSON "bonito" no basta. Ideas: comparar (clima de 3 ciudades), filtrar (cervecerías de un estado), transformar (convertir unidades), combinar dos APIs, recordar (favoritos).
  4. Páginas y rutas: escribe la lista (/, /clima/<ciudad>, …).
  5. ¿Qué pasa si la API falla? Tu sitio no debe mostrar una página de error de Python.

4. 📖 La pieza central: una función que consulta la API

(Sección creada para esta adaptación. Verificado contra la API real y con simulaciones.)

Python
import time
import requests

URL = "https://api.open-meteo.com/v1/forecast"
CACHE = {}
DURACION_CACHE = 600   # segundos

def obtener_clima(latitud, longitud):
    clave = (latitud, longitud)
    guardado = CACHE.get(clave)
    if guardado and time.time() - guardado[0] < DURACION_CACHE:
        return guardado[1]
    parametros = {"latitude": latitud, "longitude": longitud,
                  "current": "temperature_2m,relative_humidity_2m",
                  "timezone": "America/Bogota"}
    respuesta = requests.get(URL, params=parametros, timeout=10)
    respuesta.raise_for_status()
    datos = respuesta.json()["current"]
    CACHE[clave] = (time.time(), datos)
    return datos

Qué hace cada parte:

  • params= arma la URL con los parámetros (Día 33), sin concatenar texto a mano.
  • timeout=10: sin él, si la API no responde, tu sitio queda "colgado".
  • raise_for_status(): convierte respuestas 4xx/5xx en una excepción. (Verificado: una latitud de 999 devuelve 400 con el motivo en JSON.)
  • Caché: guarda la respuesta con la hora. Si alguien consulta la misma ciudad en los próximos 10 minutos, se responde sin llamar a la API. Verificado: 0,85 s la primera vez, menos de 1 ms la segunda. Ventajas: rapidez y respeto de los límites de la API. (Esta caché vive en la memoria del proceso: se pierde al reiniciar; es suficiente para el proyecto.)
  • La función devuelve datos; no sabe nada de HTML. La ruta de Flask decide cómo mostrarlos.

La ruta, con manejo de errores:

Python
@app.route("/clima/<ciudad>")
def clima(ciudad):
    if ciudad not in CIUDADES:
        abort(404)
    nombre, lat, lon = CIUDADES[ciudad]
    try:
        datos = obtener_clima(lat, lon)
    except requests.RequestException:
        return render_template("error.html",
                               mensaje="No pudimos consultar el clima. Intenta más tarde."), 503
    return render_template("clima.html", nombre=nombre, datos=datos)
  • requests.RequestException es la "madre" de los errores de requests (sin conexión, tiempo agotado, error HTTP): una sola cláusula los atrapa.
  • 503 (Service Unavailable) comunica que el problema es temporal.

Si tu API necesita clave: léela con os.environ["MI_CLAVE"] (Día 35), nunca en el código. Y no la pongas en JavaScript del navegador: cualquiera podría verla con "Inspeccionar". Por eso conviene que sea tu servidor Flask quien llame a la API.

⚠️ Errores frecuentes

Error Qué ocurre Solución
KeyError: 'current' La API devolvió un error en JSON, no los datos raise_for_status() y revisar la respuesta antes de acceder a claves
JSONDecodeError La respuesta no era JSON (página de error HTML) Revisa respuesta.status_code y respuesta.headers["Content-Type"]
Sitio lento Cada visita llama a la API Caché
429 Too Many Requests Superaste el límite Caché, menos peticiones, respetar la cabecera Retry-After
La clave aparece en GitHub Cualquiera puede usar tu cuenta Variables de entorno y .gitignore; si ya se publicó, revócala
Error 500 cuando la API falla Se ve una traza de Python try/except requests.RequestException

5. 📖 Probar sin depender de internet: simulaciones

(Sección creada para esta adaptación. Verificado.)

¿Cómo pruebas que tu sitio muestra el mensaje de error si la API falla, si la API funciona bien hoy? Con una simulación (mock): durante la prueba, reemplazas requests.get por algo que falla a propósito.

Python
from unittest.mock import patch
import requests
from app import app, CACHE

cliente = app.test_client()
CACHE.clear()

with patch("app.requests.get", side_effect=requests.ConnectionError("sin red")):
    respuesta = cliente.get("/clima/medellin")
    assert respuesta.status_code == 503
  • patch("app.requests.get", ...) reemplaza requests.get solo dentro del with y solo en el módulo app.
  • side_effect=... hace que, al llamarlo, lance esa excepción.
  • También puedes simular una respuesta correcta con datos fijos (verificado: con 30,5 °C simulados, la página de Cartagena mostró 30,5 °C). Así tus pruebas no dependen del clima real ni de la conexión.
  • CACHE.clear() evita que una respuesta guardada en otra prueba "esconda" el fallo.

6. 🚀 Proyecto del día: construcción por versiones

(Desarrolla la tarea dia96-tarea15.md. Las versiones son una propuesta de esta adaptación.)

Problema

Construir un sitio web o servicio basado en una API pública que te interese.

Contexto

Elige un tema cercano: el clima para la salida pedagógica, películas para el cineclub, países para la clase de sociales, datos abiertos de tu municipio, deportes, música.

Objetivo

Un sitio Flask que aporte valor sobre los datos de la API (comparar, filtrar, combinar o recordar), robusto ante fallos.

Requisitos funcionales

  1. Al menos 2 páginas que usen datos de la API.
  2. Algún tipo de interacción del usuario (búsqueda, filtro, elección).
  3. Mensajes amables si la API falla o no hay resultados.
  4. Cita de la fuente de los datos en el sitio.

Restricciones técnicas

  • Consultas a la API en funciones separadas de las rutas, con timeout.
  • Caché para respetar los límites.
  • Claves (si las hay) en variables de entorno.
  • Al menos una prueba con simulación de fallo.

Planificación

  1. Elegir y comprobar la API (sección 2). 2. Entradas: lo que el usuario elige. 3. Procesos: consultar, transformar. 4. Salidas: páginas. 5. Dividir en funciones y rutas. 6. Bocetar. 7. Implementar. 8. Probar. 9. Corregir. 10. Mejorar.

Construcción paso a paso

  • Versión 1 — Explorar: un script que imprima los datos que te interesan.
  • Versión 2 — Función de consulta: con timeout, raise_for_status y su prueba con simulación.
  • Versión 3 — Primera página en Flask.
  • Versión 4 — Interacción: formulario de búsqueda o filtros (GET con request.args).
  • Versión 5 — Robustez: errores, "sin resultados", caché.
  • Versión final: diseño, cita de la fuente, README.md, diario y, si se autoriza, publicación (Día 71).

Pruebas

# Caso Resultado esperado
1 Consulta válida 200 con los datos
2 Parámetro desconocido (ciudad, id) 404 o mensaje "no encontrado"
3 API caída (simulación) 503 con mensaje amable
4 Respuesta simulada con datos fijos La página muestra esos datos
5 Dos consultas iguales seguidas La segunda no llama a la API (compruébalo con patch y assert_called_once)
6 Búsqueda sin resultados Mensaje "No hay resultados"
7 Revisar el repositorio No hay claves

🧪 Prueba y depuración

Python
@app.route("/buscar")
def buscar():
    texto = request.args["q"]
    datos = requests.get(f"https://api.ejemplo.com/buscar?q={texto}&key=abc123").json()
    return render_template("resultados.html", resultados=datos["results"])

Encuentra cinco problemas: request.args["q"] falla si no hay búsqueda (usa .get); la URL se arma a mano (caracteres como espacios o & la rompen: usa params=); la clave está en el código; no hay timeout ni manejo de errores; y se accede a datos["results"] sin comprobar que la respuesta fue exitosa.

⭐ Extensión del proyecto

  • 🟢 Reto 1 — Aplicación: página de "favoritos" guardados en la sesión de Flask (session).
  • 🟡 Reto 2 — Ampliación: un gráfico con los datos (por ejemplo, la temperatura de las próximas 24 horas con plotly, Día 76).
  • 🟠 Reto 3 — Desafío: combinar dos APIs (por ejemplo, REST Countries para la capital y Open-Meteo para su clima).
  • 🔴 Reto 4 — Pensamiento computacional: tu sitio se vuelve popular y la API permite solo 100 peticiones por hora. Diseña una estrategia (caché más larga, precargar datos, cola de peticiones) y estima cuántos usuarios podrías atender.

7. 🌟 Proyecto extra: tablero de datos abiertos del municipio

(Proyecto creado para esta adaptación; no existe en el curso original.)

Situación

El consejo estudiantil quiere un sitio que muestre datos públicos de su municipio o departamento: calidad del aire, matrícula escolar, presupuesto, puntos de reciclaje… publicados en datos.gov.co.

Requisitos

  1. Encontrar al menos 2 conjuntos de datos del municipio o departamento en datos.gov.co y consumirlos con su API (parámetros de consulta de Socrata, como $limit y $where; consulta su documentación).
  2. Una página por conjunto de datos con una tabla resumida y un gráfico.
  3. Fuente, fecha de actualización del conjunto y fecha de consulta visibles.

Decisiones que debes tomar (y justificar)

  • Si el conjunto de datos tiene 100 000 filas, ¿cuántas pides y cómo resumes?
  • ¿Cómo explicas los datos a estudiantes que no son expertos?

Casos de prueba

  • Con la API simulada caída, el sitio muestra la última copia guardada (o un mensaje claro).
  • Un filtro sin resultados muestra un mensaje, no una tabla vacía.

8. 🏗️ Mi Portafolio Profesional — Etapa 15

(Proyecto integrador creado para esta adaptación; etapas anteriores en los Días 82 a 95.)

Etapa 15 (hoy):

  1. Repositorio con api.py (funciones de consulta), app.py, pruebas con simulaciones, .env.example y requirements.txt.
  2. README.md con: la API elegida y por qué (criterios de la sección 2), qué aporta tu sitio, límites de la API y cómo los respetas.
  3. DIARIO.md del Día 96 y ficha en proyectos.json.
  4. Integración: si tu sitio de portafolio usa Flask, agrega una pequeña sección con datos en vivo de una API (por ejemplo, tus repositorios desde la API pública de GitHub).

9. 🧩 Comprueba lo aprendido

Conceptuales

  1. ¿Por qué una API sugerida en 2020 puede no funcionar hoy?
  2. ¿Qué ventajas tiene una caché al consumir una API?
  3. ¿Por qué la clave de una API no debe ir en el JavaScript del navegador?
  4. ¿Qué es una simulación (mock) y para qué sirve en las pruebas?

Análisis de código

Python
respuesta = requests.get(URL, params={"q": "café con leche"}, timeout=5)
print(respuesta.url)

¿Cómo se verá la URL impresa? ¿Qué hizo requests con los espacios y la tilde?

Predicción

Con DURACION_CACHE = 600, consultas Bogotá a las 10:00:00, 10:05:00 y 10:11:00. ¿Cuántas veces se llama a la API?

Depuración

"Mi sitio funciona en mi casa, pero en el colegio muestra un error 500 después de 30 segundos." ¿Qué le falta al código?

Modificación

Cambia obtener_clima para pedir también la velocidad del viento (busca el nombre del parámetro en la documentación de Open-Meteo).

Creación

Escribe consultar_con_reintentos(url, params, intentos=3) que reintente si hay error de conexión, esperando 1, 2 y 4 segundos entre intentos.


10. 🤖 Programar con apoyo de IA

Ciclo de trabajo: formula el problema → inténtalo tú primero → consulta a la IA sobre un punto concreto → lee y entiende → prueba → compara con la documentación oficial → modifica → explica con tus palabras.

Actividad 1 — ¿La API existe? Pide a una IA "5 APIs gratuitas sobre X". Comprueba cada una con una petición real. Anota cuántas funcionaban, cuáles pedían clave y cuáles no existían. (Las IA suelen recomendar APIs que ya cerraron, como las de la lista de 2020.)

Actividad 2 — Parámetros reales. Pide a una IA el código para consultar tu API. Compara cada parámetro con la documentación oficial: ¿existen todos? ¿se llaman igual?

Actividad 3 — Pruebas. Pide a una IA que te explique unittest.mock.patch y escribe tú una prueba nueva (por ejemplo, la prueba 5).

Declara el uso: sección "Uso de IA" en el README.md.


11. 🛠️ Observaciones sobre el material original

Archivo Observación Tratamiento
dia96-tarea15.md Varias APIs sugeridas ya no responden (404 o sin respuesta), comprobado el 28/09/2026 Tabla de la sección 2 y alternativas vigentes
dia96-tarea15.md No menciona límites, caché ni errores Secciones 4 y 5
dia96-tarea15.md Recuerda proyectos anteriores (alerta de lluvia, ISS, Bitcoin) Se usa una API del clima (Open-Meteo) como ejemplo, en continuidad con la alerta de lluvia
dia96-tarea15.md Algunas APIs requieren registro Advertencia sobre edad mínima y cuentas gestionadas por el docente

12. 📋 Matriz de trazabilidad

Contenido original Archivo de origen Concepto desarrollado Ejercicio original Reto adicional Proyecto relacionado
Sitio web que usa datos de una API pública dia96-tarea15.md Secciones 3 y 4 Sitio sobre una API Retos 1 a 3 Secciones 6 y 8
Antecedentes: lluvia, ISS, Bitcoin dia96-tarea15.md Sección 4 (ejemplo del clima) — — —
Elegir una API interesante dia96-tarea15.md Sección 2 Sitio sobre una API Reto 4 Sección 6
Lista de 13 APIs de ejemplo dia96-tarea15.md Sección 2 (estado actual) — — Sección 7
Momento de reflexión dia96-tarea15.md Sección 6 (versión final) Pregunta de reflexión — Sección 8
(No existe en el curso original) — Caché, errores, simulaciones — Tablero de datos abiertos Sección 7

13. 📦 Evidencias de aprendizaje del Día 96

  • La comprobación escrita de la API elegida (criterios de la sección 2).
  • El sitio funcionando (capturas) y la fuente citada en él.
  • Pruebas con simulaciones superadas (fallo de red y datos fijos).
  • DIARIO.md del Día 96 y ficha en el portafolio.
  • Cuaderno de errores: API caída, KeyError por respuesta de error, JSONDecodeError, falta de timeout, 429 por exceso de peticiones, clave publicada, URL armada a mano.