Creación de una calculadora de costos pre-solicitud con la API de conteo de tokens
Un pipeline que solo descubre el costo de una solicitud después de recibir la respuesta no puede hacer cumplir un presupuesto, solo puede informar una violación después de que el dinero ya se ha gastado.
messages.count_tokens cierra esa brecha: devuelve un recuento exacto de tokens de entrada sin ejecutar el modelo, por lo que puedes valorar una solicitud antes de que llegue al endpoint de finalización.
Esta página crea una calculadora de costos reutilizable en torno a esa llamada, y luego la integra en una puerta que puede rechazar o degradar una solicitud demasiado grande antes de que se envíe.
Receta
Tarjeta de receta de referencia rápida, lista para copiar y pegar.
import anthropic
client = anthropic.Anthropic()
RATES = {
"claude-sonnet-5": {"input": 2.00, "output": 10.00},
"claude-opus-4-8": {"input": 5.00, "output": 25.00},
"claude-haiku-4-5": {"input": 1.00, "output": 5.00},
}
def estimate_cost(client, model: str, max_tokens: int, **kwargs) -> float:
count = client.messages.count_tokens(model=model, **kwargs)
rate = RATES[model]
input_cost = (count.input_tokens / 1_000_000) * rate["input"]
worst_case_output_cost = (max_tokens / 1_000_000) * rate["output"]
return input_cost + worst_case_output_cost
cost = estimate_cost(
client,
model="claude-sonnet-5",
max_tokens=500,
messages=[{"role": "user", "content": "Redacta una nota de lanzamiento para la v2.4."}],
)
print(f"${cost:.6f}")Cuándo usar esto:
- Estás creando un pipeline donde una sola solicitud incorrecta o demasiado grande (por ejemplo, un documento de 50 páginas pegado por un usuario) podría agotar un presupuesto por solicitud o por inquilino.
- Quieres registrar un costo estimado junto con una solicitud antes de que se envíe, para fines de observabilidad o flujos de aprobación.
- Estás comparando el costo de enrutar la misma solicitud a dos o tres niveles de modelos antes de elegir uno.
- Necesitas un límite estricto para el costo en un pipeline automatizado, no solo una advertencia suave después del hecho.
Ejemplo de trabajo
import anthropic
client = anthropic.Anthropic()
RATES = {
"claude-sonnet-5": {"input": 2.00, "output": 10.00},
"claude-opus-4-8": {"input": 5.00, "output": 25.00},
"claude-haiku-4-5": {"input": 1.00, "output": 5.00},
}
class BudgetExceeded(Exception):
pass
def estimate_cost(client, model: str, max_tokens: int, **request_kwargs) -> dict:
"""Devuelve un desglose del costo de entrada estimado, el costo de salida en el peor de los casos y el total."""
count = client.messages.count_tokens(model=model, **request_kwargs)
rate = RATES[model]
input_cost = (count.input_tokens / 1_000_000) * rate["input"]
worst_case_output_cost = (max_tokens / 1_000_000) * rate["output"]
return {
"input_tokens": count.input_tokens,
"input_cost": input_cost,
"worst_case_output_cost": worst_case_output_cost,
"worst_case_total": input_cost + worst_case_output_cost,
}
def send_with_budget(client, max_cost_usd: float, model: str, max_tokens: int, **request_kwargs):
"""Estima el costo, aplica un presupuesto y luego envía la solicitud solo si encaja."""
breakdown = estimate_cost(client, model, max_tokens, **request_kwargs)
if breakdown["worst_case_total"] > max_cost_usd:
raise BudgetExceeded(
f"El costo en el peor de los casos ${breakdown['worst_case_total']:.4f} excede "
f"el presupuesto ${max_cost_usd:.4f} para el modelo {model}"
)
response = client.messages.create(model=model, max_tokens=max_tokens, **request_kwargs)
return response, breakdown
try:
response, breakdown = send_with_budget(
client,
max_cost_usd=0.02,
model="claude-sonnet-5",
max_tokens=500,
messages=[{"role": "user", "content": "Redacta una nota de lanzamiento para la v2.4."}],
)
print(response.content[0].text)
print(f"Estimado: ${breakdown['worst_case_total']:.6f}")
except BudgetExceeded as e:
print(f"Rechazado: {e}")Lo que esto demuestra:
estimate_costaísla las matemáticas de precios de la solicitud en sí, por lo que se puede reutilizar para registrar, controlar o comparar sin duplicar la tabla de tarifas.- La estimación de salida en el peor de los casos asume que se consume el presupuesto completo de
max_tokens, lo que proporciona un límite superior conservador en lugar de una suposición optimista. send_with_budgetsolo llama amessages.createdespués de que la estimación supera la verificación del presupuesto, por lo que una solicitud rechazada nunca llega a la API y nunca se factura.- Generar una excepción dedicada
BudgetExceededpermite al código que llama distinguir "demasiado caro" de un error real de la API y manejar cada uno de manera diferente.
Profundización
Cómo funciona
messages.count_tokensacepta los mismos parámetrosmodel,system,toolsymessagesquemessages.create, por lo que cuenta exactamente los tokens de entrada que produciría tu llamada real.- No acepta ni necesita
max_tokens, ya que nunca genera salida, solo mide el lado de entrada. - La llamada en sí no consume tokens facturables, es una operación de medición, no una operación de generación, por lo que llamarla liberalmente en una verificación previa no agrega un costo significativo.
- El costo de salida solo se puede estimar, nunca contar exactamente, antes de la llamada, porque el modelo aún no ha generado nada. Usar
max_tokenscomo la longitud de salida asumida proporciona un verdadero peor caso, ya que la API nunca generará más que ese límite. - Para una estimación de salida más ajustada (pero no garantizada), puedes rastrear un promedio móvil de
output_tokensreales de llamadas similares anteriores en lugar de asumir siempre el límite completo demax_tokens.
Creación de una estimación más ajustada con promedios históricos
class RollingOutputEstimator:
"""Rastrea el uso real de tokens de salida por modelo para refinar la suposición del peor caso."""
def __init__(self):
self._history: dict[str, list[int]] = {}
def record(self, model: str, output_tokens: int) -> None:
self._history.setdefault(model, []).append(output_tokens)
def typical(self, model: str, fallback: int) -> int:
samples = self._history.get(model)
if not samples:
return fallback
return sum(samples) // len(samples)
estimator = RollingOutputEstimator()
def estimate_cost_v2(client, model: str, max_tokens: int, **request_kwargs) -> dict:
count = client.messages.count_tokens(model=model, **request_kwargs)
rate = RATES[model]
input_cost = (count.input_tokens / 1_000_000) * rate["input"]
typical_output = estimator.typical(model, fallback=max_tokens)
typical_output_cost = (typical_output / 1_000_000) * rate["output"]
return {"input_cost": input_cost, "typical_output_cost": typical_output_cost}Este patrón intercambia un límite superior garantizado por una estimación más realista una vez que tienes datos de uso reales, útil para paneles donde deseas un número representativo en lugar de un peor caso.
Parámetros y valores de retorno
| Parámetro | Tipo | Descripción |
|---|---|---|
model | str | Requerido, el mismo identificador de modelo utilizado para messages.create. |
system | str | list | None | Opcional, refleja el system que enviarías en la solicitud real. |
tools | list | None | Opcional, refleja las tools que enviarías; los catálogos de herramientas grandes cuentan para input_tokens. |
messages | list | Requerido, la misma lista de mensajes que enviarías a messages.create. |
count.input_tokens | int | El valor de retorno: recuento exacto de tokens de entrada para la forma de solicitud dada. |
Errores comunes
- Olvidar pasar
systemytoolsacount_tokens. Si tu solicitud real incluye un prompt del sistema o herramientas, omitirlos de la llamadacount_tokenssubestima significativamente. Solución: siempre llama acount_tokenscon elsystem/tools/messagesidéntico que estás a punto de enviar. - Asumir que
count_tokensestima la salida. Solo devuelveinput_tokens, no hay campo de salida, porque la salida no existe hasta que ocurre la generación. Solución: usamax_tokens(peor caso) o un promedio móvil (caso típico) para el lado de la salida. - Tratar el costo total del peor caso como el costo esperado. Una puerta de presupuesto construida sobre
max_tokensa menudo rechazará solicitudes que en la práctica habrían terminado muy por debajo del presupuesto. Solución: usa la puerta del peor caso para la aplicación estricta del presupuesto, pero rastrea los costos reales por separado para informes realistas. - Codificar tarifas en línea en lugar de en una tabla compartida. Dispersar literales de
2.00y10.00en la base de código hace que una actualización de precios sea propensa a errores. Solución: manténRATEScomo una única fuente de verdad, idealmente cargada desde la configuración en lugar de un literal de Python, para que pueda actualizarse sin un despliegue de código. - No volver a verificar el presupuesto después de una reversión de modelo. Si tu pipeline reintenta una solicitud rechazada en un modelo más barato, omitir una segunda llamada a
estimate_costsignifica que confías en que la reversión encaja sin verificarlo. Solución: vuelve a ejecutar la estimación para el modelo de reversión, no asumas simplemente que "modelo más barato" siempre significa "dentro del presupuesto". - Ignorar que las propias tarifas de precios de
count_tokenspueden cambiar. Una tabla de tarifas integrada en el momento de la creación se desviará silenciosamente de la realidad después de una actualización de precios. Solución: revisa la tabla de tarifas según un horario, o mejor aún, extráela de una fuente de configuración que controles independientemente de los despliegues de código.
Alternativas
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
Puerta count_tokens pre-solicitud (esta página) | Necesitas rechazar o redirigir antes de pagar por una llamada | La latencia de la propia verificación de la puerta es más importante que la precisión del presupuesto |
Registro de usage post-solicitud solamente | Quieres visibilidad de costos sin bloquear ninguna solicitud | Necesitas un límite de presupuesto estricto que nunca se pueda exceder |
| Estimación aproximada de tokens por recuento de caracteres | Necesitas una estimación rápida y aproximada sin un viaje de ida y vuelta a la API | Necesitas cifras exactas para una decisión de presupuesto estricta |
| Límites de gasto en el lado del servidor (a nivel de plataforma) | Quieres un respaldo de cuenta completo independiente del código de la aplicación | Necesitas granularidad por solicitud o por inquilino |
Preguntas frecuentes
¿Llamar a count_tokens cuesta algo?
No, es una operación de medición que no ejecuta el modelo ni genera salida, por lo que no consume tokens de entrada o salida facturables.
¿Puede count_tokens decirme cuántos tokens de salida usará una solicitud?
No, los tokens de salida no existen hasta que ocurre la generación. Lo mejor que puedes hacer antes de la llamada es una estimación del peor caso usando max_tokens, o una estimación típica a partir de promedios históricos.
¿Por qué mi estimación de costo del peor caso parece mucho más alta que lo que realmente se me facturó?
Porque asume que se utilizó el límite completo de max_tokens como salida, que es un límite superior real, no una predicción del uso real. La mayoría de las finalizaciones terminan muy por debajo de su límite de max_tokens.
¿Debería cada solicitud en mi pipeline pasar por esta puerta?
Es más valioso para solicitudes de tamaño impredecible, como documentos proporcionados por el usuario, y menos necesario para llamadas internas de forma fija donde ya sabes que el recuento de tokens es pequeño y estable.
¿Qué sucede si omito system o tools de la llamada count_tokens?
Los input_tokens devueltos subestimarán, ya que solo reflejan lo que realmente pasaste. Siempre refleja la forma exacta de la solicitud que pretendes enviar.
¿Es seguro usar un estimador de promedio móvil para un límite de presupuesto estricto?
No por sí solo, ya que puede subestimar en una respuesta inusualmente larga. Úsalo para informes o advertencias suaves, y mantén el peor caso de max_tokens para cualquier aplicación estricta.
¿Con qué frecuencia debo actualizar la tabla RATES?
Cada vez que cambien los precios anunciados, y especialmente en torno a fechas de transición conocidas como la expiración de los precios de introducción. Almacenar las tarifas en una configuración externa en lugar de en el código hace que esto sea una actualización de datos, no un despliegue.
¿Puedo usar este mismo patrón para comparar costos entre modelos antes de elegir uno?
Sí, llama a estimate_cost una vez por cada modelo candidato con la misma forma de solicitud y compara los totales, es una extensión directa de este patrón.
¿Esta calculadora tiene en cuenta los descuentos de caché de prompts?
No tal como está escrita, asume tokens de entrada a precio completo. Una versión consciente de la caché necesitaría saber qué parte del prefijo se espera que llegue a la caché, lo cual count_tokens por sí solo no puede decirte.
Relacionado
- Conceptos básicos de economía de tokens - los patrones subyacentes de
count_tokensy uso en los que se basa esta calculadora. - Cómo funciona realmente la fijación de precios de tokens de Claude - por qué la tabla de tarifas tiene precios de entrada y salida separados por modelo.
- Niveles de modelos: Enrutamiento de tareas simples a Haiku, tareas difíciles a Opus - uso de una estimación de costos como una entrada para una decisión de enrutamiento.
- Economía de API por lotes: Cuándo el 50 por ciento de descuento supera las llamadas en tiempo real - ampliación de este tipo de calculadora para comparar precios síncronos vs. por lotes.
Versiones de 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, precios y versiones de SDK cambian rápidamente; verifica los detalles actuales en platform.claude.com/docs antes de confiar en ellos.