API Abierta

Exportar detalles de uso

Guía de exportación de uso de LMU AI: inicia sesión para obtener un JWT, luego llama a /api/v1/usage con paginación, rango de fechas y filtrado por modelo, con ejemplos en tres lenguajes.

Inicia sesión con tu cuenta y contraseña para obtener un JWT, luego usa ese JWT para llamar a /api/v1/usage y obtener la tabla completa de detalles de uso de tokens, idéntica a la página /usage de la consola.

Ideal para scripts de larga duración que automatizan la exportación, la conciliación y la carga en un BI empresarial — sin necesidad de descargar un CSV manualmente cada vez.

¿Por qué no puedes obtenerlo directamente con una clave API sk-...? La clave solo sirve para llamadas de negocio a través del gateway (/v1/messages, etc.) y no expone consultas de detalle a nivel de cuenta — esto es un diseño de seguridad deliberado: si una clave se filtra, no querrás que se vuelque toda tu factura. Los datos a nivel de cuenta deben usar autenticación de cuenta (JWT).


1. Resumen de la API

ElementoValor
URL basehttps://api.lmuai.com
AutenticaciónAuthorization: Bearer <access_token> (el JWT del inicio de sesión de la cuenta)
Validez del access token86400 segundos (24 horas), renovable con refresh_token
Formato de respuestaJSON; code: 0 significa éxito, en caso de fallo code != 0 con un campo message
Uso recomendadoExportación automatizada de facturación, carga en un BI empresarial, scripts de conciliación

2. Paso 1 — Inicia sesión para obtener un JWT

POST /api/v1/auth/login

POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json

{
  "email": "your@email.com",
  "password": "your-password"
}

Respuesta (extracto):

{
  "code": 0,
  "message": "success",
  "data": {
    "access_token": "eyJhbGc...",
    "refresh_token": "rt_756a220ffa...",
    "expires_in": 86400,
    "token_type": "Bearer",
    "user": { "id": 3, "email": "...", "balance": 1575.13 }
  }
}

Todas las solicitudes posteriores simplemente incluyen Authorization: Bearer <access_token>.


3. Paso 2 — Obtén los detalles de uso

GET /api/v1/usage

GET /api/v1/usage?page=1&page_size=200&start_date=2026-06-01&end_date=2026-06-23&timezone=Asia/Shanghai
Authorization: Bearer <access_token>

Parámetros de consulta

ParámetroTipoRequeridoPredeterminadoDescripción
pageintNo1Número de página, empezando desde 1
page_sizeintNo20Filas por página, se recomienda 200 (los valores excesivos se truncan)
start_datestringNoFecha de inicio YYYY-MM-DD, interpretada según timezone
end_datestringNoFecha final (incluye ese día)
timezonestringNoUTCZona horaria para interpretar los parámetros de fecha, p. ej. Asia/Shanghai; muy recomendable, de lo contrario los límites no coincidirán con tu hora local
api_key_idintNoVer los detalles de una sola clave (debe pertenecer al usuario actual)
modelstringNoFiltrar por ID de modelo (p. ej. claude-sonnet-5)
streamboolNotrue solo para streaming, false solo para no streaming
billing_typeintNoEnum de tipo de facturación (ver más abajo)
sort_bystringNocreated_atCampo de ordenamiento
sort_orderstringNodescasc o desc

Estructura de la respuesta

{
  "code": 0,
  "message": "success",
  "data": {
    "items": [ { /* UsageLog */ }, ... ],
    "total": 30423,
    "page": 1,
    "page_size": 200,
    "pages": 153
  }
}

Los campos de paginación están en el nivel superior de data, no en el envoltorio exterior. El arreglo de detalles está en data.itemsdata en sí no es un arreglo.

Mapeo de campos de un solo registro

Estos corresponden exactamente a cada columna de la página /usage de la consola:

