> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 日誌查詢 API

> 用程式拉取呼叫日誌，獲取每次請求的模型、實際扣費、耗時與錯誤碼，實現自動對賬與故障自查

## 介面概述

日誌查詢介面返回你賬號下**每一次 API 呼叫**的明細記錄，包括呼叫的模型、實際扣費、耗時、
是否流式、以及失敗時的錯誤碼。

它和[餘額查詢 API](/zh-Hant/api-capabilities/balance-query) 是互補的：餘額查詢告訴你「現在還剩多少錢」，
日誌查詢告訴你「錢花在哪了」。

三類典型用途：

<CardGroup cols={3}>
  <Card title="自動對賬" icon="calculator">
    按時間段、按模型統計實際消費，與自己的業務賬單核對
  </Card>

  <Card title="故障自查" icon="bug">
    查失敗請求的錯誤碼，定位是引數問題還是上游問題
  </Card>

  <Card title="報障提工單" icon="life-buoy">
    拿到 `request_id` 給客服，能精確定位到那一次呼叫
  </Card>
</CardGroup>

<Info>
  控制台也能看日誌（「日誌」頁面）。本介面是同一份資料的程式化入口，適合需要自動化對賬、
  定時匯出、或接入自己監控系統的場景。手動檢視請直接用控制台，見 [如何檢視我的呼叫記錄](/zh-Hant/faq/call-logs)。
</Info>

## 如何獲取系統令牌

日誌介面用**系統令牌**認證，與 API Key 不是一回事（詳見文末注意事項）。

<Steps>
  <Step title="訪問控制台">
    訪問 `api.apiyi.com/account/profile` 個人中心頁面
  </Step>

  <Step title="找到系統令牌">
    在頁面最下方找到「賬號選項 - 系統令牌」部分
  </Step>

  <Step title="生成 AccessToken">
    輸入當前的賬戶密碼後，會得到一個 AccessToken，該金鑰可用於後續介面的查詢資料
  </Step>
</Steps>

<img src="https://mintcdn.com/apiyillc/PXVoab-l7wSQlQVE/images/apiyi-system-accesstoken.png?fit=max&auto=format&n=PXVoab-l7wSQlQVE&q=85&s=eb4f48476a795dfa5bfd7cb053081bdc" alt="獲取系統令牌" width="1020" height="460" data-path="images/apiyi-system-accesstoken.png" />

## 介面資訊

| 專案        | 說明                                                    |
| --------- | ----------------------------------------------------- |
| **介面URL** | `https://api.apiyi.com/api/log/self`                  |
| **請求方法**  | `GET`                                                 |
| **認證方式**  | Authorization Header（直接填 token 字串，**不加 `Bearer` 字首**） |
| **響應格式**  | JSON（gzip 壓縮）                                         |
| **資料範圍**  | 僅本賬號自己的日誌                                             |

## 請求說明

### 請求 Headers

| Header名稱        | 必填 | 說明                       |
| --------------- | -- | ------------------------ |
| `Authorization` | 是  | 系統令牌，格式：直接填寫 token 字串    |
| `Accept`        | 否  | 建議設定為 `application/json` |

### 查詢引數

| 引數                | 型別      | 必填 | 說明                    |
| ----------------- | ------- | -- | --------------------- |
| `p`               | Integer | 否  | 頁碼，**從 0 開始**（不是 1）   |
| `page_size`       | Integer | 否  | 每頁條數，**上限 10**（見下方提示） |
| `type`            | Integer | 否  | 日誌型別，對賬請傳 `2`，取值見下表   |
| `model_name`      | String  | 否  | 按模型名精確過濾，如 `gpt-5.6`  |
| `token_name`      | String  | 否  | 按令牌名過濾                |
| `start_timestamp` | Integer | 否  | 起始時間，Unix 秒           |
| `end_timestamp`   | Integer | 否  | 結束時間，Unix 秒           |
| `group`           | String  | 否  | 按分組過濾                 |

