Costos, límites y buenas prácticas

"Cualquiera hace una app que llama a un LLM. Profesional es quien sabe cuánto cuesta cada llamada — antes de hacerla."

Qué vas a aprender en este capítulo

Tu tutor del capítulo 4 funciona. Ahora falta lo que distingue un experimento de un sistema: ingeniería de costos. En este capítulo vas a aprender cómo se factura exactamente la API, a calcular el costo de un request real con lápiz y papel (y con código), a elegir modelo con criterio económico, y a usar las tres palancas grandes de ahorro: modelo correcto, prompt caching y Batches API. Cerramos con rate limits, seguridad operativa de la key, monitoreo de gastos — y el proyecto final del libro: tu centro de productividad completo, con presupuesto calculado.

Todos los cálculos usan la tabla de precios del capítulo 1 (precios por millón de tokens, junio 2026). Si los precios cambian mañana, el método sigue siendo el mismo.

5.1 Cómo se factura: el medidor de tokens

💡 Intuición

La API se factura como la energía eléctrica: pagás por consumo medido, no por cuota fija. El "kilovatio" es el token, y hay dos tarifas: los tokens de entrada (todo lo que mandás: system, historial, pregunta) y los de salida (lo que el modelo genera), con la salida ~5 veces más cara.

Y como con la luz, el secreto no es dejar de usar — es saber qué aparato consume cuánto. Un aire acondicionado encendido sin necesidad (historial gigante reenviado en cada turno) infla la factura más que cien focos (preguntas sueltas).

📐 Fundamento

La fórmula de todo request:

costo=tokensentrada1,000,000×Pentrada+tokenssalida1,000,000×Psalida\text{costo} = \frac{\text{tokens}_{entrada}}{1{,}000{,}000} \times P_{entrada} + \frac{\text{tokens}_{salida}}{1{,}000{,}000} \times P_{salida}

con los precios de la tabla del capítulo 1:

Modelo Entrada (por 1M) Salida (por 1M)
Claude Fable 5 $10 $50
Claude Opus 4.8 $5 $25
Claude Sonnet 4.6 $3 $15
Claude Haiku 4.5 $1 $5

Cálculo completo de un request real. Tu tutor manda: system de 80 tokens + historial de 3,000 tokens + pregunta de 120 tokens = 3,200 de entrada; la respuesta genera 900 de salida. Con Opus 4.8:

3,2001,000,000×5+9001,000,000×25=0.016+0.0225=0.0385\frac{3{,}200}{1{,}000{,}000} \times 5 + \frac{900}{1{,}000{,}000} \times 25 = 0.016 + 0.0225 = 0.0385

$0.0385 — menos de 4 centavos. Fijate en el detalle revelador: la salida (900 tokens) costó más que la entrada (3,200 tokens). La asimetría de precios manda: pedir respuestas concisas es la optimización más barata que existe.

Y la versión en código, directa del usage del capítulo 4:

PRECIOS = {  # dólares por millón de tokens: (entrada, salida)
    "claude-fable-5": (10, 50),
    "claude-opus-4-8": (5, 25),
    "claude-sonnet-4-6": (3, 15),
    "claude-haiku-4-5": (1, 5),
}

def costo_usd(modelo, usage):
    p_in, p_out = PRECIOS[modelo]
    return (usage.input_tokens * p_in + usage.output_tokens * p_out) / 1_000_000

# después de cualquier request:
print(f"Este request costó ${costo_usd('claude-opus-4-8', respuesta.usage):.4f}")

El multiplicador silencioso: el historial. En una conversación, el turno N reenvía todos los turnos anteriores como entrada. Una sesión de 20 turnos no cuesta 20 × (un turno): cuesta mucho más, porque la entrada crece turno a turno. Es la razón de fondo para truncar/resumir historiales largos (ejercicio 4 del cap. 4) — y para la palanca de la sección 5.4.

5.2 La palanca #1: el modelo correcto por tarea

📐 Fundamento

La decisión más cara de tu app no está en el código: está en el parámetro model. Mismo trabajo, factura 10× distinta.

