LMU AI Docs
Open API

Export Usage Details

LMU AI Usage export guide: log in for a JWT, then call /api/v1/usage with pagination, date range and model filtering, with examples in three languages.

Log in with your account and password to get a JWT, then use that JWT to call /api/v1/usage and pull the full token usage details table, identical to the console /usage page.

Ideal for long-running scripts that automate export, reconciliation, and loading into enterprise BI — no need to manually download a CSV each time.

Why can't you pull it directly with an sk-... API key? The key is only for gateway business calls (/v1/messages, etc.) and does not expose account-level detail queries — this is a deliberate security design: if a key leaks, you don't want your entire bill dumped. Account-level data must use account authentication (JWT).


1. API overview

ItemValue
Base URLhttps://api.lmuai.com
AuthenticationAuthorization: Bearer <access_token> (the JWT from account login)
Access token validity86400 seconds (24 hours), renewable with refresh_token
Response formatJSON; code: 0 means success, on failure code != 0 with a message field
Recommended useAutomated billing export, loading into enterprise BI, reconciliation scripts

2. Step 1 — Log in to get a 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"
}

Response (excerpt):

{
  "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 }
  }
}

All subsequent requests just include Authorization: Bearer <access_token>.


3. Step 2 — Pull the usage details

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>

Query parameters

ParameterTypeRequiredDefaultDescription
pageintNo1Page number, starting from 1
page_sizeintNo20Rows per page, 200 recommended (oversized values are truncated)
start_datestringNoStart date YYYY-MM-DD, parsed by timezone
end_datestringNoEnd date (inclusive of that day)
timezonestringNoUTCTimezone for parsing the date parameters, e.g. Asia/Shanghai; strongly recommended, otherwise the boundaries won't match your local time
api_key_idintNoView only one key's details (must belong to the current user)
modelstringNoFilter by model ID (e.g. claude-sonnet-5)
streamboolNotrue for streaming only, false for non-streaming only
billing_typeintNoBilling type enum (see below)
sort_bystringNocreated_atSort field
sort_orderstringNodescasc or desc

Response structure

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

The pagination fields are at the top level of data, not in the outer envelope. The details array is at data.itemsdata is not itself an array.

Field mapping for a single record

These correspond exactly to each column of the console /usage page:

Console columnJSON fieldTypeDescription
ModelmodelstringThe model ID actually billed
Reasoning effortreasoning_effortstring | nulllow / medium / high / xhigh / max
Inbound endpointinbound_endpointstring | nullThe path the client called, e.g. /v1/messages
Upstream endpointupstream_endpointstring | nullThe normalized upstream path
Request typerequest_typestringchat / stream / responses / messages, etc.
StreamingstreamboolWhether it was streaming
Billing modebilling_modestringtoken / per_request / image / subscription
Billing typebilling_typeintBilling type enum
Multiplierrate_multiplierfloatThe channel multiplier applied to this request
Input tokensinput_tokensint
Output tokensoutput_tokensint
Cache creationcache_creation_tokensintAll cache writes
Cache creation 5mcache_creation_5m_tokensintAnthropic ephemeral 5-minute tier
Cache creation 1hcache_creation_1h_tokensintAnthropic 1-hour tier
Cache readcache_read_tokensint
Input costinput_costfloatUSD
Output costoutput_costfloatUSD
Cache creation costcache_creation_costfloatUSD
Cache read costcache_read_costfloatUSD
List-price totaltotal_costfloatUSD, before the multiplier
Actual chargeactual_costfloat= total_cost × rate_multiplier, what is actually deducted from your balance
First-token latencyfirst_token_msint | nullTTFT, valid for streaming
Total durationduration_msint | null
Timecreated_atISO8601Server-side write time
Request IDrequest_idstringFor troubleshooting and tracing
Image generationimage_count / image_size / image_output_tokens / image_output_costPopulated only for multimodal requests
API Keyapi_key_id + nested api_key objectint + obj
Groupgroup_id + nested group objectint + obj

Each record carries nested user / api_key / group objects (about +1KB per row). The api_key object contains the full key text — be sure to redact it in script logs / screenshots / CSVs; for bulk export, if you don't need the nested objects, use jq or field mapping on the client to keep only the fields you need.

Billing type enum (billing_type)

ValueMeaning
0Billed by token (the vast majority of requests)
1Deducted from a subscription's quota

The billing_mode field is the string version, describing the specific pricing model (token / per_request / image / subscription, etc.); it is more readable and recommended for categorization.


4. Token renewal

The access token expires after 24 hours. There are two ways to renew it:

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

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

Returns a new access_token (and usually rotates the refresh_token too). The refresh token is valid for 30 days.

Log in again

Crude but simple, good for one-off scripts: call /auth/login again before each run.


5. Code examples

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. Practical notes

  1. Add a delay between bulk page turns — when paging through N pages in a row, sleep(0.1~0.3s) is recommended to avoid triggering rate limits.
  2. Don't pull too long a time range at once — spanning several months and millions of rows is slow. Slicing by month is more stable.
  3. page_size of 200 recommended — the server truncates oversized values.
  4. Save bandwidth — the nested user / api_key / group objects are large; if you only want the detail rows, filter with jq on the client.
  5. Common errors:
    • 401 JWT expired → renew with refresh_token or log in again
    • 403 Unauthorized access (e.g. querying someone else's key via api_key_id) → check that the key belongs to the current user
    • 400 Bad parameter (e.g. start_date format) → check that YYYY-MM-DD + timezone are valid

7. Security recommendations (must read)

Your account password + JWT are equivalent to full account access. Before scripting an export, make sure:

ItemRecommendation
Credential managementNever commit password / access_token / refresh_token to git or CI logs; use environment variables, a secrets manager, or .env (with .gitignore)
Account securityStrongly recommend enabling TOTP two-factor authentication (enable it in account settings). This API supports TOTP verification during login
Regular rotationChange your account password regularly; rotate the refresh_token immediately after it expires
Anomaly monitoringWatch the account's "last login time / IP"; change your password immediately if you spot an unfamiliar device
Field redactionThe nested api_key.key (full sk-... text), user.email, and refresh_token in the response are all sensitive fields — redact them before output / logs / CSV
Least privilegeIf you can separate them, use a dedicated sub-account for the export script (if your organization has a multi-account structure) to avoid long-term exposure of the main account
Error retriesDon't retry indefinitely on login failure — it easily triggers risk controls; exponential backoff is recommended

8. Field quick reference

Handy for CSV column mapping:

LabelFieldUnit
Modelmodel
Reasoning effortreasoning_effort
Inbound endpointinbound_endpointpath
Upstream endpointupstream_endpointpath
Request typerequest_typeenum
Streamingstreambool
Billing modebilling_modeenum
Billing typebilling_typeint
Multiplierrate_multiplierfloat
Input tokensinput_tokensint
Output tokensoutput_tokensint
Cache read tokenscache_read_tokensint
Cache write tokenscache_creation_tokensint
Cache write 5mcache_creation_5m_tokensint
Cache write 1hcache_creation_1h_tokensint
Input costinput_costUSD
Output costoutput_costUSD
Cache creation costcache_creation_costUSD
Cache read costcache_read_costUSD
List-price totaltotal_costUSD
Actual chargeactual_costUSD
First tokenfirst_token_msms
Total durationduration_msms
Timecreated_atISO8601
Request IDrequest_idstring
Groupgroup_id / group.nameint / string
API Keyapi_key_id / api_key.nameint / string

Last updated:

On this page