<Warning>
  **`page_size` 的實際上限是 10。** 傳 `page_size=100` 也只會返回 10 條 —— 這不是報錯，
  很容易被誤認為「我這段時間只有 10 次呼叫」。**拉取任何有意義的時間段都必須翻頁**，
  翻到返回空陣列為止。下方的 Python 與 Node.js 示例已經處理好翻頁。
</Warning>

### 日誌型別 type

| 取值  | 含義     | 說明                         |
| --- | ------ | -------------------------- |
| `1` | 充值     | 記錄充值前後餘額，`quota` 為 0       |
| `2` | **消費** | **對賬只需要這一類**，`quota` 是實際扣費 |
| `3` | 管理     | 賬號資訊變更等操作記錄，`quota` 為 0    |
| `4` | 系統     | 系統贈送額度等，`quota` 為 0        |

<Warning>
  **統計花費時務必帶上 `type=2`。** 不傳 `type` 會把充值、系統贈送記錄一起返回，
  這些記錄的 `quota` 雖然是 0，但 `model_name` 與 `token_name` 也是空的，
  直接遍歷求和或按模型分組會得到錯誤結果。
</Warning>

## 響應說明

### 成功響應示例

```json theme={null}
{
  "success": true,
  "message": "",
  "data": [
    {
      "request_id": "2026080114481936471351696e93ae3FTV8WKfk",
      "created_at": 1785595715,
      "type": 2,
      "content": "模型固定價格 0.015，分組倍率 1",
      "username": "your-account",
      "token_name": "生產環境-主力",
      "token_group": "default",
      "model_name": "gpt-5.6",
      "quota": 7500,
      "prompt_tokens": 1000,
      "completion_tokens": 0,
      "duration_for_view": 16,
      "is_stream": false,
      "error_code": "",
      "other": "{\"billing_type\":\"by_count\",\"request_path\":\"/v1/images/generations\",\"group_ratio\":1,\"model_ratio\":1,\"usage\":{}}"
    }
  ]
}
```

### 關鍵響應欄位

| 欄位名                                   | 型別      | 說明                               |
| ------------------------------------- | ------- | -------------------------------- |
| `quota`                               | Integer | **本次實際扣費**（額度），÷ 500,000 = 美元    |
| `content`                             | String  | 人類可讀的計價說明，如「模型固定價格 0.015，分組倍率 1」 |
| `model_name`                          | String  | 實際計費的模型名                         |
| `token_name`                          | String  | 哪一把令牌發起的                         |
| `token_group`                         | String  | 令牌所屬分組（注意是分組標識，見常見問題）            |
| `prompt_tokens` / `completion_tokens` | Integer | 輸入 / 輸出 token 數                  |
| `duration_for_view`                   | Integer | 本次呼叫耗時（秒）                        |
| `is_stream`                           | Boolean | 是否為流式呼叫                          |
| `error_code`                          | String  | 失敗原因碼，成功時為空字串                    |
| `created_at`                          | Integer | 呼叫時間，Unix 秒                      |
| `request_id`                          | String  | **請求 ID，報障提工單時提供這個**             |
| `other`                               | String  | 計費與請求的補充資訊，**是 JSON 字串，需要二次解析**  |

<Info>
  `other` 欄位存的是一段 JSON **字串**而不是巢狀物件，取用前需要再解析一次
  （Python 用 `json.loads()`，JavaScript 用 `JSON.parse()`）。裡面包含
  `billing_type`（計費方式）、`request_path`（實際呼叫的端點）、`group_ratio`（分組倍率）、
  `model_ratio`（模型倍率）、`usage`（用量明細）等。
</Info>

### 額度換算

<Card title="換算規則" icon="calculator">
  500,000 額度 = \$1.00 美金 (USD)
</Card>

**計算公式：** 美金金額 = `quota` ÷ 500,000

**示例：**