Columna de la consolaCampo JSONTipoDescripción
ModelomodelstringEl ID del modelo realmente facturado
Esfuerzo de razonamientoreasoning_effortstring | nulllow / medium / high / xhigh / max
Endpoint de entradainbound_endpointstring | nullLa ruta que llamó el cliente, p. ej. /v1/messages
Endpoint upstreamupstream_endpointstring | nullLa ruta upstream normalizada
Tipo de solicitudrequest_typestringchat / stream / responses / messages, etc.
StreamingstreamboolSi fue streaming
Modo de facturaciónbilling_modestringtoken / per_request / image / subscription
Tipo de facturaciónbilling_typeintEnum de tipo de facturación
Multiplicadorrate_multiplierfloatEl multiplicador del canal aplicado a esta solicitud
Tokens de entradainput_tokensint
Tokens de salidaoutput_tokensint
Creación de cachécache_creation_tokensintTodas las escrituras de caché
Creación de caché 5mcache_creation_5m_tokensintNivel efímero de 5 minutos de Anthropic
Creación de caché 1hcache_creation_1h_tokensintNivel de 1 hora de Anthropic
Lectura de cachécache_read_tokensint
Costo de entradainput_costfloatUSD
Costo de salidaoutput_costfloatUSD
Costo de creación de cachécache_creation_costfloatUSD
Costo de lectura de cachécache_read_costfloatUSD
Total a precio de listatotal_costfloatUSD, antes del multiplicador
Cargo realactual_costfloat= total_cost × rate_multiplier, lo que realmente se descuenta de tu saldo
Latencia del primer tokenfirst_token_msint | nullTTFT, válido para streaming
Duración totalduration_msint | null
Horacreated_atISO8601Hora de escritura del lado del servidor
ID de solicitudrequest_idstringPara resolución de problemas y trazabilidad
Generación de imágenesimage_count / image_size / image_output_tokens / image_output_costSe completa solo para solicitudes multimodales
API Keyapi_key_id + objeto anidado api_keyint + obj
Grupogroup_id + objeto anidado groupint + obj

Cada registro lleva objetos anidados user / api_key / group (unos +1KB por fila). El objeto api_key contiene el texto completo de la clave — asegúrate de redactarlo en los logs / capturas de pantalla / CSVs de tus scripts; para exportaciones masivas, si no necesitas los objetos anidados, usa jq o mapeo de campos en el cliente para conservar solo los campos que necesitas.

Enum de tipo de facturación (billing_type)

ValorSignificado
0Facturado por token (la gran mayoría de las solicitudes)
1Descontado de la cuota de una suscripción

El campo billing_mode es la versión en cadena de texto, que describe el modelo de precios específico (token / per_request / image / subscription, etc.); es más legible y se recomienda para la categorización.


4. Renovación del token

El access token expira después de 24 horas. Hay dos formas de renovarlo:

Renovar con refresh_token (recomendado para scripts)

POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refresh_token": "rt_756a220ffa..."
}

Devuelve un nuevo access_token (y normalmente también rota el refresh_token). El refresh token es válido durante 30 días.

Iniciar sesión de nuevo

Rudimentario pero simple, adecuado para scripts de un solo uso: llama a /auth/login de nuevo antes de cada ejecución.


5. Ejemplos de código

Bash + curl + jq

#!/usr/bin/env bash
set -e

EMAIL="${EMAIL:?set EMAIL env}"
PASSWORD="${PASSWORD:?set PASSWORD env}"
BASE="https://api.lmuai.com"

JWT=$(curl -s -X POST "$BASE/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" \
  | jq -r '.data.access_token')

[ -n "$JWT" ] && [ "$JWT" != "null" ] || { echo "login failed" >&2; exit 1; }

START="2026-06-01"; END="2026-06-30"; TZ="Asia/Shanghai"; PAGE_SIZE=200