Caso de estudio. Clasificar 10,000 correos de soporte en categorías (facturación/técnico/otro). Cada correo ≈ 300 tokens de entrada (con instrucciones), salida ≈ 5 tokens (la etiqueta). Total: 3M de entrada, 50K de salida.

Modelo Entrada Salida Total
Haiku 4.5 3 × $1 = $3.00 0.05 × $5 = $0.25 $3.25
Sonnet 4.6 3 × $3 = $9.00 0.05 × $15 = $0.75 $9.75
Opus 4.8 3 × $5 = $15.00 0.05 × $25 = $1.25 $16.25
Fable 5 3 × $10 = $30.00 0.05 × $50 = $2.50 $32.50

Para clasificar etiquetas simples, Haiku rinde prácticamente igual que Opus. Elegir mal acá no es un matiz: es pagar $32.50 por un trabajo de $3.25.

El método para elegir:

  1. Prototipá con el modelo barato que plausiblemente pueda con la tarea (casi siempre Haiku).
  2. Armá un mini-set de evaluación: 20-30 casos con la respuesta correcta conocida.
  3. Medí. ¿Haiku acierta lo suficiente? Quedate. ¿Falla? Subí un escalón y repetí.
  4. Mezclá modelos en una misma app. El patrón profesional: Haiku filtra/clasifica el 95% fácil, y solo los casos difíciles pasan a Opus. Lo barato hace el volumen; lo caro, la excepción.

Regla memorizable: Haiku para clasificar, Sonnet para conversar, Opus para razonar, Fable para lo imposible.

5.3 Contar antes de mandar: count_tokens

🛠️ En la práctica

¿Cuánto va a costar ESTE request antes de mandarlo? La API tiene un endpoint para contar tokens gratis y sin generar nada:

import anthropic

client = anthropic.Anthropic()

conteo = client.messages.count_tokens(
    model="claude-opus-4-8",
    messages=[{"role": "user", "content": texto_largo}],
)
print(conteo.input_tokens)   # tokens de entrada exactos de este request

Usos típicos:

  • Presupuestar un trabajo grande: contás un documento representativo, multiplicás por el lote, decidís modelo ANTES de gastar.
  • Validar entradas de usuarios: si alguien sube un PDF descomunal a tu app, lo detectás antes de pagar por procesarlo.
  • Vigilar el historial: cuando el conteo del historial supere tu umbral, truncás o resumís.
def estimar_costo_lote(client, modelo, doc_ejemplo, n_docs, salida_estimada=200):
    """Estima el costo de procesar n_docs similares a doc_ejemplo."""
    entrada = client.messages.count_tokens(
        model=modelo,
        messages=[{"role": "user", "content": doc_ejemplo}],
    ).input_tokens
    p_in, p_out = PRECIOS[modelo]
    total = n_docs * (entrada * p_in + salida_estimada * p_out) / 1_000_000
    return total

# ¿cuánto costaría resumir mis 240 lecturas del ciclo con Haiku?
print(f"${estimar_costo_lote(client, 'claude-haiku-4-5', lectura_tipica, 240):.2f}")

Para estimaciones de servilleta sin código: 1 palabra ≈ 1.3 tokens en español. Una página (~400 palabras) ≈ 520 tokens.

⚠️ Trampa común

Contar tokens con tiktoken. Es la primera librería que aparece al buscar "contar tokens en Python" — y es el tokenizador de OpenAI, no el de Claude. Los modelos parten el texto distinto: tus conteos quedarán sistemáticamente mal (típicamente cortos), tus presupuestos también, y tus truncados de historial cortarán donde no es.

Lo correcto: para Claude, contar con client.messages.count_tokens(...) — el conteo oficial, del modelo exacto que vas a usar — o con la regla 1.3 para estimaciones gruesas. tiktoken solo si tu código habla con modelos de OpenAI.

5.4 La palanca #2: prompt caching

💡 Intuición

Tu tutor manda el mismo system prompt — y quizás los mismos apuntes de 50 páginas — en CADA request. Es como si un profesor releyera el libro completo del curso antes de contestar cada pregunta de cada alumno: trabajo idéntico, repetido, cobrado completo cada vez.

Prompt caching le dice a la API: "esta parte inicial no cambia — guardala procesada". Las siguientes veces, releer lo guardado cuesta ~10× menos que procesarlo de nuevo.