* `quota: 7500` → \$0.015 USD
* `quota: 22500` → \$0.045 USD
* `quota: 18` → \$0.000036 USD

這與[餘額查詢 API](/zh-Hant/api-capabilities/balance-query) 是同一套換算口徑，可以直接對齊。

## 錯誤響應

### HTTP 401 - 認證失敗

```json theme={null}
{
  "success": false,
  "message": "You are not authorized to perform this operation. The access token is invalid."
}
```

**原因：** 系統令牌無效、已過期，或誤把 API Key（`sk-` 開頭）當成系統令牌使用。

**解決方法：** 回控制台重新生成系統令牌，確認 `Authorization` 裡填的是**不帶 `Bearer` 字首**的裸值。

## 程式碼示例

### cURL 示例（單頁，快速驗證）

```bash theme={null}
export APIYI_SYS_TOKEN='YOUR_SYSTEM_TOKEN'

curl --compressed -s 'https://api.apiyi.com/api/log/self?p=0&page_size=10&type=2' \
  -H "Authorization: $APIYI_SYS_TOKEN" \
  -H 'Accept: application/json' | jq '.data[] | {created_at, model_name, quota, request_id}'
```

<Warning>
  **必須新增 `--compressed` 選項**，因為 API 返回的是 gzip 壓縮內容，否則會得到亂碼。
</Warning>

<Info>
  這條命令只能拿到 10 條。真正對賬請用下面帶翻頁的完整版。
</Info>

### Python 示例（帶翻頁，可直接執行）

```python theme={null}
import json
import os
import time
from collections import defaultdict

import requests

BASE = "https://api.apiyi.com"
TOKEN = os.environ["APIYI_SYS_TOKEN"]
QUOTA_PER_USD = 500_000

HEADERS = {"Authorization": TOKEN, "Accept": "application/json"}


def fetch_logs(hours=24, model_name=None):
    """拉取最近 hours 小時的消費日誌，自動翻頁。"""
    now = int(time.time())
    params = {
        "page_size": 10,          # 服務端上限就是 10，傳更大也無效
        "type": 2,                # 2 = 消費，對賬只看這一類
        "start_timestamp": now - hours * 3600,
        "end_timestamp": now,
    }
    if model_name:
        params["model_name"] = model_name

    rows, seen = [], set()
    page = 0
    while True:
        resp = requests.get(f"{BASE}/api/log/self",
                            headers=HEADERS, params={**params, "p": page}, timeout=30)
        resp.raise_for_status()
        data = resp.json().get("data") or []
        if not data:
            break                 # 翻到空陣列即結束

        fresh = 0
        for row in data:
            # 部分記錄的 id 欄位恆為 0，用 request_id 去重更可靠
            key = row.get("request_id")
            if key in seen:
                continue
            seen.add(key)
            rows.append(row)
            fresh += 1
        if fresh == 0:
            break                 # 整頁都是重複，防禦性退出
        page += 1
    return rows


def summarize(rows):
    """按模型彙總呼叫次數與花費。"""
    stat = defaultdict(lambda: {"count": 0, "quota": 0})
    for row in rows:
        s = stat[row.get("model_name") or "(無)"]
        s["count"] += 1
        s["quota"] += row.get("quota") or 0

    total = sum(s["quota"] for s in stat.values())
    print(f"{'模型':32s} {'次數':>6s} {'花費(USD)':>12s}")
    for model, s in sorted(stat.items(), key=lambda kv: -kv[1]["quota"]):
        print(f"{model:32s} {s['count']:6d} {s['quota'] / QUOTA_PER_USD:12.4f}")
    print(f"\n合計 {len(rows)} 次呼叫，{total:,} 額度 = ${total / QUOTA_PER_USD:.4f} USD")


if __name__ == "__main__":
    logs = fetch_logs(hours=24)
    summarize(logs)

    # other 是 JSON 字串，需要二次解析
    if logs:
        extra = json.loads(logs[0].get("other") or "{}")
        print("\n最近一次呼叫的端點:", extra.get("request_path"))
```

