Guía completa de Prompt Caching en la API de Anthropic: cómo funciona y cuánto ahorras
Cada vez que tu app llama a la API de Claude, le estás mandando el mismo system prompt de 2,000 tokens. Las mismas instrucciones de personalidad, las mismas reglas de negocio, los mismos ejemplos few-shot. Si tenés 50,000 requests al día, eso es 100 millones de tokens de input que el modelo reprocesa desde cero, todos los días, cobrando tarifa completa. El prompt caching de la API de Anthropic existe exactamente para resolver ese problema: en lugar de reprocesar contenido idéntico en cada llamada, el modelo lo lee desde caché al 10% del precio estándar. No es una optimización marginal. Es una reducción del 90% en los tokens más repetidos de tu app.
El problema que tiene este feature es que no se activa solo. A diferencia del caching automático de OpenAI, Anthropic requiere que vos le indiques explícitamente qué segmentos querés cachear usando el parámetro cache_control. Eso tiene una ventaja enorme: tenés control granular sobre exactamente qué va al caché, con qué TTL, y hasta cuatro puntos de corte distintos por request. Pero también significa que si nunca configuraste esto, estás dejando dinero sobre la mesa en cada request que tu app hace hoy.
Este artículo te explica cómo funciona el mecanismo internamente, qué cuesta escribir y leer del caché, cómo estructurar tus prompts para maximizar el hit rate, y te da el código Python completo para implementarlo con métricas de ahorro en tiempo real. Todos los precios están verificados contra la documentación oficial de Anthropic de mayo 2026.
Cómo funciona el prompt caching en la API de Anthropic
La mecánica interna es la de un prefix cache o caché de prefijos. Cuando marcás un bloque con cache_control, Anthropic genera una representación KV (key-value) de todos los tokens del prompt hasta ese punto, la guarda en memoria y le asigna un TTL. En el próximo request donde el prefijo hasta esa marca sea bit-a-bit idéntico, el modelo lee desde caché en lugar de reprocesar. El resultado son dos beneficios simultáneos: reducción de costo del 90% en esos tokens, y reducción de latencia de hasta el 85% en tiempo de respuesta para prompts largos. Un prompt de 100K tokens que tardaba 11.5 segundos en procesarse puede bajar a 2.4 segundos con caché activo.
El orden de procesamiento importa
Anthropic procesa los componentes del request en un orden fijo: primero tools, después system, después messages. Eso define cómo se construye el prefijo del caché. Si ponés contenido dinámico antes del contenido estático dentro de cualquiera de esos bloques, la parte estática queda afuera del caché porque su key incluye el contenido variable que viene antes.
La regla de oro es simple: el contenido estático siempre primero, el contenido dinámico siempre al final. Las instrucciones del sistema van primero. Los ejemplos few-shot van después. El contexto de conversación histórico va antes del mensaje actual del usuario. Ese orden no es sugerencia: si lo invertís, el cache hit rate se va a cero.
TTL: 5 minutos o 1 hora
Hay dos duraciones disponibles. El TTL estándar de 5 minutos es el default: el caché se refresca con cada uso dentro de ese período, y expira si no hay actividad por más de 5 minutos. El TTL extendido de 1 hora se configura con "ttl": "1h" en el cache_control y tiene un costo de escritura mayor. La lógica de cuándo usar cuál es directa:
- Si tu app tiene requests frecuentes (cada pocos minutos o menos), usá el TTL de 5 minutos.
- Si tu workload es por lotes, nocturno, o con períodos de inactividad largos, el TTL de 1 hora evita que el caché expire entre rafagas.
Los precios reales del caché por modelo
El pricing de prompt caching tiene tres componentes que se suman en cada request:
| Cache Write 5 min | Cache Write 1h | Cache Read | |
|---|---|---|---|
| Multiplicador | 1.25× precio base | 2.0× precio base | 0.1× precio base |
| Sonnet 4.6 ($3.00 base) | $3.75/MTok | $6.00/MTok | $0.30/MTok |
| Haiku 4.5 ($1.00 base) | $1.25/MTok | $2.00/MTok | $0.10/MTok |
La escritura al caché es levemente más cara que un request normal (25% más para TTL de 5 minutos). El beneficio empieza desde el segundo request que usa ese caché. El punto de equilibrio es exactamente 1.4 lecturas por prefijo cacheado: si mandás el mismo system prompt más de una vez (y en producción lo hacés miles de veces por hora), el ahorro es inmediato y acumulativo.
Restricciones técnicas a tener en cuenta
El caché tiene un mínimo de tokens por bloque para ser elegible:
- Claude Haiku 4.5: mínimo 2,048 tokens
- Claude Sonnet y Opus: mínimo 1,024 tokens
Un system prompt de 300 tokens no se puede cachear. Si el tuyo es corto, la solución es agregar documentos de referencia, ejemplos o especificaciones al bloque del sistema hasta superar el mínimo. Además, los hits del caché requieren coincidencia exacta bit a bit del prefijo. Un solo espacio de diferencia, una fecha dinámica, un timestamp en el system prompt: cualquiera de esos rompe el cache key y te fuerza a pagar una escritura completa.
Implementación completa en Python con métricas de ahorro
Este script implementa un cliente de Claude con prompt caching activo, múltiples puntos de corte, y un tracker en tiempo real del ahorro acumulado en cada request.
python# cliente_claude_con_cache.py # Cliente de Claude con prompt caching explícito y métricas de ahorro en tiempo real. # Compatible con la API de Anthropic, mayo 2026. import anthropic from dataclasses import dataclass from typing import Optional # --- Precio base por modelo (USD por millón de tokens) --- # Verificados en platform.claude.com/docs/en/about-claude/pricing — mayo 2026 PRECIOS = { "claude-haiku-4-5-20251001": { "input": 1.00, # Base input por MTok "output": 5.00, # Output por MTok "cache_write": 1.25, # Escritura de caché 5min por MTok "cache_read": 0.10, # Lectura de caché por MTok }, "claude-sonnet-4-6-20260101": { "input": 3.00, "output": 15.00, "cache_write": 3.75, "cache_read": 0.30, }, } @dataclass class MetricasCache: """Acumula tokens y costos para calcular el ahorro real de la sesión.""" tokens_input_frescos: int = 0 tokens_cache_writes: int = 0 tokens_cache_reads: int = 0 tokens_output: int = 0 costo_real_usd: float = 0.0 costo_sin_cache_usd: float = 0.0 total_requests: int = 0 def registrar(self, uso: dict, modelo: str): """Registra los tokens de un response y acumula métricas.""" precios = PRECIOS[modelo] frescos = uso.get("input_tokens", 0) writes = uso.get("cache_creation_input_tokens", 0) reads = uso.get("cache_read_input_tokens", 0) output = uso.get("output_tokens", 0) self.tokens_input_frescos += frescos self.tokens_cache_writes += writes self.tokens_cache_reads += reads self.tokens_output += output self.total_requests += 1 # Costo real de este request (con caché) costo = ( (frescos / 1_000_000) * precios["input"] + (writes / 1_000_000) * precios["cache_write"] + (reads / 1_000_000) * precios["cache_read"] + (output / 1_000_000) * precios["output"] ) # Costo hipotético si no hubiera caché total_input = frescos + writes + reads costo_sin_cache = ( (total_input / 1_000_000) * precios["input"] + (output / 1_000_000) * precios["output"] ) self.costo_real_usd += costo self.costo_sin_cache_usd += costo_sin_cache @property def ahorro_usd(self) -> float: return self.costo_sin_cache_usd - self.costo_real_usd @property def porcentaje_ahorro(self) -> float: if self.costo_sin_cache_usd == 0: return 0.0 return (self.ahorro_usd / self.costo_sin_cache_usd) * 100 def imprimir_resumen(self): print(f"\n{'─'*55}") print(f" MÉTRICAS DE CACHÉ — {self.total_requests} requests") print(f"{'─'*55}") print(f" Tokens frescos procesados : {self.tokens_input_frescos:>10,}") print(f" Tokens escritos al caché : {self.tokens_cache_writes:>10,}") print(f" Tokens leídos desde caché : {self.tokens_cache_reads:>10,}") print(f" Tokens de output : {self.tokens_output:>10,}") print(f"{'─'*55}") print(f" Costo real (con caché) : ${self.costo_real_usd:>10.5f}") print(f" Costo sin caché : ${self.costo_sin_cache_usd:>10.5f}") print(f" Ahorro acumulado : ${self.ahorro_usd:>10.5f} " f"({self.porcentaje_ahorro:.1f}%)") print(f"{'─'*55}\n") def construir_sistema_cacheado( instrucciones_base: str, documentos_referencia: Optional[str] = None ) -> list: """ Construye el system prompt con hasta dos puntos de caché: 1) Instrucciones base del sistema (más estables, máxima cobertura). 2) Documentos de referencia opcionales (cambian menos que el contexto dinámico). Ambos bloques se marcan con cache_control para maximizar el hit rate. """ bloques = [ { "type": "text", "text": instrucciones_base, # Primer breakpoint: las instrucciones base casi nunca cambian. # SIEMPRE va primero para asegurar la cobertura máxima del prefijo. "cache_control": {"type": "ephemeral"} } ] if documentos_referencia: bloques.append({ "type": "text", "text": documentos_referencia, # Segundo breakpoint: los documentos cambian con menos frecuencia # que el historial de conversación — cache separado por eso. "cache_control": {"type": "ephemeral"} }) return bloques def llamar_claude_con_cache( cliente: anthropic.Anthropic, modelo: str, system_cacheado: list, historial: list, mensaje_usuario: str, metricas: MetricasCache, ) -> str: """ Envía un request a Claude con caché activo y registra las métricas. Solo las partes estáticas del sistema llevan cache_control. El historial de conversación va en messages sin caché explícito. """ messages = historial + [{"role": "user", "content": mensaje_usuario}] response = cliente.messages.create( model=modelo, max_tokens=1024, system=system_cacheado, messages=messages, ) metricas.registrar(response.usage.model_dump(), modelo) return response.content[0].text # ───────────────────────────────────────────── # DEMO: 5 requests reutilizando el mismo system prompt cacheado # ───────────────────────────────────────────── if __name__ == "__main__": cliente = anthropic.Anthropic() # Lee ANTHROPIC_API_KEY del entorno modelo = "claude-haiku-4-5-20251001" metricas = MetricasCache() instrucciones = """Sos un asistente técnico especializado en arquitecturas de microservicios y APIs REST. Tu objetivo es ayudar a equipos de desarrollo a diseñar sistemas escalables, seguros y mantenibles. PRINCIPIOS QUE SIEMPRE SEGUÍS: - Priorizás la simplicidad operativa sobre la elegancia técnica. - Cada recomendación viene acompañada de sus trade-offs. - Usás ejemplos concretos de sistemas reales cuando es posible. - Preferís herramientas maduras y bien documentadas. - Advertís explícitamente sobre antipatrones comunes. [... incluí aquí tus instrucciones reales hasta superar los tokens mínimos ...] """ * 4 # Repetimos para superar el mínimo de tokens requerido para cachear documentos = """CONTEXTO DE LA EMPRESA: Stack actual: Python/FastAPI, PostgreSQL, Redis, Kubernetes en GKE. Equipo: 8 developers backend, 3 frontend, 2 DevOps. Volumen: ~2M requests/día, latencia P95 target < 200ms. Restricciones: presupuesto de infraestructura fijo, migración incremental.""" system_cacheado = construir_sistema_cacheado(instrucciones, documentos) preguntas = [ "¿Cómo implementamos circuit breakers en nuestro stack?", "¿Qué estrategia de versionado de API recomendás?", "¿Cómo manejamos la consistencia eventual entre microservicios?", "¿Cuándo tiene sentido usar sagas vs transacciones distribuidas?", "¿Cómo monitoreamos la salud de los microservicios en producción?", ] historial = [] for i, pregunta in enumerate(preguntas, 1): print(f"\n[Request {i}] {pregunta[:60]}...") respuesta = llamar_claude_con_cache( cliente, modelo, system_cacheado, historial, pregunta, metricas ) print(f"Respuesta: {respuesta[:120]}...") historial.append({"role": "user", "content": pregunta}) historial.append({"role": "assistant", "content": respuesta}) metricas.imprimir_resumen()
Cómo leer el output del script
En la primera llamada vas a ver cache_creation_input_tokens con tokens (el caché se está escribiendo, cuesta 1.25× el precio base). Desde la segunda llamada en adelante, cache_read_input_tokens va a mostrar los tokens del system prompt que se leyeron desde caché al 10% del costo. El resumen final muestra el ahorro acumulado en dólares y el porcentaje real de reducción de costo para toda la sesión.
Con el system prompt del ejemplo y 5 requests, el ahorro típico va del 55% en el primer ciclo completo al 80%+ una vez que el caché está caliente. En una app de producción con miles de usuarios compartiendo el mismo system prompt, ese porcentaje se estabiliza por encima del 85%.
Estrategias para maximizar el hit rate del caché
Entender el mecanismo es la mitad del trabajo. La otra mitad es estructurar tu app para que el cache hit rate sea lo más alto posible.
El error más común: contenido dinámico en el system prompt
El error que más frecuentemente invalida el caché en producción es insertar contenido variable dentro de los bloques cacheados. Un timestamp, la fecha de hoy, el nombre del usuario, la versión del sistema: cualquier string que cambie entre requests destruye el cache key del bloque completo.
La solución es mover todo el contenido dinámico al último mensaje del usuario o a un bloque sin cache_control. El system prompt cacheado debe contener exclusivamente contenido que nunca cambia entre requests del mismo tipo.
Hasta cuatro breakpoints por request
La API permite hasta cuatro bloques con cache_control por request. Eso habilita una estrategia de caché en capas por frecuencia de cambio:
- Instrucciones del sistema — nunca cambian → caché con TTL de 1 hora
- Especificaciones del producto o dominio — cambian raramente → caché de 5 min
- Documentos de referencia dinámicos — cambian por sesión → caché de 5 min
- Historial de conversación largo — estable dentro de la sesión → caché de 5 min
Cada capa solo se invalida cuando ese contenido específico cambia, no cuando cambia cualquier otra capa. Es un sistema de cache busting quirúrgico que maximiza la proporción de tokens que pagan el 10% en lugar del 100%.
TTL de 1 hora para workloads batch o de baja frecuencia
Si tu app procesa documentos en lotes nocturnos, análisis asincrónicos o cualquier workload donde los requests están separados por más de 5 minutos, el TTL estándar expira entre rafagas. Cada reinicio del lote paga un cache write (1.25× el precio base) antes de volver a beneficiarse de las lecturas. El TTL extendido de 1 hora cuesta más al escribir (2× el precio base en lugar de 1.25×), pero amortiza ese costo si los requests siguen llegando dentro de esa ventana.
Cuánto ahorras realmente: cálculo con datos de producción
Los números varían según el perfil de tu app, pero hay un patrón consistente en producción.
Caso 1: Chatbot de soporte con 100,000 requests/día
- System prompt: 2,000 tokens (instrucciones + tono + políticas)
- Input promedio de usuario: 150 tokens
- Output promedio: 250 tokens
- Modelo: Haiku 4.5
Sin caché: 100,000 × 2,150 tokens × $1.00/MTok ≈ $215/día de input + output.
Con caché al 85% de hit rate: los 2,000 tokens del sistema cuestan $0.10/MTok en la mayoría de requests. Eso baja el costo de input del system prompt de $200/día a ~$22/día. Ahorro diario de $178, o $5,340/mes, solo en el system prompt.
Caso 2: Pipeline de análisis de documentos con contexto extenso
Un equipo reportó en producción que al usar Haiku 4.5 con prompt caching más Batch API para análisis de root cause (4,000 tokens de input, 500 de output, ~75% de tokens cacheados), el costo por análisis bajó de aproximadamente $0.0045 a $0.00023, una reducción del 95%. A escala de decenas de miles de análisis por mes, eso representa un ahorro de varios miles de dólares.
El punto de equilibrio real
El break-even del caché de Anthropic está en 1.4 lecturas por cada escritura. Con cualquier prompt estático que se usa más de una vez, ya estás ahorrando. En una app de producción real, el mismo system prompt se reutiliza miles de veces por hora. El ROI no es gradual: es inmediato y se acumula con el volumen.
Conclusión: activá el caché esta semana, no el próximo sprint
El prompt caching de Anthropic no es una optimización futura: es una reducción directa e inmediata del costo de producción que requiere exactamente un cambio en tu código — agregar cache_control a los bloques correctos del prompt. Si tu app tiene un system prompt de más de 1,024 tokens y hace más de dos requests que lo incluyen, estás pagando de más desde el momento en que salió el primer request.
La secuencia para implementarlo esta semana:
- Auditá tu system prompt actual y verificá que supera el mínimo de tokens para cacheo (1,024 para Sonnet/Opus, 2,048 para Haiku).
- Identificá todo el contenido dinámico que esté dentro del system prompt y movelo al mensaje del usuario.
- Agregá
cache_control: { type: "ephemeral" }al bloque del sistema en tu primer request. - Logeá
cache_creation_input_tokensycache_read_input_tokensdel objetousageen cada response para verificar que el caché está funcionando. - Si tus requests están separados por más de 5 minutos, evaluá el TTL de 1 hora.
El código de este artículo ya tiene el cliente listo con las métricas de ahorro incorporadas. Tomalo, reemplazá el system prompt con el tuyo, y en el primer batch de requests vas a ver los números reales de cuánto estabas dejando sobre la mesa.
Fredo
Estudiante de Ing. en Sistemas & Desarrollador
"Apasionado por la tecnología, el desarrollo web y la inteligencia artificial. Explorando el futuro de la ingeniería de software."