Cómo Contar Tokens y Estimar los Costos de las APIs de LLM
Cuenta tokens de LLM con precisión y estima costos de API con los tokenizers de OpenAI, Claude, Gemini y Llama, además de contexto y facturación.
Para contar tokens con precisión, procesa el cuerpo completo de la petición a través del tokenizador del modelo que realmente estás invocando, y luego estima el costo como (input_tokens ÷ 1.000.000) × input_rate + (output_tokens ÷ 1.000.000) × output_rate, tomando las tarifas actuales de la página de precios del proveedor.
Nadie calcula esto por adelantado. El tema surge la mañana en que llega la factura, o la tarde en que una conversación larga empieza a lanzar errores de ventana de contexto a usuarios reales, y de repente “¿cuántos tokens son estos?” es la única pregunta que importa. Lo incómodo es que un token no es una palabra, el conteo depende del modelo, y la mitad de lo que se te factura nunca aparece en tu cadena de prompt.
Este artículo te da el método repetible: cuándo basta con una estimación aproximada, cómo obtener un conteo exacto por proveedor, qué se factura realmente, y cómo convertir los conteos en una proyección de costos que sobreviva a un cambio de precios.
Puntos Clave
- Cuenta tokens con el tokenizador del modelo que estás invocando: tiktoken para OpenAI,
messages.countTokenspara Claude,countTokenspara Gemini, y el propio tokenizador del modelo en Hugging Face para Llama. - Las heurísticas como caracteres ÷ 4 son aceptables para planificación de capacidad, pero nunca para facturación; fallan con código, JSON, texto no inglés y emojis.
- El prompt facturado es el cuerpo completo de la petición, incluyendo el system prompt, el encuadre de roles, los esquemas de herramientas y el historial de conversación reenviado, no solo el mensaje del usuario.
- Los conteos de tokens de entrada son deterministas, los de salida no: muestrea entre 50 y 200 peticiones reales, planifica el costo a partir de la longitud media de salida y define
max_tokensa partir del percentil 95. - El costo estimado por petición es (input_tokens ÷ 1M) × input_rate + (output_tokens ÷ 1M) × output_rate, con las tarifas leídas en vivo desde la página de precios del proveedor.
¿Por Qué un Token No Es una Palabra?
Un token es una unidad de texto específica de cada modelo, producida por un tokenizador de subpalabras, y no se corresponde ni con palabras ni con caracteres. Los tokenizadores basados en byte pair encoding, como tiktoken de OpenAI, fusionan secuencias de caracteres frecuentes en tokens únicos y dividen las palabras poco comunes en varias piezas. La palabra “idempotency” se codifica en cuatro tokens (“id”, “emp”, “ot”, “ency”) con cl100k_base, la codificación de la era GPT-4, y en tres (“id”, “empot”, “ency”) con o200k_base, la codificación que usan los modelos actuales de OpenAI.
Ese último punto es el que importa para la facturación: la división es específica de cada modelo. La misma frase produce conteos distintos con los tokenizadores de GPT, Claude, Gemini y Llama, porque cada uno fue entrenado con datos diferentes y un vocabulario diferente. Cualquier conteo hecho con el tokenizador equivocado es una conjetura.
¿Cuándo Basta con una Estimación Aproximada?
Para prosa en inglés, caracteres ÷ 4 o palabras × 1,33 te acerca lo suficiente como para dimensionar una columna de base de datos o esbozar un plan de capacidad. Usa heurísticas para planificación de capacidad, nunca para facturación ni para decisiones sobre la ventana de contexto.
Las heurísticas fallan exactamente donde vive el tráfico de producción: código, JSON, texto no inglés y emojis. Las cargas útiles estructuradas se tokenizan según patrones de puntuación y espacios en blanco que el conteo de caracteres ignora, y un solo emoji puede expandirse en varios tokens, así que caracteres ÷ 4 subestima gravemente las cadenas con muchos emojis. Las diferencias entre tokenizadores, que se mantienen moderadas con prosa en inglés, crecen de forma significativa con código y datos estructurados, que es precisamente el contenido que envía un resumidor o un agente.
¿Qué Contador de Tokens LLM Da un Conteo Exacto?
El principio cabe en una línea: cuenta con el tokenizador que pertenece al modelo que estás invocando. Las rutas por proveedor:
| Proveedor | Ruta para conteo exacto |
|---|---|
| OpenAI | tiktoken, o js-tiktoken en Node y entornos de ejecución edge |
| Anthropic | el endpoint count-tokens, client.messages.countTokens() en el SDK de TypeScript |
| Gemini | ai.models.countTokens() en el SDK @google/genai |
| Llama y otros modelos abiertos | el propio tokenizador del modelo publicado en Hugging Face |
En JavaScript, js-tiktoken es un port en JS puro, así que no hay binario WASM que cargar ni memoria que liberar manualmente, y puedes importar una sola codificación en lugar del conjunto completo, lo que mantiene el bundle pequeño:
import { Tiktoken } from "js-tiktoken/lite";
import o200k_base from "js-tiktoken/ranks/o200k_base";
const enc = new Tiktoken(o200k_base);
const count = enc.encode("Summarise this ticket thread for support.").length;
El endpoint de Anthropic es gratuito de invocar, sujeto únicamente a sus propios límites de tasa, así que no hay excusa de costo para aproximar los conteos de Claude con el tokenizador de otro proveedor. Trata su resultado como el conteo previo autorizado, no como uno exacto: Anthropic lo documenta como una estimación, y la cifra facturada proviene de los campos de uso de la respuesta. Los tokenizadores también cambian entre generaciones de modelos dentro de un mismo proveedor. La documentación de conteo de tokens de Anthropic sitúa a Claude 4.7 y posteriores en un tokenizador más reciente que convierte el mismo texto en aproximadamente un 30 por ciento más tokens de lo que hacían los modelos Claude anteriores, y la diferencia exacta depende de tu contenido. Un conteo antiguo no es transferible; haz uno nuevo con el modelo que realmente estás invocando. Y cuando solo quieras el número sin cablear un SDK, pega el prompt en un contador de tokens LLM que cubra GPT, Claude, Gemini y Llama.
¿Por Qué Mi Conteo No Coincide con la Factura?
El prompt facturado es el cuerpo completo de la petición, no la cadena que escribiste. El encuadre de roles, el system prompt, los esquemas de herramientas y funciones, y los separadores por mensaje suman tokens, y por eso contar solo el mensaje del usuario siempre subestima. Una única definición de herramienta puede añadir cientos de tokens de entrada a cada petición que la incluya.
El historial de conversación es el multiplicador. Una función de chat reenvía el historial completo en cada turno, así que la entrada de cada turno incluye todos los turnos anteriores, y el costo por conversación crece de forma superlineal con la longitud de la conversación. La solución para el conteo es simple: ensambla el array de mensajes exacto, el system prompt y las herramientas que vas a enviar, y cuenta eso. El endpoint count-tokens de Anthropic acepta la misma carga útil que habrías enviado para crear el mensaje, definiciones de herramientas incluidas, así que puedes pasarle directamente la petición ensamblada.
¿Cómo Convierto los Conteos de Tokens en una Estimación de Costo?
El costo estimado por petición es una línea de aritmética, mantenida en forma simbólica:
cost = (input_tokens / 1_000_000) * input_rate + (output_tokens / 1_000_000) * output_rate
Donde los proveedores admiten caché de prompts, los tokens de entrada cacheados se facturan a una cached_input_rate separada y más baja. Los precios por modelo cambian en cuestión de semanas, así que aquí no se imprime ninguna tarifa. Trata las tarifas como configuración inyectada en tu código, lee los valores actuales desde la página de precios del proveedor, y usa una calculadora de costos de LLM para comparar cifras actuales entre modelos.
Dos hechos condicionan toda estimación. Primero, los tokens de salida suelen tener una tarifa considerablemente más alta que los de entrada en los principales proveedores, así que la longitud de la respuesta a menudo domina el costo. Segundo, los conteos de entrada son deterministas mientras que los de salida no lo son: la misma petición siempre cuenta igual al entrar, pero lo que vuelve varía según el muestreo. Mide la salida empíricamente. Ejecuta entre 50 y 200 peticiones representativas, planifica el costo a partir de la longitud media de salida, y define max_tokens a partir del percentil 95 para que las respuestas legítimas no se trunquen mientras las generaciones descontroladas quedan acotadas.
¿Cómo Sé si un Prompt Cabe en la Ventana de Contexto?
Los tokens de entrada más los tokens de salida esperados deben caber dentro de la ventana de contexto del modelo, o la llamada falla directamente o la respuesta se trunca. La comprobación previa pertenece a tu wrapper de peticiones: reparte el presupuesto de la ventana entre el contexto del sistema, el historial de conversación y el margen para la salida, cuenta la petición ensamblada, y recorta el historial antes de enviar en lugar de después de un error. Un verificador de ventana de contexto te dice si un prompt dado cabe en un modelo dado sin tener que memorizar tamaños de ventana que cambian con cada lanzamiento.
La ubicación del wrapper importa porque un desbordamiento es visible para el usuario: una respuesta truncada o un error a mitad del stream, y el reflejo del usuario es reintentar, así que un bug de presupuesto de tokens factura el doble. Las repeticiones de sesión de funciones respaldadas por LLM revelan exactamente ese bucle de reintentos, mucho antes de que aparezca en una factura revisada mensualmente.
Qué Registrar en Producción
El método es estable aunque los precios no lo sean: cuenta la petición ensamblada con el propio tokenizador del modelo invocado, muestrea tráfico real para conocer tu distribución de salida, y mantén las tarifas como configuración que actualizas desde las páginas de precios. Luego cierra el ciclo en producción. Todos los proveedores principales devuelven conteos reales de tokens en los campos de uso de la respuesta, como usage.input_tokens de Anthropic y usageMetadata de Gemini, aunque la nueva API de Interactions de Gemini, todavía en Beta, devuelve usage con total_input_tokens y total_output_tokens. Regístralos por petición desde el primer día; guardarlos es trivial, reconstruirlos después de que llegue la factura sorpresa no lo es.
Preguntas Frecuentes
¿Puedo usar tiktoken para contar tokens de modelos Claude o Gemini?
No. El tokenizador de cada proveedor tiene su propio vocabulario, así que un conteo con tiktoken solo es válido para modelos de OpenAI y puede divergir sustancialmente sobre la misma entrada para Claude o Gemini. Usa el endpoint count-tokens de Anthropic, que es gratuito de invocar, para Claude; el método countTokens del SDK @google/genai para Gemini; y el tokenizador publicado en Hugging Face para modelos abiertos como Llama.
¿Cuál es la diferencia entre los paquetes npm tiktoken y js-tiktoken?
tiktoken es un binding de WASM: carga un binario compilado y requiere llamar a free() para liberar la memoria del encoder cuando terminas. js-tiktoken es un port en JavaScript puro con métodos en camelCase (getEncoding, encodingForModel), sin binario WASM y sin gestión manual de memoria, lo que lo convierte en la opción más segura para entornos edge y serverless. Importar un único archivo de ranks de codificación mantiene pequeño su tamaño de bundle.
¿Las respuestas en streaming siguen reportando el uso de tokens?
Sí, pero no por defecto en todas partes. Para Chat Completions de OpenAI, define stream_options con include_usage en true y la API transmite un chunk final adicional cuyo campo usage cubre la petición completa y cuyo array choices está vacío. Anthropic transmite el uso automáticamente: el evento message_start lleva input_tokens y los eventos message_delta llevan output_tokens acumulados. Registra estos campos en lugar de contar tú mismo los chunks transmitidos.
¿Qué codificación de tiktoken debo usar para cada modelo de OpenAI?
Usa o200k_base para los modelos actuales de OpenAI como gpt-4o y posteriores, y cl100k_base solo para modelos de la era GPT-4. Las dos codificaciones dividen el texto de forma distinta, así que un conteo hecho con una no es transferible a la otra. Dado un ID de modelo, encodingForModel en js-tiktoken selecciona la codificación correspondiente por ti, lo que evita fijar la codificación equivocada a medida que los modelos cambian.