MandAPI
Español ▾
Claves y consumo

Exporta los eventos de uso de tu aplicación a CSV

Convierte registros JSONL propios en un informe mínimo sin confundirlo con una factura de la plataforma.

Usa esta guía con tu asistente de programación

Leer las instrucciones
Crea una exportación CSV sin conexión de los registros usage-events.jsonl de mi propia aplicación. Usa el esquema de seguimiento conocido y columnas permitidas explícitas. Excluye prompts, respuestas, claves, identificadores de usuarios y campos arbitrarios; deja vacío el uso ausente, valida enteros no negativos y fechas con zona, y neutraliza celdas que parezcan fórmulas. Marca los registros como uso de aplicación, no facturación. No inventes un endpoint de exportación, API pública de análisis, botón del panel ni importes monetarios de MandAPI. Conserva el origen y evita sobrescribir salidas. Valida dos eventos sintéticos con uso ausente, un campo extra similar a un secreto y texto con aspecto de fórmula.
TXT

Elige registros que controles

Esta receta exporta el archivo usage-events.jsonl creado por tu aplicación en la guía de seguimiento. Genera un CSV para revisión operativa. No es una factura, una exportación completa de la cuenta MandAPI ni una reconstrucción de cargos reales.

Utiliza las vistas de registros y estadísticas de tu cuenta para consultar la actividad de la plataforma. Este script trabaja solo con registros de tu aplicación; no utiliza un botón de exportación de la plataforma ni un endpoint público de exportación. Consulta los créditos reales en el panel de MandAPI.

Guarda y prueba el exportador

Guárdalo como export_activity.py. Solo necesita la biblioteca estándar de Python. Empieza con --demo: no lee datos de una cuenta ni realiza solicitudes de red.

python
import argparse
import csv
from datetime import datetime
import json
from pathlib import Path

FIELDS = ("record_type", "time", "request_id", "feature", "model", "status",
          "prompt_tokens", "completion_tokens", "duration_ms")
NUMBERS = {"prompt_tokens", "completion_tokens", "duration_ms"}

def safe_text(value):
    if not isinstance(value, str) or len(value) > 200:
        raise ValueError("Expected a short text field")
    text = value.replace("\r", " ").replace("\n", " ")
    return "'" + text if text.lstrip().startswith(("=", "+", "-", "@")) else text

def clean(event):
    if not isinstance(event, dict) or event.get("status") not in {"ok", "error"}:
        raise ValueError("Invalid event status")
    timestamp = datetime.fromisoformat(event["time"])
    if timestamp.tzinfo is None:
        raise ValueError("Timestamp must include a time zone")
    row = {}
    for field in FIELDS:
        if field == "record_type":
            row[field] = "application_usage_not_billing"
        elif field in NUMBERS:
            value = event.get(field)
            if value is not None and (type(value) is not int or value < 0):
                raise ValueError("Token and duration fields must be nonnegative integers")
            row[field] = "" if value is None else value
        else:
            row[field] = safe_text(event.get(field, ""))
    return row

parser = argparse.ArgumentParser()
parser.add_argument("source", nargs="?")
parser.add_argument("--output", default="usage-activity.csv")
parser.add_argument("--demo", action="store_true")
args = parser.parse_args()
if args.demo:
    events = [
        {"time": "2026-10-30T09:00:00+00:00", "request_id": "demo-1", "feature": "support", "model": "MODEL_ID", "status": "ok", "prompt_tokens": 12, "completion_tokens": 5, "duration_ms": 400},
        {"time": "2026-10-30T09:01:00+00:00", "request_id": "demo-2", "feature": "=2+2", "status": "error", "duration_ms": 200, "api_key": "DROP_THIS_FIELD"},
    ]
else:
    if not args.source:
        parser.error("Provide your own usage-events.jsonl or choose --demo")
    with Path(args.source).open(encoding="utf-8") as handle:
        events = [json.loads(line) for line in handle if line.strip()]
rows = [clean(event) for event in events]
with Path(args.output).open("x", newline="", encoding="utf-8-sig") as handle:
    writer = csv.DictWriter(handle, fieldnames=FIELDS)
    writer.writeheader()
    writer.writerows(rows)
print(json.dumps({"rows": len(rows), "output": args.output, "billing_report": False}))
powershell
python export_activity.py --demo --output usage-demo.csv

La demostración crea dos filas. La solicitud fallida deja vacíos los tokens porque su uso es desconocido; vacío no equivale a cero. El campo adicional api_key queda fuera de las columnas permitidas y el valor de función con aspecto de fórmula se escribe como texto.

El archivo de salida no debe existir. Comprueba el resultado en el programa de hojas de cálculo que utilices: añadir un prefijo a posibles fórmulas es una protección básica, no sustituye el tratamiento cuidadoso de archivos no confiables.

Exporta tu propio registro

Utiliza una copia local con registros que tengas derecho a procesar. El ejemplo de seguimiento usa fechas con zona horaria, un ID propio por solicitud, estado y tokens cuando fueron devueltos.

powershell
python export_activity.py usage-events.jsonl --output usage-activity.csv

Solo se exportan las columnas indicadas. Se omiten prompts, respuestas, claves, identificadores de usuarios y campos adicionales. El script rechaza estados no válidos, fechas sin zona y tokens negativos o no enteros. No modifica el registro original.

Para servicios grandes, exporta una consulta autorizada y limitada de tu base de datos en lugar de cargar un archivo sin límite. Define quién puede descargar el informe y durante cuánto tiempo se conserva.

Concilia y comprueba el resultado

Compara las filas con el intervalo seleccionado. Los eventos ausentes, reintentos, uso de streaming incompleto y solicitudes de otras aplicaciones pueden dejar fuera actividad. Los tokens de una respuesta no establecen por sí solos el crédito cobrado.

El CSV de demostración debe tener dos filas de datos, la marca application_usage_not_billing y tokens vacíos para el fallo. Comprueba que no aparece el campo inesperado api_key. Investiga diferencias de facturación con registros reales del panel; no calcules importes suponiendo que el uso desconocido es cero.

Referencias

Temas inspirados en documentación pública; instrucciones y ejemplos adaptados para MandAPI.