Elegir entre Anthropic() y AsyncAnthropic() en código de producción
El paquete anthropic incluye dos clases de cliente con una superficie de métodos idéntica: Anthropic() y AsyncAnthropic().
La elección correcta depende enteramente de cómo tu programa maneja la concurrencia, no de cuál es "mejor".
Resumen
Anthropic() es un cliente bloqueante: llamar a client.messages.create(...) pausa el hilo actual hasta que llega una respuesta.
AsyncAnthropic() es un cliente no bloqueante construido sobre asyncio: llamar a await client.messages.create(...) cede el control al bucle de eventos mientras la solicitud está en curso.
Ambos exponen los mismos parámetros, los mismos tipos de respuesta y las mismas clases de error, por lo que cambiar entre ellos más tarde es principalmente mecánico.
La decisión importante es si tu aplicación ya se ejecuta en un bucle de eventos (un framework web asíncrono, una cola de trabajos asíncrona) o se ejecuta como un script lineal, porque esa decisión se tomó efectivamente por ti antes de que eligieras un cliente Claude.
Equivocarse en esto no rompe la corrección, sino que cuesta rendimiento: un cliente síncrono llamado desde dentro de un framework asíncrono bloquea todo el bucle de eventos, y un cliente asíncrono utilizado en un script de un solo hilo añade complejidad sin beneficio.
Receta
import anthropic
# Script, notebook, herramienta CLI, trabajo por lotes
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hola"}],
)
# FastAPI, aiohttp, o cualquier servicio basado en asyncio
async_client = anthropic.AsyncAnthropic()
response = await async_client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hola"}],
)Cuándo usar Anthropic():
- Scripts únicos, notebooks y herramientas CLI que realizan solicitudes secuencialmente.
- Trabajos por lotes o ETL que procesan elementos uno a la vez y no necesitan superposición entre solicitudes.
- Cualquier base de código construida sobre un framework web síncrono (Flask o Django sin vistas asíncronas).
Cuándo usar AsyncAnthropic():
- Servicios web construidos sobre un framework asíncrono, más comúnmente FastAPI, que ya se ejecutan en un bucle de eventos.
- Cargas de trabajo que distribuyen muchas llamadas a Claude concurrentemente (resumir 50 documentos a la vez, por ejemplo).
- Cualquier código que necesite llamar a Claude mientras también espera otra E/S (una consulta a la base de datos, otra llamada a la API) sin bloquear ninguna de ellas.
Ejemplo de trabajo
Este ejemplo muestra el mismo endpoint de resumen escrito de dos maneras: como un script síncrono y como una ruta de FastAPI usando el cliente asíncrono, para que el contraste sea directo.
# sync_script.py - un trabajo por lotes independiente
import anthropic
client = anthropic.Anthropic()
def summarize(text: str) -> str:
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
system="Resume la entrada en una oración.",
messages=[{"role": "user", "content": text}],
)
return message.content[0].text
if __name__ == "__main__":
documents = ["Primer texto del documento...", "Segundo texto del documento..."]
for doc in documents:
print(summarize(doc))# fastapi_service.py - un servicio web asíncrono
from fastapi import FastAPI
from pydantic import BaseModel
import anthropic
app = FastAPI()
client = anthropic.AsyncAnthropic()
class SummarizeRequest(BaseModel):
text: str
@app.post("/summarize")
async def summarize(req: SummarizeRequest):
message = await client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
system="Resume la entrada en una oración.",
messages=[{"role": "user", "content": req.text}],
)
return {"summary": message.content[0].text}Lo que esto demuestra:
- Ambas versiones llaman al mismo método (
messages.create) con los mismos parámetros; soloasync/awaity la clase de cliente difieren. - La ruta de FastAPI
awaits la llamada a Claude, por lo que el bucle de eventos puede servir otras solicitudes entrantes mientras esta espera en la red. - Ambos clientes se construyen una vez en el ámbito del módulo y se reutilizan en las llamadas, no se recrean por solicitud.
- El script por lotes no tiene solicitudes concurrentes que superponer, por lo que el cliente síncrono no añade ningún coste allí.
Inmersión profunda
Cómo funciona
- El bucle de eventos
asynciode Python ejecuta una corrutina a la vez, pero cambia a otra cada vez que la corrutina en ejecución encuentra unawaiten E/S, como una llamada de red. AsyncAnthropic()construye sus llamadas HTTP sobre el transporte asíncrono dehttpx, por lo que una solicitud de Claude pendiente cede el control al bucle en lugar de aparcar un hilo.Anthropic()utiliza el transporte síncrono dehttpx; una llamada bloquea el hilo que llama hasta que llega una cabecera de respuesta (o, al hacer streaming, hasta que llega el primer fragmento).- Un cliente síncrono llamado desde dentro de código
async deftodavía funciona, pero bloquea todo el bucle de eventos durante la duración de la llamada, paralizando todas las demás corrutinas programadas en ese bucle, incluidas las solicitudes no relacionadas que son servidas por el mismo proceso FastAPI. - Construir un cliente nuevo por solicitud (síncrono o asíncrono) descarta la agrupación de conexiones y fuerza un nuevo apretón de manos TLS más a menudo de lo necesario; ambas clases de cliente están destinadas a ser construidas una vez y reutilizadas.
Síncrono vs Asíncrono de un vistazo
| Preocupación | Anthropic() | AsyncAnthropic() |
|---|---|---|
| Estilo de llamada | client.messages.create(...) | await client.messages.create(...) |
| Bloquea el hilo que llama mientras espera | Sí | No - cede el control al bucle de eventos |
| Ajuste natural | Scripts, CLIs, trabajos por lotes, frameworks síncronos | FastAPI, aiohttp, cualquier servicio basado en asyncio |
| Modelo de concurrencia | Una solicitud a la vez por hilo (usa un pool de hilos para más) | Muchas solicitudes en curso en un bucle de eventos |
| Gestor de contexto de streaming | with client.messages.stream(...) as stream: | async with client.messages.stream(...) as stream: |
Notas de Python
# Ejecutar muchos resúmenes concurrentemente con el cliente asíncrono
import asyncio
import anthropic
client = anthropic.AsyncAnthropic()
async def summarize(text: str) -> str:
message = await client.messages.create(
model="claude-sonnet-5",
max_tokens=300,
messages=[{"role": "user", "content": text}],
)
return message.content[0].text
async def main():
documents = ["Doc A...", "Doc B...", "Doc C..."]
results = await asyncio.gather(*(summarize(doc) for doc in documents))
print(results)
asyncio.run(main())asyncio.gather es lo que realmente te da el beneficio de concurrencia del cliente asíncrono: lanza todas las solicitudes antes de esperar ninguna de ellas, por lo que se ejecutan superpuestas en lugar de una tras otra. Usar AsyncAnthropic() pero seguir haciendo await de cada llamada secuencialmente en un bucle no te da ninguna ganancia de rendimiento y solo la complejidad añadida.
Trampas
- Llamar al cliente síncrono dentro de una ruta asíncrona de FastAPI.
client.messages.create(...)(sinawait) dentro deasync defbloquea todo el bucle de eventos durante la duración de la llamada, paralizando todas las demás solicitudes que el proceso está manejando. Solución: usaAsyncAnthropic()dentro de los manejadores de rutas asíncronas, o ejecuta la llamada síncrona en un pool de hilos conawait run_in_threadpool(...). - Construir un nuevo cliente por solicitud. Crear
anthropic.Anthropic()oanthropic.AsyncAnthropic()dentro de un manejador de solicitudes descarta la agrupación de conexiones en cada llamada. Solución: construye un cliente al inicio del módulo o de la aplicación y reutilízalo. - Mezclar llamadas
asyncio.run()dentro de un bucle de eventos ya en ejecución. Llamar aasyncio.run(...)desde dentro de un manejador de FastAPI (que ya está dentro de un bucle) genera unRuntimeError. Solución: usaawaital cliente asíncrono directamente en el manejador asíncrono en lugar de envolverlo enasyncio.run(). awaitsecuencial en un bucle en lugar deasyncio.gather.for doc in docs: await summarize(doc)ejecuta las solicitudes una a la vez, renunciando a la concurrencia por la que se eligió el cliente asíncrono. Solución: lanza las corrutinas juntas y espéralas como un grupo conasyncio.gather(o unasyncio.Semaphorelimitado para limitar la concurrencia).- Olvidar
async withpara streaming asíncrono. Usarwithen lugar deasync withenAsyncAnthropic().messages.stream(...)genera unTypeError, ya que el objeto de stream del cliente asíncrono es un gestor de contexto asíncrono. Solución: empareja siempreAsyncAnthropic()streaming conasync withyasync for. - Asumir que el cliente asíncrono es más rápido por llamada. Una sola solicitud a través de
AsyncAnthropic()tarda el mismo tiempo de reloj de pared que la misma solicitud a través deAnthropic()- el beneficio solo aparece cuando múltiples solicitudes se superponen. Solución: no cambies a asíncrono solo por razones de latencia; cámbialo por concurrencia.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
Anthropic() en un pool de hilos (run_in_threadpool) | Un framework asíncrono necesita llamar a un cliente síncrono sin bloquear el bucle, por ejemplo, reutilizando código síncrono existente | Estás escribiendo código nuevo desde cero - simplemente usa AsyncAnthropic() directamente |
AsyncAnthropic() en todas partes, incluso en scripts | Quieres un tipo de cliente consistente en una base de código que mezcle scripts y servicios | El script no tiene concurrencia que explotar - el boilerplate de asyncio.run() añade ruido sin ganancia |
Múltiples clientes Anthropic() a través de un pool de hilos | Procesamiento por lotes limitado por CPU donde cada hilo de trabajo realiza sus propias llamadas secuenciales | Necesitas un control granular sobre cuántas solicitudes están en curso a la vez - asyncio.Semaphore con el cliente asíncrono es más preciso |
Preguntas frecuentes
¿Pierdo alguna característica del SDK al elegir el cliente síncrono sobre el asíncrono?
No.
Anthropic() y AsyncAnthropic() soportan los mismos parámetros, el mismo streaming y la misma configuración de reintentos/tiempos de espera; la única diferencia son las llamadas bloqueantes frente a las no bloqueantes.
¿Puedo usar el cliente síncrono dentro de una ruta de FastAPI?
Puedes, pero llamarlo directamente (sin un pool de hilos) bloquea el bucle de eventos para todas las demás solicitudes que está manejando ese proceso durante la llamada.
Prefiere AsyncAnthropic() en rutas asíncronas, o envuelve la llamada síncrona en run_in_threadpool si debes reutilizar código síncrono existente.
¿Hay un coste de rendimiento por usar AsyncAnthropic() en un script sin concurrencia?
La latencia por solicitud es la misma en ambos casos.
El coste añadido es la complejidad del código: necesitas asyncio.run(), async def, y await sin beneficio de rendimiento si nada se ejecuta concurrentemente.
¿Cómo ejecuto varias llamadas a Claude al mismo tiempo con el cliente asíncrono?
Usa asyncio.gather para lanzar múltiples corrutinas antes de esperarles, en lugar de esperar cada una en secuencia dentro de un bucle. Consulta la sección de Notas de Python de arriba.
¿Debo limitar cuántas solicitudes se ejecutan concurrentemente?
Sí, en la mayoría de las cargas de trabajo de producción.
Envuelve cada llamada con un asyncio.Semaphore dimensionado a un límite de concurrencia sensato, para que una gran distribución no abrume tus límites de tasa o el servicio de destino.
¿Funciona Django con AsyncAnthropic()?
El Django moderno soporta vistas asíncronas, por lo que AsyncAnthropic() también funciona allí.
Las vistas síncronas clásicas de Django deberían usar Anthropic() en su lugar, coincidiendo con el modelo de ejecución del framework.
¿Qué sucede si olvido "await" en una llamada a AsyncAnthropic()?
Recibes un objeto corrutina en lugar de un Message, y cualquier código que intente leer .content en él genera un AttributeError. Python no ejecuta el cuerpo de la corrutina hasta que se espera.
¿Se considera el cliente asíncrono beta o experimental?
No.
Tanto Anthropic() como AsyncAnthropic() son partes totalmente soportadas y generalmente disponibles del mismo paquete anthropic.
¿Puedo cambiar una base de código de síncrona a asíncrona más tarde sin una reescritura?
Mayormente, sí.
Dado que ambos clientes comparten nombres de métodos y parámetros, la migración generalmente implica añadir async/await, intercambiar la clase de cliente y cambiar with por async with para streaming - no rediseñar las llamadas en sí.
¿El streaming se comporta de manera diferente entre los dos clientes?
El gestor de contexto de streaming existe en ambos, pero la versión asíncrona es un gestor de contexto asíncrono iterado con async for en lugar de for. El stream subyacente de eventos es, por lo demás, el mismo.
¿Qué cliente debería usar un worker de Celery?
Los workers de Celery son típicamente síncronos, una tarea por proceso/hilo de worker, por lo que Anthropic() coincide con ese modelo de ejecución a menos que estés ejecutando Celery con un pool asíncrono.
Relacionado
- El modelo mental del SDK de Python de Anthropic - por qué síncrono y asíncrono comparten una superficie de API
- Streaming de respuestas con el SDK de Python -
stream()en ambos tipos de cliente - Manejo de uso de herramientas en streaming en flujos de trabajo de Python asíncrono - combinando
AsyncAnthropiccon uso de herramientas - Referencia de configuración de reintentos y tiempos de espera del SDK de Python -
max_retriesytimeouten ambos clientes
Versiones de la pila: Escrito contra la línea de modelos de Claude actual a partir de ~junio de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (el predeterminado), y Claude Haiku 4.5 - y el SDK oficial de Python
anthropic(última versión 0.x). Los nombres de modelos, versiones de SDK y precios cambian rápidamente; verifica los detalles actuales en platform.claude.com/docs antes de confiar en ellos.