📐 Fundamento

Se activa marcando bloques con cache_control en el system (o en mensajes):

respuesta = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": INSTRUCCIONES_Y_APUNTES,   # el bloque grande y estable
            "cache_control": {"type": "ephemeral"},
        }
    ],
    messages=[{"role": "user", "content": pregunta}],
)

La economía del caché, en múltiplos del precio de entrada normal:

Operación Costo relativo
Entrada normal (sin caché)
Escribir al caché (primer request) ~1.25×
Leer del caché (siguientes requests) ~0.1×

El primer request paga un recargo del 25% por guardar; del segundo en adelante, esa parte cuesta una décima. Con dos usos ya ganaste; con cien, el ahorro es ~90% de esa porción de entrada.

Ejemplo con números. Tutor con system + apuntes = 50,000 tokens cacheados, 40 preguntas en la sesión de estudio, con Opus 4.8 (entrada $5/M):

  • Sin caché: 40 × 50,000 × $5/M = $10.00 solo de reprocesar apuntes.
  • Con caché: 1 escritura (50,000 × $6.25/M = $0.3125) + 39 lecturas (39 × 50,000 × $0.50/M = $0.975) = $1.29.

Mismo tutor, mismas respuestas, 87% menos en esa porción. Cuanto más grande el contexto estable y más frecuente el uso, más brutal el ahorro.

La regla que lo gobierna: prefijo exacto. El caché matchea byte por byte desde el inicio. Cualquier cambio al principio del prompt invalida todo lo que sigue. Consecuencias prácticas:

  • Nada volátil al inicio del system. El clásico: poner fecha y hora al comienzo ("Hoy es 12 de junio, 18:43...") — cada request tiene un prefijo distinto y el caché nunca acierta, mientras vos pagás el recargo de escritura cada vez. Lo estable adelante, lo variable al final (o en los mensajes).
  • Verificá que funciona: el usage trae campos de caché — si las lecturas de caché siguen en cero tras varios requests idénticos, algo en tu prefijo está cambiando sin que lo veás.
  • El caché expira a los minutos de no usarse: brilla en ráfagas de requests (una sesión de estudio, un lote), no en un request por día.

5.5 La palanca #3: Batches API

📐 Fundamento

¿La tarea no necesita respuesta inmediata? La Batches API (/v1/messages/batches) procesa lotes de requests con 50% de descuento sobre el precio normal, con resultados en un máximo de 24 horas (usualmente mucho antes).

API normal Batches API
Respuesta Segundos ≤ 24 horas
Precio Tabla normal 50% de descuento
Ideal para Chat, interactivo Lotes: clasificar, resumir, generar en masa

El flujo: mandás un lote de requests (cada uno con un ID tuyo) → la API los procesa cuando hay capacidad → recogés los resultados. El caso de estudio de la sección 5.2 (10,000 correos con Haiku) baja de $3.25 a $1.63.

La pregunta de diseño es una sola: ¿alguien está esperando la respuesta en pantalla? Si no — resúmenes nocturnos, clasificación de archivos históricos, generación de datasets, reportes semanales — batches es plata regalada. Combinable además con caching y con la elección de modelo: las tres palancas se multiplican.

5.6 Rate limits y reintentos

📐 Fundamento

Tu cuenta tiene límites de uso por minuto (requests y tokens, según tu nivel de cuenta). Al excederlos, la API responde 429 con un header retry-after que dice cuántos segundos esperar.

Lo que ya sabés del capítulo 4, ahora con la visión de sistema:

  • El SDK reintenta solo los 429 y los errores de servidor (5xx), con espera exponencial. Para uso normal, no escribís nada.
  • Para lotes grandes en bucle, no te estrellés contra el límite a máxima velocidad: espaciá los requests (un time.sleep() modesto entre llamadas), procesá en tandas, y capturá RateLimitError como señal de "bajá el ritmo", no como error fatal.
  • La solución elegante para volumen casi siempre es la Batches API: los lotes no compiten con tu tráfico interactivo.
  • 529 (overloaded) es distinto de 429: no es tu límite, es la API saturada. Reintento con espera creciente, y paciencia.