**輸出示例：**

```
模型                                 次數      花費(USD)
gpt-5.6                              128       2.3850
gemini-3-pro-image                    30       1.3500
deepseek-chat                        412       0.0148

合計 570 次呼叫，1,867,400 額度 = $3.7348 USD
```

### Node.js 示例（帶翻頁）

```javascript theme={null}
const BASE = "https://api.apiyi.com";
const TOKEN = process.env.APIYI_SYS_TOKEN;
const QUOTA_PER_USD = 500_000;

async function fetchLogs({ hours = 24, modelName = null } = {}) {
  const now = Math.floor(Date.now() / 1000);
  const base = {
    page_size: "10",           // 服務端上限就是 10
    type: "2",                 // 2 = 消費
    start_timestamp: String(now - hours * 3600),
    end_timestamp: String(now),
  };
  if (modelName) base.model_name = modelName;

  const rows = [];
  const seen = new Set();
  for (let page = 0; ; page += 1) {
    const qs = new URLSearchParams({ ...base, p: String(page) });
    const resp = await fetch(`${BASE}/api/log/self?${qs}`, {
      headers: { Authorization: TOKEN, Accept: "application/json" },
    });
    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
    const { data } = await resp.json();
    if (!data || data.length === 0) break;

    let fresh = 0;
    for (const row of data) {
      // 用 request_id 去重，部分記錄的 id 恆為 0
      if (seen.has(row.request_id)) continue;
      seen.add(row.request_id);
      rows.push(row);
      fresh += 1;
    }
    if (fresh === 0) break;
  }
  return rows;
}

const logs = await fetchLogs({ hours: 24 });
const total = logs.reduce((sum, r) => sum + (r.quota || 0), 0);
console.log(`${logs.length} 次呼叫，合計 $${(total / QUOTA_PER_USD).toFixed(4)} USD`);
```

<Info>
  `fetch` 會自動處理 gzip 解壓，Node.js 側無需額外配置（Python 的 requests 同理）。
  只有 curl 需要顯式加 `--compressed`。
</Info>

## 典型場景

### 場景一：統計某段時間的花費

用上面的 Python 示例，關鍵是**帶 `type=2`** 並把 `quota` 求和後除以 500,000。
如果只想看某個模型，加 `model_name` 引數即可。

### 場景二：找出失敗的呼叫

```python theme={null}
failed = [r for r in fetch_logs(hours=24) if r.get("error_code")]
for r in failed:
    print(r["created_at"], r["model_name"], r["error_code"], r["request_id"])
```

<Info>
  被閘道拒絕的請求（引數錯誤等）`quota` 為 0，**不產生扣費**。日誌裡能看到 `error_code`，
  方便你區分「呼叫失敗了」和「呼叫成功但結果不滿意」。
</Info>

### 場景三：報障時提供 request\_id

在日誌裡定位到出問題的那次呼叫，把 `request_id` 提供給客服，可以精確查到該次請求的
完整鏈路。這比描述「大概幾點呼叫 xx 模型失敗了」高效得多。

## 常見問題

