# تصدير تفاصيل الاستخدام

> دليل تصدير استخدام LMU AI: سجّل الدخول للحصول على JWT، ثم استدعِ /api/v1/usage مع الترقيم والنطاق الزمني وتصفية النموذج، مع أمثلة بثلاث لغات.

URL: https://docs.lmuai.com/ar/docs/api/usage-export



سجّل الدخول بحسابك وكلمة المرور للحصول على JWT، ثم استخدم هذا الـ JWT لاستدعاء `/api/v1/usage` وسحب **جدول تفاصيل استخدام الرموز الكامل، المطابق لصفحة `/usage` في وحدة التحكم**.

مثالي للسكربتات طويلة الأمد التي تؤتمت التصدير والمطابقة والتحميل إلى أنظمة BI المؤسسية — دون الحاجة إلى تنزيل ملف CSV يدويًا في كل مرة.

<Callout type="info">
  **لماذا لا يمكنك سحبها مباشرةً باستخدام مفتاح API من نوع `sk-...`؟** المفتاح مخصص فقط لاستدعاءات أعمال البوابة (/v1/messages، إلخ) ولا يكشف استعلامات التفاصيل على مستوى الحساب — وهذا تصميم أمني متعمّد: إذا تسرّب مفتاح، فأنت لا تريد أن تُكشف فاتورتك بالكامل. يجب أن تستخدم البيانات على مستوى الحساب مصادقة الحساب (JWT).
</Callout>

***

## 1. نظرة عامة على الواجهة [#1-نظرة-عامة-على-الواجهة]

| العنصر               | القيمة                                                                 |
| -------------------- | ---------------------------------------------------------------------- |
| Base URL             | `https://api.lmuai.com`                                                |
| المصادقة             | `Authorization: Bearer <access_token>` (الـ JWT من تسجيل دخول الحساب)  |
| صلاحية رمز الوصول    | **86400 ثانية (24 ساعة)**، قابل للتجديد باستخدام refresh\_token        |
| تنسيق الاستجابة      | JSON؛ `code: 0` يعني النجاح، وعند الفشل `code != 0` مع حقل `message`   |
| الاستخدام المُوصى به | تصدير الفوترة المؤتمت، التحميل إلى أنظمة BI المؤسسية، سكربتات المطابقة |

***

## 2. الخطوة 1 — سجّل الدخول للحصول على JWT [#2-الخطوة-1--سجّل-الدخول-للحصول-على-jwt]

### `POST /api/v1/auth/login` [#post-apiv1authlogin]

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

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

**الاستجابة** (مقتطف):

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

جميع الطلبات اللاحقة تتضمّن فقط `Authorization: Bearer <access_token>`.

***

## 3. الخطوة 2 — اسحب تفاصيل الاستخدام [#3-الخطوة-2--اسحب-تفاصيل-الاستخدام]

### `GET /api/v1/usage` [#get-apiv1usage]

```http
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>
```

### معاملات الاستعلام [#معاملات-الاستعلام]

| المعامل        | النوع  | مطلوب | الافتراضي    | الوصف                                                                                                                  |
| -------------- | ------ | ----- | ------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `page`         | int    | لا    | 1            | رقم الصفحة، يبدأ من 1                                                                                                  |
| `page_size`    | int    | لا    | 20           | عدد الصفوف لكل صفحة، يُوصى بـ 200 (القيم المفرطة تُقتطع)                                                               |
| `start_date`   | string | لا    | —            | تاريخ البداية `YYYY-MM-DD`، يُحلَّل حسب `timezone`                                                                     |
| `end_date`     | string | لا    | —            | تاريخ النهاية (شامل ذلك اليوم)                                                                                         |
| `timezone`     | string | لا    | UTC          | المنطقة الزمنية لتحليل معاملات التاريخ، مثل `Asia/Shanghai`؛ **يُوصى بها بشدة**، وإلا فلن تتطابق الحدود مع وقتك المحلي |
| `api_key_id`   | int    | لا    | —            | عرض تفاصيل مفتاح واحد فقط (يجب أن يكون ملكًا للمستخدم الحالي)                                                          |
| `model`        | string | لا    | —            | التصفية حسب معرّف النموذج (مثل `claude-sonnet-5`)                                                                      |
| `stream`       | bool   | لا    | —            | `true` للبثّي فقط، `false` لغير البثّي فقط                                                                             |
| `billing_type` | int    | لا    | —            | قائمة تعداد نوع الفوترة (انظر أدناه)                                                                                   |
| `sort_by`      | string | لا    | `created_at` | حقل الترتيب                                                                                                            |
| `sort_order`   | string | لا    | `desc`       | `asc` أو `desc`                                                                                                        |