5.7 Seguridad operativa de la API key

🛠️ En la práctica

Las reglas del capítulo 4 (variable de entorno, .gitignore) eran la mitad. La operación completa:

Prevención:

  • Una key por proyecto/uso, no una key maestra para todo. Si algo se filtra, el daño queda acotado y sabés de dónde salió.
  • La key de un proyecto compartido NO se manda por WhatsApp ni se pega en el grupo: cada quien crea la suya.
  • Revisión periódica en console.anthropic.com: keys que ya no usás → revocadas.

Rotación: cambiar la key cada cierto tiempo (creás la nueva, actualizás la variable de entorno, revocás la vieja). Práctica estándar en industria; buen hábito desde ya.

Protocolo si se filtró (la subiste a GitHub, la pegaste donde no era, la viste en un log):

  1. Revocala YA en la consola — minutos cuentan, los bots escanean continuamente. Revocar primero, investigar después.
  2. Generá una nueva y actualizá tu entorno.
  3. Revisá el uso reciente en la consola: ¿hay consumo que no reconocés?
  4. Si estaba en un repo público, asumí que fue copiada aunque borrés el commit — el historial de git y los espejos de GitHub no perdonan. La revocación es lo único que de verdad la mata.

5.8 Monitorear el gasto

🛠️ En la práctica

Dos niveles de monitoreo, los dos necesarios:

1. La consola (console.anthropic.com): uso y gasto por períodos, por key y por modelo. Hábitos mínimos: revisión semanal (2 minutos), y configurar los avisos/límites de gasto que la consola ofrezca — un tope de $10 mensuales te garantiza que ningún bug en un bucle se convierta en una factura sorpresa.

2. Tu propio registro. La consola te dice cuánto gastaste; tu log te dice en qué:

import csv, datetime

def registrar_uso(modelo, usage, archivo="uso_api.csv"):
    """Apunta cada request en un CSV: cuándo, qué modelo, tokens, costo."""
    with open(archivo, "a", newline="") as f:
        csv.writer(f).writerow([
            datetime.date.today().isoformat(),
            modelo,
            usage.input_tokens,
            usage.output_tokens,
            round(costo_usd(modelo, usage), 6),
        ])

Una llamada a registrar_uso() después de cada request, y al final del mes tu propio CSV te cuenta qué scripts gastan, qué modelo usás de más, y dónde apuntar la próxima optimización. (Y sí: podés analizar ese CSV con Claude, como en el cap. 2.)

⚠️ Trampa común

El bucle sin techo. Un while True con un bug que repite el mismo request, un script de lote sin contador, una recursión que llama a la API... y cada iteración cuesta plata real. Es EL accidente clásico del principiante con la API — el equivalente a dejar el grifo abierto, si el agua se cobrara por mililitro.

Cinturones de seguridad para TODO script con bucle:

MAX_REQUESTS = 100      # techo duro de la corrida
PRESUPUESTO = 2.00      # dólares máximos de la corrida

gasto, n = 0.0, 0
for item in items:
    if n >= MAX_REQUESTS or gasto > PRESUPUESTO:
        print(f"Freno de seguridad: {n} requests, ${gasto:.2f}")
        break
    respuesta = procesar(item)
    gasto += costo_usd(MODELO, respuesta.usage)
    n += 1

Seis líneas. Escribilas SIEMPRE antes de correr un lote — el día que un bug intente vaciarte el saldo, este freno lo para en $2.

5.9 Proyecto final

::::{seealso} Proyecto final — Tu centro de productividad con Claude :class: proyecto

Llegó el cierre del libro: integrar las tres alas en un sistema que uses de verdad, con presupuesto calculado.

Parte A — El ala de estudio (cap. 2). Un Proyecto en claude.ai por cada materia del ciclo, cada uno con: instrucciones con modo socrático (plantilla 2.3), el sílabo y los materiales al día, y al menos una conversación de estudio real usada esta semana. Entregable: captura o lista de tus Proyectos.

Parte B — El ala de código (cap. 3). Tu repo principal con CLAUDE.md completo (comandos, estructura, convenciones, advertencias) y al menos una tarea real ejecutada con la fórmula objetivo + criterio + restricciones, revisada con el checklist 3.6. Entregable: el CLAUDE.md y el diff de la tarea.