# Fetch page 1 to get total
META=$(curl -s "$BASE/api/v1/usage?page=1&page_size=$PAGE_SIZE&start_date=$START&end_date=$END&timezone=$TZ" \
  -H "Authorization: Bearer $JWT")
PAGES=$(echo "$META" | jq -r '.data.pages')

# CSV header
echo "created_at,model,request_type,input_tokens,output_tokens,cache_read_tokens,total_cost,actual_cost,duration_ms" > usage.csv

# Write page 1
echo "$META" | jq -r '.data.items[] | [.created_at, .model, .request_type, .input_tokens, .output_tokens, .cache_read_tokens, .total_cost, .actual_cost, .duration_ms] | @csv' >> usage.csv

# Fetch remaining pages
for ((p=2; p<=PAGES; p++)); do
  curl -s "$BASE/api/v1/usage?page=$p&page_size=$PAGE_SIZE&start_date=$START&end_date=$END&timezone=$TZ" \
    -H "Authorization: Bearer $JWT" \
    | jq -r '.data.items[] | [.created_at, .model, .request_type, .input_tokens, .output_tokens, .cache_read_tokens, .total_cost, .actual_cost, .duration_ms] | @csv' >> usage.csv
done

echo "saved $(wc -l < usage.csv) lines to usage.csv"

Python

import os, csv, requests

BASE = "https://api.lmuai.com"
EMAIL = os.environ["EMAIL"]
PASSWORD = os.environ["PASSWORD"]

def login() -> str:
    r = requests.post(f"{BASE}/api/v1/auth/login",
                      json={"email": EMAIL, "password": PASSWORD},
                      timeout=15)
    r.raise_for_status()
    data = r.json()
    if data["code"] != 0:
        raise RuntimeError(data.get("message", "login failed"))
    return data["data"]["access_token"]

def fetch_usage(jwt: str, **params):
    """Yield each row from paginated /usage endpoint."""
    page = 1
    while True:
        r = requests.get(f"{BASE}/api/v1/usage",
                         headers={"Authorization": f"Bearer {jwt}"},
                         params={**params, "page": page, "page_size": 200},
                         timeout=30)
        r.raise_for_status()
        d = r.json()["data"]
        for row in d.get("items", []):
            yield row
        if page >= d.get("pages", 1):
            return
        page += 1

if __name__ == "__main__":
    jwt = login()
    cols = ["created_at","model","request_type","input_tokens","output_tokens",
            "cache_read_tokens","total_cost","actual_cost","duration_ms"]
    with open("usage.csv", "w", newline="") as f:
        w = csv.writer(f); w.writerow(cols)
        for row in fetch_usage(jwt, start_date="2026-06-01", end_date="2026-06-30",
                               timezone="Asia/Shanghai"):
            w.writerow([row.get(c) for c in cols])
    print("done")

Node.js

import fs from 'node:fs'

const BASE = 'https://api.lmuai.com'
const { EMAIL, PASSWORD } = process.env