### بنية الاستجابة [#بنية-الاستجابة]

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

<Callout type="warn">
  **حقول الترقيم موجودة في المستوى الأعلى من `data`، وليست في الغلاف الخارجي**. مصفوفة التفاصيل موجودة في `data.items` — أي أن `data` ليست مصفوفة بحدّ ذاتها.
</Callout>

### تعيين الحقول لسجل واحد [#تعيين-الحقول-لسجل-واحد]

تقابل هذه بالضبط كل عمود من أعمدة صفحة `/usage` في وحدة التحكم:

| عمود وحدة التحكم                | حقل JSON                                                                   | النوع          | الوصف                                                          |
| ------------------------------- | -------------------------------------------------------------------------- | -------------- | -------------------------------------------------------------- |
| **النموذج**                     | `model`                                                                    | string         | معرّف النموذج الذي تمّت فوترته فعليًا                          |
| **جهد الاستدلال**               | `reasoning_effort`                                                         | string \| null | `low` / `medium` / `high` / `xhigh` / `max`                    |
| **نقطة النهاية الواردة**        | `inbound_endpoint`                                                         | string \| null | المسار الذي استدعاه العميل، مثل `/v1/messages`                 |
| **نقطة النهاية العلوية**        | `upstream_endpoint`                                                        | string \| null | المسار العلوي المُطبَّع                                        |
| **نوع الطلب**                   | `request_type`                                                             | string         | `chat` / `stream` / `responses` / `messages`، إلخ              |
| **البثّ**                       | `stream`                                                                   | bool           | هل كان بثيًا                                                   |
| **وضع الفوترة**                 | `billing_mode`                                                             | string         | `token` / `per_request` / `image` / `subscription`             |
| **نوع الفوترة**                 | `billing_type`                                                             | int            | قائمة تعداد نوع الفوترة                                        |
| **المضاعِف**                    | `rate_multiplier`                                                          | float          | مضاعِف القناة المُطبَّق على هذا الطلب                          |
| **رموز الإدخال**                | `input_tokens`                                                             | int            |                                                                |
| **رموز الإخراج**                | `output_tokens`                                                            | int            |                                                                |
| **إنشاء الذاكرة المؤقتة**       | `cache_creation_tokens`                                                    | int            | جميع عمليات الكتابة إلى الذاكرة المؤقتة                        |
| **إنشاء الذاكرة المؤقتة 5m**    | `cache_creation_5m_tokens`                                                 | int            | طبقة Anthropic العابرة لمدة 5 دقائق                            |
| **إنشاء الذاكرة المؤقتة 1h**    | `cache_creation_1h_tokens`                                                 | int            | طبقة Anthropic لمدة ساعة واحدة                                 |
| **قراءة الذاكرة المؤقتة**       | `cache_read_tokens`                                                        | int            |                                                                |
| **تكلفة الإدخال**               | `input_cost`                                                               | float          | USD                                                            |
| **تكلفة الإخراج**               | `output_cost`                                                              | float          | USD                                                            |
| **تكلفة إنشاء الذاكرة المؤقتة** | `cache_creation_cost`                                                      | float          | USD                                                            |
| **تكلفة قراءة الذاكرة المؤقتة** | `cache_read_cost`                                                          | float          | USD                                                            |
| **الإجمالي بسعر القائمة**       | `total_cost`                                                               | float          | USD، قبل المضاعِف                                              |
| **الرسم الفعلي**                | `actual_cost`                                                              | float          | **= total\_cost × rate\_multiplier**، ما يُخصم فعليًا من رصيدك |
| **زمن الرمز الأول**             | `first_token_ms`                                                           | int \| null    | TTFT، صالح للبثّ                                               |
| **المدة الإجمالية**             | `duration_ms`                                                              | int \| null    |                                                                |
| **الوقت**                       | `created_at`                                                               | ISO8601        | وقت الكتابة على جانب الخادم                                    |
| **معرّف الطلب**                 | `request_id`                                                               | string         | لاستكشاف الأخطاء وتتبعها                                       |
| **توليد الصور**                 | `image_count` / `image_size` / `image_output_tokens` / `image_output_cost` | —              | يُملأ فقط للطلبات متعددة الوسائط                               |
| **مفتاح API**                   | `api_key_id` + كائن `api_key` المتداخل                                     | int + obj      |                                                                |
| **المجموعة**                    | `group_id` + كائن `group` المتداخل                                         | int + obj      |                                                                |

