La mayoría de las facturas de LLM están infladas por costumbre, no por necesidad real. Estas son las cuatro palancas que de verdad mueven la aguja (caché, enrutamiento, recorte de contexto y lotes), ordenadas por esfuerzo contra beneficio, con los comandos para aplicarlas y las fallas que, sin que te des cuenta, terminan costándote más de lo que ahorraste.

En resumen
- Mide antes de optimizar. Registra los tokens de entrada y salida por request, agrupados por funcionalidad: casi siempre hay una funcionalidad descontrolada que se lleva la mayor parte de la factura.
- La caché de prompts es la palanca que más rinde con menos esfuerzo: un cache read cuesta ~0.1x del input base, así que un prompt grande que se repite baja ~90% a partir de la primera llamada.
- Enruta por dificultad. Opus 4.8 cuesta $5/$25 por 1M de tokens; Sonnet 4.6, $3/$15; Haiku 4.5, $1/$5. Manda lo fácil a un nivel más bajo y reserva el modelo fuerte para lo difícil.
- Recorta la entrada, no la salida. Bajar max_tokens para ahorrar en la salida sale peor: el modelo corta la respuesta a la mitad y tienes que repetir toda la llamada.
- Manda el trabajo que no corre prisa (reportes nocturnos, clasificación masiva) por la Batches API y paga la mitad.
La mayoría de las facturas de LLM están infladas por costumbre, no por necesidad real. Estás reenviando el mismo system prompt de 8K tokens en cada llamada, pasando todo por tu modelo más caro y metiendo documentos enteros en un prompt del que solo se usan tres párrafos. Nada de eso te da más calidad: solo se nota en la factura. Esta guía recorre las cuatro palancas que de verdad mueven la aguja, más o menos en orden de beneficio por esfuerzo, con los comandos para aplicarlas y las fallas que, sin que te enteres, terminan costándote más de lo que ahorraste.
00 · Requisitos previos
No puedes optimizar lo que no puedes ver. Antes de tocar cualquiera de las cuatro palancas, deja esto montado: es una tarde de setup, no un proyecto entero.
- Una forma de leer el usage de cada response. El SDK de Anthropic devuelve response.usage con input_tokens, output_tokens, cache_read_input_tokens y cache_creation_input_tokens. Ese objeto es tu fuente de verdad; sin él, optimizas a ciegas.
- Una etiqueta por funcionalidad en cada llamada. Da igual si es una columna en una tabla, una línea de log estructurado o un label de métricas: necesitas agrupar el gasto por para qué fue la llamada, no solo por día.
- La tabla de precios al día, porque las palancas de abajo solo tienen sentido frente a números reales: Opus 4.8 cuesta $5 de entrada / $25 de salida por millón de tokens; Sonnet 4.6, $3 / $15; Haiku 4.5, $1 / $5. Un cache read sale a alrededor de 0.1x del precio del input base; un cache write de 5 minutos cuesta cerca de 1.25x.
Importante
Si de toda esta guía vas a hacer una sola cosa, que sea medir. Una y otra vez he visto cómo una sola funcionalidad mal acotada (un retry que reenvía el contexto completo, una ruta de debug que quedó registrando todo a máxima verbosidad) se lleva más de la mitad de la factura del mes. Eso no lo descubres mirando el total. Agrupa por funcionalidad y el culpable suele saltar a la vista en cinco minutos.
Con una línea de log mínima ya tienes para arrancar:
log({
feature: "support-reply",
model: res.model,
inputTokens: res.usage.input_tokens,
outputTokens: res.usage.output_tokens,
cacheRead: res.usage.cache_read_input_tokens ?? 0,
})
Déjalo correr unos días, suma inputTokens y outputTokens por funcionalidad, multiplica por la tabla de precios y vas a saber exactamente hacia dónde apuntar.
01 · Caché de prompts: el 90% gratis
Si mandas el mismo prefijo largo en cada llamada (un system prompt congelado, un manual de producto, un bloque de few-shot, un set de documentos recuperados) estás pagando el precio completo del input por releer tokens idénticos. La caché corta eso de raíz. El modelo mental cabe en una frase: la caché es un match por prefijo, y basta con que cambie un solo byte en cualquier parte del prefijo para invalidar todo lo que viene después.
La API arma tu request en un orden fijo (tools, luego system, luego messages) y la cache key son los bytes exactos hasta cada breakpoint. De ahí sale una regla simple: lo estable primero, lo volátil al final. Tus instrucciones congeladas y los documentos recuperados van adelante; la pregunta real del usuario, la fecha de hoy y cualquier request ID van al final.
La economía
Un cache read cuesta cerca de 0.1x del precio del input base. Un cache write de 5 minutos cuesta cerca de 1.25x. Es decir, la primera llamada paga un pequeño extra por escribir y cada llamada posterior paga más o menos la décima parte. Con la ventana por defecto de 5 minutos, con dos reúsos ya sales ganando; en un endpoint con tráfico, donde el mismo prefijo se golpea decenas de veces por minuto, la parte cacheada sale prácticamente gratis.
Cómo hacerlo
Con el SDK de TypeScript, la forma más simple es el auto-caching de nivel superior, que marca por ti el último bloque cacheable:
const res = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
cache_control: { type: "ephemeral" }, // cachea el último bloque cacheable
system: FROZEN_SYSTEM_PROMPT, // estable: va en el prefijo cacheado
messages: [{ role: "user", content: userQuestion }], // volátil: después del breakpoint
})
Atención
Hay dos trampas que se comen el hit rate sin hacer ruido. La primera: el prefijo mínimo cacheable depende del modelo (cerca de 4096 tokens en Opus 4.8 y Haiku 4.5, 2048 en Sonnet 4.6) y por debajo de eso la caché no hace nada y sin lanzar ningún error. La segunda: cualquier cosa no determinista en el prefijo (un timestamp, un UUID, un JSON.stringify sin keys ordenadas) cambia los bytes en cada request y escribes una entrada nueva cada vez. Verifícalo con usage.cache_read_input_tokens: si da cero entre requests idénticos, tienes un invalidador silencioso.
02 · Enrutamiento de modelos: deja de pagar de más por lo fácil
Tu modelo más fuerte es un exceso para casi todo lo que le mandas. Clasificar, reescribir algo corto, extraer un campo, enrutar un ticket: nada de eso necesita el nivel más alto, y la diferencia de precio es enorme. Opus 4.8 cuesta $25 por millón de tokens de salida; Haiku 4.5, $5. Eso es 5x de diferencia en la salida para trabajo donde el modelo barato rinde de sobra.
El patrón es poner un paso barato de triaje delante del caro. Manda todo primero a un nivel bajo y escala solo las llamadas que de verdad necesitan profundidad.
- Arranca abajo y escala hacia arriba. Pon Sonnet 4.6 o Haiku 4.5 como tu modelo por defecto y sube a Opus 4.8 solo cuando haya señales: confianza baja, un flag de alto riesgo, una clasificación explícita de "esto es complejo".
- Divide por subtarea, no por funcionalidad. Una misma funcionalidad suele mezclar pasos triviales con pasos difíciles. Corre la extracción en Haiku y el razonamiento en Opus, en lugar de mandar toda la funcionalidad a un solo modelo.
- No cambies de modelo a media conversación si estás cacheando. Las cachés están atadas al modelo: cambiar de modelo dentro de una conversación cacheada descarta el prefijo cacheado. Elige el modelo al inicio de la sesión, o levanta un subagente aparte más barato para la tarea secundaria en vez de cambiarle el modelo al loop principal.
Un ejemplo concreto: una bandeja de soporte donde el 70% de los tickets son consultas simples de FAQ y el 30% requiere razonar de verdad sobre el historial de la cuenta. Mandar el 70% a Haiku y dejar el 30% en Opus baja el gasto de modelo casi a la mitad frente a correr todo en Opus, y sin perder calidad en los tickets difíciles, porque esos siguen recibiendo el modelo fuerte.
03 · Recorte de contexto: manda lo que el modelo realmente usa
Casi con seguridad metes en el prompt más de lo que el modelo lee. El instinto de "dale todo por si acaso" es justo lo que infla los tokens de entrada, y en Opus eso son $5 por millón que quizá no hace falta gastar. Dos jugadas resuelven casi todo.
Recupera, no vuelques
Si tienes una base de conocimiento, no pegues el documento entero en el prompt. Recupera los pocos fragmentos relevantes y manda solo esos. Un contexto enfocado de 2K tokens normalmente le gana a un volcado de 40K tanto en costo como en calidad de la respuesta: el modelo no se distrae con veinte páginas que no vienen al caso.
Nota
Hay una distinción entre exacto y aproximado que conviene respetar. La búsqueda vectorial devuelve lo parecido, no lo verdadero. Para "encontrar contexto relacionado" es perfecta; para cualquier cosa que tenga que ser correcta (la dirección de envío actual de un usuario, el saldo de hoy) usa búsqueda exacta. Confundir las dos es justo así como terminas mandando, con toda confianza, la dirección del año pasado.
Resume los turnos viejos
En una conversación larga, el transcript completo crece en cada turno y pagas por reenviarlo entero. Resume los turnos viejos en una nota corta acumulada y deja textuales solo los turnos recientes. En los modelos compatibles, la compactación del lado del servidor hace esto sola a medida que te acercas a la ventana de contexto, pero un simple "resume todo lo más viejo que los últimos N turnos" que controles tú mismo suele bastar y es del todo predecible.
Cuando recortes, cuenta los tokens contra el modelo real en vez de adivinar. No uses tiktoken: es el tokenizador equivocado para Claude y cuenta de menos. Usa el endpoint de conteo de tokens:
const { input_tokens } = await client.messages.countTokens({
model: "claude-opus-4-8",
messages: trimmedMessages,
})
// compara input_tokens antes/después de un recorte para ver cuánto ahorraste de verdad
04 · Procesamiento por lotes: la mitad de precio para lo que puede esperar
No todo necesita respuesta en este mismo segundo. Reportes resumen nocturnos, reclasificación masiva de un backlog, generar metadata sobre una tabla entera: ese es trabajo que no tiene a nadie esperando, y la Batches API lo corre con 50% de descuento sobre los precios estándar. La mayoría de los lotes termina en menos de una hora; el tope son 24.
La concesión es latencia a cambio de dinero: mandas hasta 100,000 requests de una sola vez, haces polling hasta que terminan y recoges los resultados. Para cualquier cosa síncrona y de cara al usuario, los lotes están de más: la espera mata la experiencia. Para cualquier cosa programada o de fondo, no usar lotes es regalar la mitad del ahorro.
const batch = await client.messages.batches.create({
requests: rows.map((row) => ({
custom_id: row.id,
params: {
model: "claude-haiku-4-5", // combina lotes con enrutamiento para el máximo ahorro
max_tokens: 512,
messages: [{ role: "user", content: classifyPrompt(row) }],
},
})),
})
// haz polling de batch.processing_status hasta "ended", luego recoge los resultados
El mayor beneficio sale de apilar palancas: lote + modelo barato + un prefijo compartido cacheado en un trabajo de clasificación masiva puede dejar el costo por ítem en una fracción mínima de lo que costaría un baseline ingenuo con Opus a demanda.
Aplica estas palancas más o menos en el orden de arriba: la caché primero, porque es casi gratis; después el enrutamiento, luego el recorte y por último los lotes donde el workload lo permita. Pero nada de esto sirve hasta que midas: instrumenta primero, encuentra la única funcionalidad que se está comiendo la factura y, por lo general, vas a arreglar más en una tarde de trabajo bien dirigido que en un mes de andar adivinando.
Puntos clave
- Mide primero. Loguea los tokens por request agrupados por funcionalidad: la factura casi siempre la domina una sola cosa arreglable que no se ve en el total.
- La caché es la victoria más barata: lo estable primero, lo volátil al final, verifica con cache_read_input_tokens y ojo con el prefijo mínimo, que cambia según el modelo.
- Enruta por dificultad y recorta la entrada, no la salida: ahí el ahorro es grande y predecible sin tocar la calidad.
- Manda por lotes todo lo que nadie esté esperando para pagar la mitad, y apila lote + modelo barato + prefijo cacheado para los recortes más profundos.
Preguntas frecuentes
¿Un modelo más barato no va a bajar la calidad?
Solo si enrutas a ciegas. La idea del enrutamiento es ajustar el modelo a la dificultad del trabajo: lo fácil (clasificar, extraer campos, reescribir algo corto) se va a un modelo más barato que rinde de sobra, y lo difícil se queda en el fuerte. La calidad cae cuando mandas trabajo difícil a un modelo débil, no cuando dejas de mandarle lo trivial al caro. Agrega una señal de confianza o de riesgo para que la escalada se dé sola.
Agregué cache_control pero la factura no se movió. ¿Por qué?
Revisa usage.cache_read_input_tokens en requests repetidos. Si da cero, la caché nunca está pegando. Las dos causas típicas: tu prefijo está por debajo del tamaño mínimo cacheable (~4096 tokens en Opus 4.8, 2048 en Sonnet 4.6), así que nunca cachea y sin avisarte; o algo no determinista en el prefijo (un timestamp, un UUID, JSON serializado sin keys ordenadas) cambia los bytes en cada llamada. Haz un diff del prompt renderizado de dos requests idénticos para dar con el invalidador.
¿Por qué no simplemente bajar max_tokens para controlar el costo de la salida?
Porque por lo general te sale más caro. Si pones max_tokens muy bajo, el modelo corta la respuesta a la mitad; entonces repites toda la llamada con un límite más alto y terminas pagando la entrada dos veces más la repetición. Recorta la entrada sin miedo, que ahí el ahorro es predecible, y dale a la salida el espacio para terminar. Baja max_tokens solo cuando sepas de antemano que la salida va a ser corta, como una clasificación de una sola palabra.
¿Puedo combinar lotes con caché y enrutamiento?
Sí, y es justo apilándolos donde sale el mayor ahorro. Un trabajo de clasificación masiva que corre con un modelo barato (Haiku), por la Batches API (50% menos) y contra un prefijo compartido cacheado deja el costo por ítem en una fracción mínima de lo que costaría un baseline ingenuo con Opus a demanda. La única restricción es la latencia: los lotes son para trabajo que nadie está esperando, así que no los uses para nada síncrono y de cara al usuario.
¿Cómo cuento tokens con precisión al recortar el contexto?
Usa el endpoint de conteo de tokens (client.messages.countTokens) con el mismo model ID con el que vas a correr la inferencia: el conteo depende del modelo. No uses tiktoken ni otros tokenizadores de OpenAI; cuentan de menos los tokens de Claude, cerca de un 15–20% en texto plano y mucho más en código. Cuenta el mismo contenido antes y después de un recorte para medir exactamente cuánto ahorraste, en vez de andar adivinando.
¿Prefieres que lo hagamos por ti?
Esto mismo lo construimos para negocios como el tuyo. La primera conversación es gratis y sin compromiso.
Escríbenos por WhatsAppPrimera conversación gratis. Te responde el fundador.
Recursos relacionados

Agrega caché de prompts a una llamada de la API de Claude
Marca un prefijo estable como cacheable y bajas latencia y costo en prompts grandes que se repiten. El detalle está en el modelo de match por prefijo, que decide si consigues un hit o terminas pagando precio completo sin enterarte.

Elegir el modelo de Claude correcto para cada tarea
No elijas un modelo de Claude por fama ni por su puesto en un ranking. Elígelo según el tipo de fallo que menos te puedes permitir y de ahí enruta: usa por defecto una gama más barata y escala a la más potente solo cuando la tarea es crítica de verdad o cuando falla un chequeo de confianza barato.

LiteLLM: un solo gateway para todos tus modelos
LiteLLM unifica más de 100 proveedores de modelos detrás de una sola API con la forma de OpenAI, y su proxy convierte las llaves, presupuestos, fallbacks y logs dispersos por todos lados en un único plano de control al que apunta todo tu stack. Aquí va qué es, cuándo un gateway justifica su existencia frente a un SDK nativo, un quickstart que puedes copiar tal cual, y las concesiones sin maquillaje, incluyendo cómo la normalización va aplanando sin avisar las funciones del proveedor de las que quizá dependes.