Parte C — El ala de la API: tu asistente con presupuesto (caps. 4-5). Evolucioná el tutor.py a asistente.py con TODOS estos requisitos:

  1. System prompt cacheado: instrucciones + un material de estudio tuyo (mínimo ~5,000 tokens para que el caché valga la pena) marcados con cache_control.
  2. Modelo elegido con justificación escrita (comentario en el código): ¿por qué ese y no uno más barato/caro?
  3. Streaming para las respuestas, historial multi-turno con truncado par, y manejo de errores tipado (todo del cap. 4).
  4. Contabilidad integrada: costo_usd() por request, registrar_uso() a CSV, y comando gasto que muestre el acumulado de la sesión.
  5. Freno de presupuesto: la sesión avisa al pasar un umbral configurable (p. ej. $0.50) y se niega a continuar pasado el doble.
  6. Presupuesto mensual calculado a mano (entregable escrito): estimá tu uso real (¿cuántas preguntas por sesión? ¿cuántas sesiones por semana?), calculá con la tabla de precios el costo mensual SIN caché y CON caché, y mostrá el ahorro. Una página, con las cuentas visibles.

Criterio de éxito del proyecto completo: (1) usaste las tres alas en una semana real de clases; (2) tu presupuesto estimado y tu CSV de uso real difieren menos del doble — si difieren más, el ejercicio de explicar por qué vale más que el proyecto entero; (3) un compañero puede correr tu asistente.py leyendo solo tu README, sin pedirte ayuda (y sin tu API key — la suya en SU variable de entorno).

Tiempo estimado: 4-6 horas si seguiste los avances de cada capítulo. Es portafolio real: "construí un asistente con la API de Claude, con caching y control de costos" es una línea de CV que abre conversaciones en cualquier entrevista. ::::

Resumen visual

Mermaid

flowchart TD A[Request a la API] --> B{¿La tarea es simple?} B -->|Sí| C[Haiku 4.5 — hasta 10× más barato] B -->|No| D[Sonnet / Opus según peso] C & D --> E{¿Contexto grande repetido?} E -->|Sí| F[Prompt caching
lecturas ~0.1× — estable al inicio] E -->|No| G[Request normal] F & G --> H{¿Alguien espera en pantalla?} H -->|No| I[Batches API — 50% de descuento] H -->|Sí| J[API normal + streaming] I & J --> K[usage → costo_usd → CSV
+ freno de presupuesto]

Palanca Ahorro típico Cuándo aplica
Modelo correcto Hasta 10× Siempre — la primera decisión
Respuestas concisas ~5× en salida Siempre — la salida es lo caro
Prompt caching ~90% del contexto repetido Contexto estable + uso en ráfaga
Batches API 50% del total Nadie espera la respuesta
Truncar historial Creciente con la sesión Conversaciones largas
count_tokens + frenos Evita catástrofes Todo script con bucle

Ejercicios

✏️ Ejercicio 1 — La factura del informe

Para tu informe final usaste Opus 4.8 así: 25 requests, promedio 4,000 tokens de entrada y 1,200 de salida cada uno. (a) ¿Cuánto costó en total? (b) ¿Qué fracción del costo fue salida? (c) ¿Cuánto habría costado con Sonnet 4.6?

✅ Solución
  • (a) Totales: entrada 25 × 4,000 = 100,000; salida 25 × 1,200 = 30,000. Entrada: 0.1M × $5 = $0.50. Salida: 0.03M × $25 = $0.75. Total: $1.25.
  • (b) Salida: 0.75 / 1.25 = 60% del costo con solo el 23% de los tokens — la asimetría 5× de precios en acción.
  • (c) Con Sonnet: 0.1M × $3 + 0.03M × $15 = 0.30 + 0.45 = $0.75 — 40% menos. Si la calidad de Sonnet alcanzaba para esta tarea, era la mejor opción.

✏️ Ejercicio 2 — ¿Conviene el caché?

