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/bogotarespondió 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
- La tarea: tu servicio sobre una API que te interese
- 📖 Elegir la API: ¿sigue viva?, ¿qué pide?
- Analizar el problema antes de programar
- 📖 La pieza central: una función que consulta la API
- 📖 Probar sin depender de internet: simulaciones
- 🚀 Proyecto del día: construcción por versiones
- 🌟 Proyecto extra: tablero de datos abiertos del municipio
- 🏗️ Mi Portafolio Profesional — Etapa 15
- 🧩 Comprueba lo aprendido
- 🤖 Programar con apoyo de IA
- 🛠️ Observaciones sobre el material original
- 📋 Matriz de trazabilidad
- 📦 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
- 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.
- 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"].) - ¿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).
- Páginas y rutas: escribe la lista (
/,/clima/<ciudad>, …). - ¿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.)
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 datosQué 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:
@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.RequestExceptiones la "madre" de los errores derequests(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.
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 == 503patch("app.requests.get", ...)reemplazarequests.getsolo dentro delwithy solo en el móduloapp.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
- Al menos 2 páginas que usen datos de la API.
- Algún tipo de interacción del usuario (búsqueda, filtro, elección).
- Mensajes amables si la API falla o no hay resultados.
- 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
- 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_statusy 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
@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
- 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
$limity$where; consulta su documentación). - Una página por conjunto de datos con una tabla resumida y un gráfico.
- 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):
- Repositorio con
api.py(funciones de consulta),app.py, pruebas con simulaciones,.env.exampleyrequirements.txt. README.mdcon: 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.DIARIO.mddel Día 96 y ficha enproyectos.json.- 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
- ¿Por qué una API sugerida en 2020 puede no funcionar hoy?
- ¿Qué ventajas tiene una caché al consumir una API?
- ¿Por qué la clave de una API no debe ir en el JavaScript del navegador?
- ¿Qué es una simulación (mock) y para qué sirve en las pruebas?
Análisis de código
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.mddel Día 96 y ficha en el portafolio.- Cuaderno de errores: API caída,
KeyErrorpor respuesta de error,JSONDecodeError, falta detimeout, 429 por exceso de peticiones, clave publicada, URL armada a mano.