<Callout type="warn">
  يحمل كل سجل كائنات `user` / `api_key` / `group` متداخلة (حوالي +1KB لكل صف). **يحتوي كائن `api_key` على النص الكامل للمفتاح — احرص على إخفائه في سجلات السكربت / لقطات الشاشة / ملفات CSV**؛ وللتصدير بالجملة، إذا لم تكن بحاجة إلى الكائنات المتداخلة، استخدم `jq` أو تعيين الحقول على جانب العميل للاحتفاظ فقط بالحقول التي تحتاجها.
</Callout>

### قائمة تعداد نوع الفوترة (billing\_type) [#قائمة-تعداد-نوع-الفوترة-billing_type]

| القيمة | المعنى                                      |
| ------ | ------------------------------------------- |
| `0`    | الفوترة بالرمز (الغالبية العظمى من الطلبات) |
| `1`    | خصم من حصة الاشتراك                         |

حقل `billing_mode` هو النسخة النصية، ويصف نموذج التسعير المحدد (`token` / `per_request` / `image` / `subscription`، إلخ)؛ وهو أسهل قراءةً ويُوصى به للتصنيف.

***

## 4. تجديد الرمز [#4-تجديد-الرمز]

تنتهي صلاحية رمز الوصول بعد 24 ساعة. هناك طريقتان لتجديده:

### التجديد باستخدام refresh\_token (يُوصى به للسكربتات) [#التجديد-باستخدام-refresh_token-يُوصى-به-للسكربتات]

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

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

يُعيد `access_token` جديدًا (وعادةً يُدوّر `refresh_token` أيضًا). صلاحية رمز التحديث 30 يومًا.

### تسجيل الدخول مجددًا [#تسجيل-الدخول-مجددًا]

طريقة بدائية لكنها بسيطة، مناسبة للسكربتات لمرة واحدة: استدعِ `/auth/login` مجددًا قبل كل تشغيل.

***

## 5. أمثلة برمجية [#5-أمثلة-برمجية]

### Bash + curl + jq [#bash--curl--jq]