Tu app de preguntas sobre el reglamento estudiantil manda siempre el mismo reglamento de 30,000 tokens como contexto, con Haiku 4.5. (a) ¿Cuánto cuesta la porción del reglamento en 50 preguntas SIN caché? (b) ¿Y CON caché (1 escritura a 1.25×, 49 lecturas a 0.1×)? (c) ¿Y si la app recibiera solo 1 pregunta por día (el caché expira entre usos)?

✅ Solución
  • (a) Sin caché: 50 × 30,000 × $1/M = $1.50.
  • (b) Con caché: escritura 30,000 × $1.25/M = $0.0375; lecturas 49 × 30,000 × $0.10/M = $0.147. Total: $0.185 — ahorro del 88%.
  • (c) Con 1 pregunta/día el caché expira entre requests: cada pregunta paga la escritura (1.25×) sin que nadie lea después. Costo: 50 × 30,000 × $1.25/M = $1.875 — PEOR que sin caché. Moraleja: el caching paga en ráfagas, no en goteo. Para ese patrón de uso, mejor sin cache_control (o evaluar si el contexto puede reducirse).

✏️ Ejercicio 3 — Diseño de pipeline barato

Tu asociación de estudiantes quiere resumir y clasificar las 4,000 sugerencias del buzón digital del año (cada una ~150 tokens; salida deseada ~80 tokens). Nadie necesita los resultados antes del viernes. Diseñá la solución más barata combinando las tres palancas y calculá el costo. Las instrucciones de clasificación ocupan 600 tokens y son idénticas para todas.

✅ Solución

Diseño: (1) modelo: Haiku 4.5 — resumir/clasificar texto corto es su terreno; (2) Batches API — nadie espera, 50% de descuento; (3) las instrucciones de 600 tokens son contexto repetido — cacheables, aunque con 600 tokens el ahorro es menor (los mínimos de caché favorecen contextos grandes; calculemos sin caché para ser conservadores).

Cálculo sin caché, con batch:

  • Entrada: 4,000 × (600 + 150) = 3,000,000 tokens → 3M × $1 = $3.00.
  • Salida: 4,000 × 80 = 320,000 → 0.32M × $5 = $1.60.
  • Subtotal: $4.60 → con 50% de batch: $2.30.

Por comparación, lo mismo con Opus 4.8 sin batch: $23.00 — 10× más. Las palancas combinadas (modelo + batch) convierten un gasto notable en uno trivial. Bonus: correr primero un piloto de 30 sugerencias para validar la calidad de Haiku antes de soltar las 4,000.

✏️ Ejercicio 4 — El bug de $40

Un compañero te muestra su script: "le pedí que tradujera mis 200 apuntes anoche, y hoy amanecí con $40 menos de saldo. ¡Debería haber costado centavos!". El script usa Opus 4.8, y su bucle, tras cada traducción, agrega el documento Y la traducción a una lista historial que reenvía completa en cada request "para que mantenga el estilo consistente". Cada apunte ≈ 1,500 tokens. (a) ¿Cuál es el bug de costos? (b) Estimá por qué $40 es plausible. (c) Proponé el arreglo (manteniendo estilo consistente) y los dos frenos que faltaron.

✅ Solución
  • (a) El historial acumulativo innecesario: las traducciones son independientes, pero cada request reenvía TODOS los documentos y traducciones anteriores como entrada. La entrada del request N crece ~3,000 × N tokens — costo cuadrático en el número de documentos.
  • (b) Entrada acumulada ≈ 3,000 × (1 + 2 + ... + 199) ≈ 3,000 × 19,900 ≈ 59.7M tokens → 59.7 × $5 ≈ $298 si hubiera terminado... con $40 llegó a unos 70-80 documentos antes de agotar el saldo. Plausible, tristemente.
  • (c) Arreglo: requests independientes (sin historial). La consistencia de estilo no se logra con historial sino con un system prompt fijo que defina el estilo ("traducí en tono formal, conservá términos técnicos en inglés...") — que además, siendo idéntico y al inicio, es cacheable. Mejor aún: Batches API (nadie esperaba despierto) y Haiku si la traducción es directa → el lote completo por centavos. Los frenos que faltaron: techo de requests/presupuesto en el bucle (sección 5.8) y una estimación previa con count_tokens — cualquiera de los dos habría detectado el problema antes del primer dólar.

Para profundizar