Skip to content

📙 Clase 23 — requests: consumir APIs

Fase 4 · Archivos y el mundo exterior ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Hacer peticiones HTTP con requests y leer respuestas JSON.
  • Códigos de estado (200, 404…), timeout y manejo de errores de red.
  • Traer datos de internet a tu app de escritorio (clima, tipo de cambio, lo que sea).

📖 PARTE TEÓRICA

🌐 1. ¿Qué es una API?

Una API web es un servicio al que le pides datos por una URL y te responde (casi siempre) JSON — el formato que ya dominas de la Clase 20.

tu app ──── GET https://api.servicio.com/usuarios/ana ────▶  servidor
tu app ◀─── {"nombre": "Ana", "repos": 8}  (JSON) ─────────  servidor

📦 2. Instalar y usar

requests no viene con Python (se instala en tu venv — ya está en el del curso):

bash
pip install requests
python
import requests

r = requests.get("https://api.github.com/users/octocat", timeout=10)

print(r.status_code)     # 200  ← todo OK
datos = r.json()         # el JSON de la respuesta, YA convertido a dict
print(datos["login"])           # octocat
print(datos["public_repos"])    # 8

📌 Verificado en tu .venv con requests 2.34.2 contra la API real de GitHub: respuesta 200, login='octocat', public_repos=8.

🚦 3. Códigos de estado

CódigoSignificaQué haces
200OKusar r.json()
404no existeavisar "no encontrado"
401 / 403sin permisorevisar API key
500+el servidor fallóreintentar más tarde
python
r = requests.get("https://api.github.com/users/este-usuario-no-existe-xyz-999",
                 timeout=10)
print(r.status_code)     # 404  ← la petición "funcionó", pero no hay tal usuario

⚠️ Un 404 no lanza excepción: la petición HTTP se completó. Comprueba r.status_code (o llama r.raise_for_status() para convertir errores HTTP en excepción).

🛡️ 4. Lo que sí lanza excepciones: la red

Sin internet, DNS caído, servidor lento… eso sí explota. El patrón robusto completo:

python
def consultar_usuario(nombre):
    try:
        r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
    except requests.ConnectionError:
        return None, "Sin conexión a internet"
    except requests.Timeout:
        return None, "El servidor tardó demasiado"

    if r.status_code == 404:
        return None, "Usuario no encontrado"
    if r.status_code != 200:
        return None, f"Error del servidor ({r.status_code})"
    return r.json(), ""

⚠️ Pon siempre timeout=: sin él, una petición puede colgarse para siempre — y en una GUI eso significa ventana congelada.

🧪 Tip de entrevista: "¿Qué métodos HTTP conoces?" → GET (leer), POST (crear), PUT/PATCH (modificar), DELETE (borrar). Con requests: requests.post(url, json={...}) etc.


🖥️ EN TU APP DE ESCRITORIO

Datos vivos en tu GUI — con la lógica separada (Clase 08) y el resultado en un label:

python
class App(ctk.CTk):
    def _buscar(self):
        usuario = self.entry.get().strip()
        datos, error = consultar_usuario(usuario)     # la función de arriba
        if error:
            self.resultado.configure(text=f"⚠️ {error}", text_color="orange")
            return
        self.resultado.configure(
            text=f"{datos['login']}{datos['public_repos']} repos públicos",
            text_color="white",
        )
Idea de appAPI pública
Widget de climaOpen-Meteo (sin API key)
Conversor de monedasexchangerate / frankfurter
Buscador de perfiles GitHubapi.github.com (la de esta clase)
Precio de criptoCoinGecko

⚠️ El gotcha grande: una petición lenta dentro de un callback congela la ventana (el mainloop queda esperando — es el problema de la Clase 03). Con timeout=10 sobrevives por ahora; la solución profesional es threading, y es exactamente la Clase 33 de la Fase 6.


🗄️ CON BASE DE DATOS (caso de uso)

Patrón API + caché local: consulta una vez, guarda en tu BD/JSON, y sirve desde ahí (la app funciona incluso sin internet):

python
def obtener_tipo_cambio(con):
    fila = con.execute("SELECT valor, fecha FROM cache WHERE clave='usd'").fetchone()
    if fila and es_de_hoy(fila[1]):
        return fila[0]                     # ① servir del caché
    datos, error = consultar_api()         # ② si no, pedir a la API
    if datos:
        con.execute("INSERT OR REPLACE INTO cache VALUES ('usd', ?, ?)",
                    (datos["valor"], hoy()))   # ③ y guardar para la próxima
    return datos["valor"] if datos else None

🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Tu perfil de GitHub

Consulta https://api.github.com/users/TU_USUARIO e imprime nombre y repos públicos.

Ver solución
python
import requests

r = requests.get("https://api.github.com/users/octocat", timeout=10)
if r.status_code == 200:
    d = r.json()
    print(d["login"], "-", d["public_repos"], "repos")   # octocat - 8 repos

Ejercicio 2 — Manejo del 404

Escribe existe_usuario(nombre) que devuelva True/False según el status code.

Ver solución
python
import requests

def existe_usuario(nombre):
    r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
    return r.status_code == 200

print(existe_usuario("octocat"))                          # True
print(existe_usuario("este-usuario-no-existe-xyz-999"))   # False

Ejercicio 3 — A prueba de balas

Mejora existe_usuario para que devuelva None si no hay internet (atrapa requests.ConnectionError).

Ver solución
python
import requests

def existe_usuario(nombre):
    try:
        r = requests.get(f"https://api.github.com/users/{nombre}", timeout=10)
    except requests.ConnectionError:
        return None            # sin red: "no sé"
    return r.status_code == 200

❓ Preguntas y respuestas (autoevaluación)

1. ¿Qué devuelve r.json()?

El cuerpo JSON de la respuesta convertido a estructuras Python (dict/list) — como json.loads.

2. ¿Un 404 lanza excepción en requests?

No: la petición HTTP se completó. Se revisa r.status_code (o r.raise_for_status()).

3. ¿Por qué timeout= es obligatorio en la práctica?

Sin él la petición puede colgarse indefinidamente — en una GUI, ventana congelada.

4. ¿Qué excepciones de red conviene atrapar?

requests.ConnectionError (sin red/DNS) y requests.Timeout (tardó de más).


📎 Apuntes relacionados

  • El JSON que devuelven las APIs → Clase 20
  • Separar la consulta de la GUI → Clase 08 · return
  • La solución al congelamiento → Threading (Fase 6)

➡️ Siguiente

¡Fase 4 lista! 🎉 Fase 5: Clase 24 · lambda, map y filter.