```bash
#!/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 [#python]

```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 [#nodejs]

```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. ملاحظات عملية [#6-ملاحظات-عملية]

1. **أضف تأخيرًا بين تقليب الصفحات بالجملة** — عند تصفح N صفحة على التوالي، يُوصى بـ `sleep(0.1~0.3s)` لتجنّب تفعيل حدود المعدل.
2. **لا تسحب نطاقًا زمنيًا طويلًا جدًا دفعة واحدة** — امتداد عدة أشهر وملايين الصفوف بطيء. التقطيع حسب الشهر أكثر استقرارًا.
3. **يُوصى بـ `page_size` قدره 200** — يقتطع الخادم القيم المفرطة.
4. **وفّر عرض النطاق الترددي** — الكائنات المتداخلة `user` / `api_key` / `group` كبيرة؛ إذا كنت تريد صفوف التفاصيل فقط، صفّها باستخدام `jq` على جانب العميل.
5. **الأخطاء الشائعة**:
   * `401` انتهت صلاحية الـ JWT → جدّد باستخدام `refresh_token` أو سجّل الدخول مجددًا
   * `403` وصول غير مصرّح به (مثل الاستعلام عن مفتاح شخص آخر عبر `api_key_id`) → تحقّق من أن المفتاح يخصّ المستخدم الحالي
   * `400` معامل خاطئ (مثل تنسيق `start_date`) → تحقّق من صحة `YYYY-MM-DD` + `timezone`

***

## 7. توصيات الأمان (يجب قراءتها) [#7-توصيات-الأمان-يجب-قراءتها]

<Callout type="warn">
  **كلمة مرور حسابك + الـ JWT تعادلان الوصول الكامل إلى الحساب.** قبل كتابة سكربت للتصدير، تأكّد من:
</Callout>

| العنصر                     | التوصية                                                                                                                                                   |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| إدارة بيانات الاعتماد      | **لا تُدرج أبدًا** كلمة المرور / access\_token / refresh\_token في git أو سجلات CI؛ استخدم متغيرات البيئة، أو مدير أسرار، أو `.env` (مع `.gitignore`)     |
| أمان الحساب                | يُوصى بشدة بتفعيل **المصادقة الثنائية TOTP** (فعّلها في إعدادات الحساب). تدعم هذه الواجهة التحقق عبر TOTP أثناء تسجيل الدخول                              |
| التدوير الدوري             | غيّر كلمة مرور حسابك بانتظام؛ ودوّر refresh\_token فورًا بعد انتهاء صلاحيته                                                                               |
| مراقبة الشذوذ              | راقب "آخر وقت / عنوان IP لتسجيل الدخول" في الحساب؛ وغيّر كلمة المرور فورًا إذا لاحظت جهازًا غير مألوف                                                     |
| إخفاء الحقول               | إن `api_key.key` المتداخل (نص sk-... الكامل)، و`user.email`، و`refresh_token` في الاستجابة كلها حقول حساسة — **أخفِها قبل الإخراج / السجلات / ملفات CSV** |
| أقل امتياز                 | إن أمكنك الفصل، **استخدم حسابًا فرعيًا مخصصًا لسكربت التصدير** (إذا كانت لمؤسستك بنية متعددة الحسابات) لتجنّب التعرّض طويل الأمد للحساب الرئيسي           |
| إعادة المحاولة عند الأخطاء | لا تُعِد المحاولة إلى ما لا نهاية عند فشل تسجيل الدخول — فهذا يفعّل ضوابط المخاطر بسهولة؛ يُوصى بالتراجع الأسّي                                           |

***

## 8. مرجع سريع للحقول [#8-مرجع-سريع-للحقول]

مفيد لتعيين أعمدة CSV:

| التسمية                     | الحقل                         | الوحدة       |
| --------------------------- | ----------------------------- | ------------ |
| النموذج                     | `model`                       | —            |
| جهد الاستدلال               | `reasoning_effort`            | —            |
| نقطة النهاية الواردة        | `inbound_endpoint`            | path         |
| نقطة النهاية العلوية        | `upstream_endpoint`           | path         |
| نوع الطلب                   | `request_type`                | enum         |
| البثّ                       | `stream`                      | bool         |
| وضع الفوترة                 | `billing_mode`                | enum         |
| نوع الفوترة                 | `billing_type`                | int          |
| المضاعِف                    | `rate_multiplier`             | float        |
| رموز الإدخال                | `input_tokens`                | int          |
| رموز الإخراج                | `output_tokens`               | int          |
| رموز قراءة الذاكرة المؤقتة  | `cache_read_tokens`           | int          |
| رموز كتابة الذاكرة المؤقتة  | `cache_creation_tokens`       | int          |
| كتابة الذاكرة المؤقتة 5m    | `cache_creation_5m_tokens`    | int          |
| كتابة الذاكرة المؤقتة 1h    | `cache_creation_1h_tokens`    | int          |
| تكلفة الإدخال               | `input_cost`                  | USD          |
| تكلفة الإخراج               | `output_cost`                 | USD          |
| تكلفة إنشاء الذاكرة المؤقتة | `cache_creation_cost`         | USD          |
| تكلفة قراءة الذاكرة المؤقتة | `cache_read_cost`             | USD          |
| الإجمالي بسعر القائمة       | `total_cost`                  | USD          |
| الرسم الفعلي                | `actual_cost`                 | USD          |
| الرمز الأول                 | `first_token_ms`              | ms           |
| المدة الإجمالية             | `duration_ms`                 | ms           |
| الوقت                       | `created_at`                  | ISO8601      |
| معرّف الطلب                 | `request_id`                  | string       |
| المجموعة                    | `group_id` / `group.name`     | int / string |
| مفتاح API                   | `api_key_id` / `api_key.name` | int / string |
