Respuesta corta: la forma más sencilla y fiable de facturar a cada cliente de un producto de IA multi-tenant por su uso de modelos es asignar a cada tenant (o a cada nivel de tenant) su propia API key. Configure en esa clave un tope de gasto en USD y un tope de concurrencia, y registre el cargo exacto que devuelve el gateway en cada solicitud en lugar de estimarlo a partir del número de tokens. En HopBase, cada respuesta no streaming facturada incluye X-HopBase-Cost-USD y usage.cost_usd. Cada tarea de video o de imagen finalizada informa usage.cost_usd, y GET /v1/usage devuelve el saldo que le queda a una clave. Cuando el tope de un tenant se agota, sus solicitudes reciben 402 insufficient_quota mientras los demás tenants siguen funcionando. Una clave que supera su tope de concurrencia recibe 429 apikey_concurrency_limit, de modo que un solo cliente ruidoso no puede agotar su capacidad. Las respuestas en streaming no llevan cabecera de costo; concílielas con la exportación CSV por clave, donde cada fila puede recalcularse.
El problema: una sola factura del proveedor, muchos clientes
Si opera una plataforma de agentes de IA, un SaaS vertical con funciones de IA o un servicio de IA de marca blanca, su proveedor de modelos le envía una sola factura. Cada uno de sus clientes espera la suya. Entre ambas surge una serie de preguntas que todo equipo termina teniendo que responder:
- ¿Cuánto nos costó el tenant A este mes, por modelo?
- ¿Cómo evitamos que el bucle descontrolado de un agente de un tenant consuma el presupuesto de todos?
- ¿Cómo mostramos a un cliente su crédito restante en nuestro propio panel?
- Cuando finanzas lo pida, ¿podemos justificar cada línea de una factura?
El número de tokens por sí solo no responde a estas preguntas. Los precios varían según el modelo, la entrada en caché es más barata, algunos modelos facturan los tokens de razonamiento como salida, otros cobran más por encima de cierto umbral de contexto y el video se factura por segundo o por token de video. Si multiplica usted mismo los tokens por una tabla de precios, esa tabla se queda desactualizada con cada lanzamiento de modelo.
Tres arquitecturas comparadas
| Enfoque | Cómo se separan los tenants | De dónde sale la cifra de costo | Aplicación de límites |
|---|---|---|---|
| Etiquetar solicitudes y llevar un libro contable propio | Un ID de tenant en su aplicación; una clave compartida del proveedor | Su propia tabla de precios × número de tokens | Su código, antes de cada llamada |
| Proxy autoalojado | Claves virtuales o un campo de ID de cliente en el proxy | La tabla de precios del proxy | El proxy |
| Una clave por tenant en un gateway que informa los cargos reales | Una clave del gateway por tenant o por nivel | El cargo del gateway por cada solicitud | El gateway: tope de gasto y tope de concurrencia por clave |
Las tres funcionan. El resto de esta guía trata la tercera, porque elimina las dos partes más difíciles: mantener correcta una tabla de precios y aplicar los límites antes de que salga una solicitud.
Paso 1: una clave por tenant, con topes
En la consola de HopBase, abra API Keys y cree una clave para cada tenant. Las claves se crean y editan en la consola, no mediante una API pública, así que conviene incluir la creación de la clave como un paso del alta de cada tenant. Tres ajustes por clave hacen la mayor parte del trabajo:
- Grupos: deciden qué modelos puede llamar la clave. Una clave incluye de 1 a 10 grupos de distintas familias, de modo que un tenant «Basic» puede tener solo DeepSeek y Qwen, mientras que un tenant «Pro» también obtiene Claude y video. Un modelo fuera de los grupos de la clave devuelve 404
model_not_found. - Cuota: es un tope de gasto en USD para la clave. Cuando se agota, las solicitudes de la clave devuelven 402
insufficient_quotacon «API key quota exhausted». Las demás claves de la cuenta no se ven afectadas. - Concurrencia: es el número de solicitudes que pueden estar en curso en esta clave. Por encima del tope, las solicitudes reciben 429
apikey_concurrency_limitconRetry-After: 1. Déjelo vacío si no desea un tope a nivel de clave. El tope de la cuenta se sigue aplicando por encima: por defecto son 5 solicitudes en curso por cuenta, y puede ampliarse bajo solicitud.
Guarde la clave cifrada en su tabla de tenants y nunca la exponga en el navegador del tenant. Su backend realiza la llamada al modelo en nombre del tenant.
Paso 2: registrar el cargo real de cada solicitud
En las llamadas síncronas (chat, imágenes, voz), una respuesta facturada devuelve el importe descontado de su saldo:
- La cabecera
X-HopBase-Cost-USD, por ejemplo0.012. - En el JSON no streaming, el mismo valor como
usage.cost_usd, junto a los recuentos de tokens habituales.
Un envoltorio ligero convierte esto en un asiento del libro contable:
import os, sqlite3
from openai import OpenAI
db = sqlite3.connect("ledger.db")
db.execute("""CREATE TABLE IF NOT EXISTS usage_ledger (
ts TEXT DEFAULT CURRENT_TIMESTAMP, tenant_id TEXT, model TEXT,
prompt_tokens INT, completion_tokens INT, cost_usd REAL, request_id TEXT)""")
def client_for(tenant):
# tenant["hopbase_key"] is that tenant's own key, decrypted server-side
return OpenAI(base_url="https://api.hop-base.com/v1", api_key=tenant["hopbase_key"])
def chat(tenant, model, messages, **kw):
raw = client_for(tenant).chat.completions.with_raw_response.create(
model=model, messages=messages, **kw)
resp = raw.parse()
cost = float(raw.headers.get("X-HopBase-Cost-USD") or 0)
db.execute("INSERT INTO usage_ledger (tenant_id, model, prompt_tokens, completion_tokens, cost_usd, request_id) VALUES (?,?,?,?,?,?)",
(tenant["id"], model, resp.usage.prompt_tokens,
resp.usage.completion_tokens, cost, resp.id))
db.commit()
return resp
Algunas reglas sobre esta cifra que importan para la facturación:
- Las solicitudes fallidas que no produjeron salida no se facturan y no llevan cabecera de costo.
- Si el gateway reintenta una solicitud internamente, solo se factura el intento que se completó, y ese es el importe informado. Una clave con varios grupos se cobra según el grupo que atendió la solicitud.
- Si su cliente se desconecta antes de tiempo, la salida ya generada se factura igualmente. Ese cargo aparece en los registros de Usage de la consola aunque usted nunca haya visto la cabecera.
- Las respuestas en streaming (SSE) no llevan cabecera de costo, porque las cabeceras se envían antes de que empiece la generación, y el evento final de uso del stream incluye los recuentos de tokens pero no el importe. Registre los recuentos de tokens (envíe
stream_options: {"include_usage": true}) como un asiento pendiente y liquide el importe con la exportación CSV (Paso 4).
Paso 3: tareas asíncronas de video e imagen
Las tareas de video se envían con POST /v1/video/generate y se consultan con GET /v1/video/tasks/{task_id}. Cuando una tarea llega a un estado final, la consulta de tarea individual incluye un objeto usage de nivel superior:
{
"id": "vt-1",
"status": "completed",
"usage": { "cost_usd": 0.5 }
}
Escriba usage.cost_usd en el libro contable cuando la tarea termine. Una tarea fallida que no se cobró informa 0. En una consulta de tarea individual, solo la clave que creó la tarea ve el campo. Los listados de tareas (GET /v1/video/tasks sin ID) abarcan toda la cuenta, incluidas las tareas creadas por otras claves, así que guarde cada ID de tarea junto con su tenant y nunca pase un listado sin filtrar a un tenant. Además, los envíos de video reservan su costo estimado por adelantado, de modo que un tenant cerca de su tope puede recibir 402 en el momento del envío y no a mitad del proceso.
Paso 4: conciliar y facturar
Al final de un periodo de facturación, exporte la página Usage de la consola como CSV, filtrada por clave y rango de fechas (hasta 50,000 filas y 400 días por archivo). Cada fila contiene la hora, el modelo, el uso, la moneda del precio oficial de lista, el costo oficial, el descuento, el tipo de cambio, el importe cobrado y una nota, de modo que cada línea puede recalcularse:
official cost × discount ÷ exchange rate = charged amount
Las solicitudes fallidas se marcan como no cobradas y las solicitudes interrumpidas se marcan como cobradas por la salida ya producida. Sume el importe cobrado por clave, compárelo con su libro contable y liquide los asientos de streaming del Paso 2. La consola también muestra gráficas de gasto y de tendencia de solicitudes por clave. Si trabaja con un cliente MCP, la herramienta de solo lectura get_usage devuelve los totales de solicitudes, tokens y costo de la clave que hace la llamada, por modelo, de los últimos 7 días por defecto y hasta 92 días.
Lo que cobre al tenant es decisión suya. Un patrón habitual es el costo más un margen, o un paquete de créditos con un precio superior al costo esperado. Como el libro contable contiene cargos reales, cualquiera de las dos opciones se reduce a una sola multiplicación.
Paso 5: mostrar a los tenants su crédito restante
Para mostrar en su panel una cifra en vivo de «crédito restante», llame a GET /v1/usage con la clave del tenant:
curl https://api.hop-base.com/v1/usage \
-H "Authorization: Bearer sk-tenant-key"
Para una clave con una cuota de $10 y $2 usados, balance es 8, quota.total es 10 y quota.used es 2, todo en USD. Si la clave no tiene cuota propia, balance es el saldo de la cuenta, así que configure una cuota en cada clave de tenant si piensa mostrar esta cifra. Una clave con cuota disponible se detiene igualmente con 402 si el saldo de la cuenta se agota antes. La llamada en sí no se factura.
Manejo de errores que respeta los límites entre tenants
- 402
insufficient_quota: se alcanzó el tope de este tenant o el saldo de su cuenta está vacío. ConsulteGET /v1/usagepara saber cuál de los dos, y muestre «crédito agotado» solo a ese tenant. - 429
apikey_concurrency_limit: este tenant superó su propio tope de concurrencia. Ponga en cola o reduzca el ritmo para este tenant sin ralentizar a los demás. - 429
user_concurrency_limit: toda su cuenta está en su tope. Esto requiere una conversación sobre capacidad, no un ajuste por tenant. - 404
model_not_found: el tenant pidió un modelo que no está en su plan. Conviértalo en una invitación a mejorar de plan.
Lista de verificación antes del lanzamiento
- Cada clave de tenant tiene una cuota y un tope de concurrencia.
- Los asientos del libro contable usan el cargo devuelto, nunca una tabla de precios local.
- Las solicitudes en streaming y las desconectadas se liquidan con la exportación CSV.
- Los cargos de las tareas asíncronas se registran cuando la tarea llega a un estado final, vinculados a los ID de tarea que guardó por tenant.
- La conciliación mensual compara los totales del libro contable por clave con la exportación.
Preguntas frecuentes
¿Cómo controlo el costo de LLM por cliente sin mantener una tabla de precios?
Lea el cargo que devuelve el gateway. En HopBase es la cabecera X-HopBase-Cost-USD o usage.cost_usd en las llamadas síncronas, y usage.cost_usd en las tareas asíncronas finalizadas. Cada uno es el importe exacto en USD descontado por esa solicitud.
¿Puedo limitar cuánto puede gastar un cliente?
Sí. Configure una cuota en la API key de ese cliente. Cuando se agota, las solicitudes de esa clave devuelven 402 insufficient_quota y las demás claves siguen funcionando.
¿Por qué no hay cabecera de costo en las respuestas en streaming?
Las cabeceras se envían antes de que comience la generación, por lo que el cargo final aún no se conoce. Registre los recuentos de tokens del evento final de uso del stream como un asiento pendiente y liquide el importe con los registros de Usage de la consola o con la exportación CSV.
¿Pueden distintos clientes tener acceso a distintos modelos?
Sí. Cada clave incluye solo los grupos que usted elija, de 1 a 10, de modo que el plan lo aplica la propia clave. Un modelo fuera de los grupos de la clave devuelve 404.
¿Dónde están los detalles?
Consulte API de cuenta y catálogo para los campos de costo y /v1/usage, Concurrencia, tiempos de espera y facturación para los topes, y nuestra guía de conciliación de uso para el método con CSV. Los precios de lista están en hop-base.com/pricing.