(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisboa

Cómo hacer llamadas paralelas a una API en Python

João Barros 07 de July de 2026 4 min de lectura

Cuando un programa necesita llamar a una API decenas o cientos de veces — por ejemplo, para alimentar un dashboard o recoger datos de varios endpoints — hacerlo de una llamada en una se vuelve muy lento. Las llamadas paralelas a una API en Python resuelven este problema: en lugar de esperar a que cada petición termine antes de empezar la siguiente, enviamos varias a la vez y el tiempo total baja drásticamente.

Requisitos previos

  • Python 3.8 o superior instalado.
  • Saber hacer una petición simple con requests o httpx.
  • Una terminal para instalar paquetes con pip.
  • Ganas de experimentar: usaremos la API pública de pruebas jsonplaceholder.typicode.com.

Paso 1: Entender por qué las llamadas secuenciales son lentas

Imagina que necesitas obtener 20 tareas de una API. Con un bucle normal, cada petición solo empieza cuando termina la anterior. Si cada una tarda unos 0,2 segundos, el total ronda los 4 segundos — y crece a medida que añades más peticiones.

import time, httpx

inicio = time.perf_counter()
with httpx.Client() as cliente:
    for i in range(1, 21):
        r = cliente.get(f"https://jsonplaceholder.typicode.com/todos/{i}")
        r.json()
print(f"Sequencial: {time.perf_counter() - inicio:.2f} s")

El problema no es que tu ordenador esté ocupado: casi siempre está esperando la respuesta de la red. Ese tiempo muerto, en el que no ocurre nada, es justo lo que vamos a aprovechar para adelantar otras peticiones.

Paso 2: Instalar httpx

httpx es un cliente HTTP moderno que funciona de forma síncrona (como requests) y también asíncrona, lo que es ideal para peticiones en paralelo. Instálalo con pip:

pip install httpx

Paso 3: Crear una función asíncrona para una llamada

En código asíncrono usamos async def para definir la función y await para esperar la respuesta sin bloquear el resto del programa. Mientras una petición espera a la red, Python queda libre para atender las demás. Esta función obtiene una tarea y devuelve el JSON.

async def obter(cliente, id):
    r = await cliente.get(f"https://jsonplaceholder.typicode.com/todos/{id}")
    return r.json()

Fíjate en que la función recibe el cliente como argumento: así todas las llamadas reutilizan la misma conexión, lo que es más rápido que abrir una nueva cada vez.

Paso 4: Ejecutar varias llamadas en paralelo con asyncio.gather

asyncio.gather recibe varias tareas y las ejecuta a la vez, devolviendo los resultados en el mismo orden en que las pediste. Es el corazón de las llamadas paralelas.

import asyncio, time, httpx

async def obter(cliente, id):
    r = await cliente.get(f"https://jsonplaceholder.typicode.com/todos/{id}")
    return r.json()

async def principal():
    inicio = time.perf_counter()
    async with httpx.AsyncClient() as cliente:
        tarefas = [obter(cliente, i) for i in range(1, 21)]
        resultados = await asyncio.gather(*tarefas)
    print(f"Paralelo: {time.perf_counter() - inicio:.2f} s")
    return resultados

resultados = asyncio.run(principal())

El *tarefas despliega la lista como argumentos individuales. Al ejecutarlo, el tiempo total suele pasar de varios segundos a pocas décimas.

Paso 5: Limitar la concurrencia con un Semaphore

Enviar 500 peticiones a la vez puede sobrecargar la API y provocar el error 429 Too Many Requests. Un Semaphore limita cuántas llamadas ocurren al mismo tiempo, para que seas un buen vecino y evites bloqueos.

import asyncio, httpx

async def obter(cliente, limite, id):
    async with limite:
        r = await cliente.get(f"https://jsonplaceholder.typicode.com/todos/{id}")
        return r.json()

async def principal():
    limite = asyncio.Semaphore(5)  # no máximo 5 ao mesmo tempo
    async with httpx.AsyncClient(timeout=10) as cliente:
        tarefas = [obter(cliente, limite, i) for i in range(1, 21)]
        return await asyncio.gather(*tarefas)

resultados = asyncio.run(principal())
print(f"Recebidas {len(resultados)} respostas")

Con Semaphore(5), solo cinco peticiones se ejecutan a la vez; las demás esperan su turno de forma automática.

Verificar el resultado

Para confirmar que todo salió bien, compara el tiempo impreso en la versión secuencial (Paso 1) con el de la versión paralela (Paso 4): debería ser bastante menor. Comprueba también que len(resultados) es igual al número de peticiones que hiciste (20) y que ningún resultado viene vacío. Si aparecen errores de red, aumenta el timeout o reduce el valor del Semaphore.

Conclusión

Has aprendido a convertir llamadas lentas, una a una, en peticiones paralelas que aprovechan el tiempo de espera de la red, y a controlar la carga con un Semaphore. El siguiente paso es añadir manejo de errores — por ejemplo, reintentar peticiones fallidas — y recoger resultados a medida que llegan con asyncio.as_completed. ¿Has pensado cuántas peticiones por segundo aguanta tu API antes de devolver 429?