Consumir una API REST con Python permite integrar datos de clima, pagos, inventario, mapas, redes sociales o sistemas internos. La biblioteca Requests ofrece una interfaz clara para enviar solicitudes HTTP, leer JSON, configurar cabeceras y controlar errores. Sin embargo, un cliente fiable necesita más que una llamada rápida: debe usar tiempos de espera, validar respuestas, respetar límites y proteger credenciales.
Antes de comenzar, consulta las guías sobre Requests en Python, JSON, excepciones, logging y entornos virtuales. Como fuentes externas, revisa el Quickstart oficial de Requests y el estándar RFC 9110 sobre semántica HTTP.
Instalar Requests
python -m pip install requestsInstálalo en un entorno virtual y fija versiones en un archivo de dependencias para que el proyecto sea reproducible.
Primera solicitud GET
import requests
respuesta = requests.get(
"https://api.example.com/productos",
timeout=10,
)
respuesta.raise_for_status()
datos = respuesta.json()
print(datos)timeout evita que el programa espere indefinidamente. raise_for_status() convierte estados de error en excepciones.
Parámetros de consulta
parametros = {
"categoria": "libros",
"pagina": 1,
"limite": 50,
}
respuesta = requests.get(
"https://api.example.com/productos",
params=parametros,
timeout=10,
)Requests codifica correctamente los parámetros. No construyas la URL concatenando texto, especialmente cuando contiene espacios o caracteres especiales.
Leer JSON con validación
Una respuesta exitosa no garantiza que el cuerpo tenga la estructura esperada.
datos = respuesta.json()
if not isinstance(datos, dict):
raise ValueError("La respuesta no es un objeto JSON")
productos = datos.get("items")
if not isinstance(productos, list):
raise ValueError("Falta la lista items")Valida tipos y campos antes de utilizarlos. Una API puede cambiar o devolver un mensaje de error en formato diferente.
Enviar datos con POST
nuevo = {
"nombre": "Teclado",
"precio": 99.90,
}
respuesta = requests.post(
"https://api.example.com/productos",
json=nuevo,
timeout=10,
)
respuesta.raise_for_status()El argumento json serializa el objeto y configura la cabecera adecuada. No lo confundas con data, usado para formularios u otros cuerpos.
Autenticación con token
import os
token = os.environ["API_TOKEN"]
cabeceras = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
}
respuesta = requests.get(
"https://api.example.com/perfil",
headers=cabeceras,
timeout=10,
)No escribas claves en el código ni las incluyas en logs. Usa variables de entorno o un gestor de secretos.
Comprender los códigos de estado
Los códigos 2xx indican éxito, 4xx suelen señalar un problema en la solicitud y 5xx un fallo del servidor. No todos los éxitos devuelven contenido: una respuesta 204, por ejemplo, puede tener cuerpo vacío. Trata cada endpoint según su contrato.
Tiempos de espera separados
respuesta = requests.get(
url,
timeout=(3.05, 20),
)La tupla separa el tiempo para conectar del tiempo de lectura. El valor correcto depende del servicio y de la experiencia que deseas ofrecer.
Manejo de excepciones
from requests import RequestException, Timeout, HTTPError
try:
respuesta = requests.get(url, timeout=10)
respuesta.raise_for_status()
datos = respuesta.json()
except Timeout:
print("La API tardó demasiado")
except HTTPError as error:
print(f"Error HTTP: {error.response.status_code}")
except ValueError:
print("La respuesta no contiene JSON válido")
except RequestException as error:
print(f"Error de red: {error}")No captures todas las excepciones de forma silenciosa. Diferencia fallos temporales de errores permanentes.
Usar una Session
sesion = requests.Session()
sesion.headers.update({
"Accept": "application/json",
"User-Agent": "MiAplicacion/1.0",
})
respuesta = sesion.get(url, timeout=10)Una sesión reutiliza conexiones y cabeceras. Ciérrala con un bloque with o llamando a close().
Paginación
def obtener_todos(base_url):
pagina = 1
while True:
respuesta = requests.get(
base_url,
params={"page": pagina},
timeout=10,
)
respuesta.raise_for_status()
datos = respuesta.json()
yield from datos["items"]
if not datos.get("next"):
break
pagina += 1No asumas que todos los resultados caben en una respuesta. Respeta el mecanismo de paginación documentado.
Reintentos
Reintenta únicamente operaciones seguras o idempotentes y errores temporales. Usa espera exponencial, un máximo pequeño y considera la cabecera Retry-After. Repetir un POST de pago sin una clave de idempotencia puede duplicar una operación.
Límites de uso
Muchas APIs aplican cuotas por minuto o día. Lee las cabeceras de límite, almacena en caché respuestas apropiadas y agrupa solicitudes cuando el servicio lo permita. Un bucle sin control puede bloquear tu cuenta.
Descargar archivos en streaming
with requests.get(url, stream=True, timeout=30) as respuesta:
respuesta.raise_for_status()
with open("archivo.zip", "wb") as salida:
for bloque in respuesta.iter_content(1024 * 1024):
if bloque:
salida.write(bloque)El streaming evita cargar el archivo completo en memoria. Comprueba tamaño, tipo y hash cuando la integridad sea importante.
Pruebas
No dependas de la API real en todas las pruebas. Simula respuestas de éxito, error, tiempo de espera, JSON inválido y paginación. Prueba también que las credenciales no aparezcan en mensajes de error.
Errores frecuentes
Los fallos habituales son omitir timeout, asumir que todo 200 contiene el JSON esperado, guardar tokens en Git, reintentar sin límite, ignorar paginación y registrar cuerpos sensibles. Otro error es desactivar la verificación TLS para solucionar certificados; corrige la configuración en lugar de eliminar la protección.
Conclusión
Consumir una API REST de forma profesional requiere controlar red, protocolo y datos. Usa parámetros estructurados, valida JSON, protege tokens, configura tiempos de espera, maneja códigos HTTP y limita reintentos. Con sesiones, paginación, streaming y pruebas, el cliente seguirá siendo fiable aunque el servicio tenga latencia o respuestas inesperadas.