async function login() {
  const r = await fetch(`${BASE}/api/v1/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
  })
  const j = await r.json()
  if (j.code !== 0) throw new Error(j.message || 'login failed')
  return j.data.access_token
}

async function* fetchUsage(jwt, params) {
  for (let page = 1; ; page++) {
    const q = new URLSearchParams({ ...params, page, page_size: '200' })
    const r = await fetch(`${BASE}/api/v1/usage?${q}`, {
      headers: { Authorization: `Bearer ${jwt}` },
    })
    const j = await r.json()
    for (const row of j.data.items) yield row
    if (page >= j.data.pages) return
  }
}

const jwt = await login()
const cols = ['created_at','model','request_type','input_tokens','output_tokens',
              'cache_read_tokens','total_cost','actual_cost','duration_ms']
const out = fs.createWriteStream('usage.csv')
out.write(cols.join(',') + '\n')
for await (const row of fetchUsage(jwt, {
  start_date: '2026-06-01', end_date: '2026-06-30', timezone: 'Asia/Shanghai',
})) {
  out.write(cols.map(c => JSON.stringify(row[c] ?? '')).join(',') + '\n')
}
console.log('done')

6. Notas prácticas

  1. Agrega un retraso entre cambios de página masivos — al paginar N páginas seguidas, se recomienda sleep(0.1~0.3s) para evitar activar los límites de tasa.
  2. No obtengas un rango de tiempo demasiado largo de una vez — abarcar varios meses y millones de filas es lento. Segmentar por mes es más estable.
  3. Se recomienda un page_size de 200 — el servidor trunca los valores excesivos.
  4. Ahorra ancho de banda — los objetos anidados user / api_key / group son grandes; si solo quieres las filas de detalle, fíltralas con jq en el cliente.
  5. Errores comunes:
    • 401 JWT expirado → renueva con refresh_token o inicia sesión de nuevo
    • 403 Acceso no autorizado (p. ej. consultar la clave de otra persona vía api_key_id) → verifica que la clave pertenezca al usuario actual
    • 400 Parámetro incorrecto (p. ej. formato de start_date) → verifica que YYYY-MM-DD + timezone sean válidos

7. Recomendaciones de seguridad (lectura obligatoria)

Tu contraseña de cuenta + JWT equivalen a acceso completo a la cuenta. Antes de scriptear una exportación, asegúrate de:

ElementoRecomendación
Gestión de credencialesNunca comprometas la contraseña / access_token / refresh_token en git o en los logs de CI; usa variables de entorno, un gestor de secretos o .env (con .gitignore)
Seguridad de la cuentaSe recomienda encarecidamente habilitar la autenticación de dos factores TOTP (habilítala en la configuración de la cuenta). Esta API admite la verificación TOTP durante el inicio de sesión
Rotación periódicaCambia tu contraseña de cuenta regularmente; rota el refresh_token inmediatamente después de que expire
Monitoreo de anomalíasVigila el "último inicio de sesión / IP" de la cuenta; cambia tu contraseña de inmediato si detectas un dispositivo desconocido
Redacción de camposEl api_key.key anidado (texto completo sk-...), user.email y refresh_token en la respuesta son todos campos sensibles — redáctalos antes de la salida / logs / CSV
Privilegio mínimoSi puedes separarlas, usa una subcuenta dedicada para el script de exportación (si tu organización tiene una estructura multicuenta) para evitar la exposición prolongada de la cuenta principal
Reintentos ante erroresNo reintentes indefinidamente ante un fallo de inicio de sesión — es fácil que active los controles de riesgo; se recomienda backoff exponencial

8. Referencia rápida de campos

Útil para el mapeo de columnas de CSV:

EtiquetaCampoUnidad
Modelomodel
Esfuerzo de razonamientoreasoning_effort
Endpoint de entradainbound_endpointruta
Endpoint upstreamupstream_endpointruta
Tipo de solicitudrequest_typeenum
Streamingstreambool
Modo de facturaciónbilling_modeenum
Tipo de facturaciónbilling_typeint
Multiplicadorrate_multiplierfloat
Tokens de entradainput_tokensint
Tokens de salidaoutput_tokensint
Tokens de lectura de cachécache_read_tokensint
Tokens de escritura de cachécache_creation_tokensint
Escritura de caché 5mcache_creation_5m_tokensint
Escritura de caché 1hcache_creation_1h_tokensint
Costo de entradainput_costUSD
Costo de salidaoutput_costUSD
Costo de creación de cachécache_creation_costUSD
Costo de lectura de cachécache_read_costUSD
Total a precio de listatotal_costUSD
Cargo realactual_costUSD
Primer tokenfirst_token_msms
Duración totalduration_msms
Horacreated_atISO8601
ID de solicitudrequest_idstring
Grupogroup_id / group.nameint / string
API Keyapi_key_id / api_key.nameint / string

Última actualización:

En esta página