<AccordionGroup>
  <Accordion title="為什麼我只拿到 10 條記錄？">
    `page_size` 的服務端上限就是 10，傳更大的值不會報錯但也不會生效。
    需要拉更多資料必須翻頁（`p=0`、`p=1`、`p=2` …），翻到返回空陣列為止。
    上面的 Python 與 Node.js 示例已經封裝好這個邏輯。
  </Accordion>

  <Accordion title="響應裡有些欄位是空的，是不是出問題了？">
    不是。部分欄位屬於平臺內部資訊，普通賬號視角下為空或 0，屬正常現象，
    不影響對賬與排障需要的欄位（`quota`、`model_name`、`error_code`、`request_id` 等都是完整的）。
  </Accordion>

  <Accordion title="token_group 的值和控制台顯示的分組名不一樣？">
    介面返回的是分組的**識別符號**，控制台顯示的是分組的**展示名**，兩者可能不同。
    例如介面返回 `default`，控制台顯示「Default」。

    完整的對應關係可以從公開介面 `https://api.apiyi.com/api/pricing` 的 `usable_group`
    欄位獲取，它是一個「識別符號 → 展示名」的對映表。如果你要讓自己的報表和控制台顯示一致，
    需要自己做一次對映。
  </Accordion>

  <Accordion title="quota 和 usage 裡的 token 數對不上怎麼辦？">
    以 `quota` 為準。`quota` 是本次呼叫**實際扣除的額度**，是唯一可用於對賬的欄位。
    響應體裡的 token 數在部分按次計費的模型（如出圖、影片類）上可能是佔位值，
    不參與計價 —— 這類模型的計價方式在 `other.billing_type` 裡會標為 `by_count`。
  </Accordion>

  <Accordion title="日誌能查多久以前的？">
    通過 `start_timestamp` / `end_timestamp` 指定時間範圍即可。
    具體的歷史資料保留期請諮詢客服。建議對賬資料定期匯出留存，不要長期依賴介面回查。
  </Accordion>

  <Accordion title="curl 返回亂碼或 jq 報錯怎麼辦？">
    **原因：** API 返回的是 gzip 壓縮內容（`Content-Encoding: gzip`），curl 沒有自動解壓。

    **解決方案：** 新增 `--compressed` 選項：

    ```bash theme={null}
    curl --compressed 'https://api.apiyi.com/api/log/self?p=0' \
      -H "Authorization: $APIYI_SYS_TOKEN" | jq
    ```

    Python 的 requests 與 Node.js 的 fetch 會自動解壓，無需額外配置。
  </Accordion>

  <Accordion title="查詢日誌會消耗額度嗎？">
    不會。日誌查詢介面不消耗任何配額。
  </Accordion>
</AccordionGroup>

## 注意事項

<Warning>
  **系統令牌不是 API Key，兩者不能互換**

  * **API Key**（`sk-` 開頭）用於 `/v1/*` 推理端點，拿去調 `/api/log/self` 會返回 401
  * **系統令牌**（一串不帶字首的字元）用於 `/api/*` 管理端點，拿去調 `/v1/chat/completions` 會返回 `Invalid token`

  系統令牌的權限範圍覆蓋整個賬號，請**像保管賬號密碼一樣保管它**：存進金鑰管理工具而不是程式碼，
  不要提交進程式碼倉庫，定期輪換。
</Warning>

<Warning>
  **日誌響應裡含有你自己的 API Key 明文**

  日誌記錄會返回發起該次呼叫的令牌資訊。**不要把日誌的原始響應直接貼到公開場合、
  截圖發群、或轉交給第三方**，匯出前先剔除敏感欄位。

  特別提醒：這段明文不帶 `sk-` 字首，**常見的金鑰掃描工具可能掃不出來**，
  不要依賴自動化檢查兜底。
</Warning>

<Info>
  **請求限制**

  * 建議查詢間隔不少於 1 秒，避免頻繁請求觸發限流
  * 建議設定合理的請求超時時間（推薦 30 秒）
  * 大範圍時間段的拉取請做好翻頁與重試處理
</Info>

<Card title="相關文件" icon="link">
  * [餘額查詢 API](/zh-Hant/api-capabilities/balance-query) —— 查賬號剩餘額度
  * [令牌管理 API](/zh-Hant/api-capabilities/token-management) —— 程式化建立與管理 API Key
  * [如何檢視我的呼叫記錄](/zh-Hant/faq/call-logs) —— 控制台手動檢視
  * [日誌與賬單怎麼看](/zh-Hant/faq/log-billing-explained) —— 計費欄位解讀
</Card>
