> ## 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](/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>
  控制台也能看日志（「日志」页面）。本接口是同一份数据的程序化入口，适合需要自动化对账、
  定时导出、或接入自己监控系统的场景。手动查看请直接用控制台，见 [如何查看我的调用记录](/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](/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](/api-capabilities/balance-query) —— 查账号剩余额度
  * [令牌管理 API](/api-capabilities/token-management) —— 程序化创建与管理 API Key
  * [如何查看我的调用记录](/faq/call-logs) —— 控制台手动查看
  * [日志与账单怎么看](/faq/log-billing-explained) —— 计费字段解读
</Card>
