运维与计费/API Key 用量查询

查询单个 API Key 的调用记录、用量明细、计费结果、状态和分页数据。

API Key 用量查询

通过 API Key 用量查询接口获取用于鉴权的当前 API Key 所产生的调用记录、用量明细和计费结果。该接口适用于按 API Key 对账、成本分析、内部用量展示和自动化导出;不会返回同一账户下其他 API Key 的记录。

接口说明

项目
方法GET
路径/v1/usage
API 地址https://unigateway.ai/v1/usage
鉴权Authorization: Bearer $UNIGATEWAY_API_KEY
响应格式application/json

注意:/v1/usage 是 API Key 管理专用接口,必须使用 https://unigateway.ai,不能使用常规的 https://api.unigateway.ai API 地址。

鉴权说明: 此接口使用常规 API Key 鉴权,不使用管理 Key。请将需要查询用量的 API Key 作为 Bearer token;接口仅返回该 API Key 自身产生的调用记录和用量数据。

准备工作

请前往 UniGateway API Keys 创建或获取 API Key。请按业务要求配置 API Key 的访问控制、轮换和安全管理。

将 API Key 配置为服务端环境变量。不要将 API Key 写入浏览器前端代码、日志、公开仓库或导出文件:

export UNIGATEWAY_API_KEY="<YOUR_UNIGATEWAY_API_KEY>"

Windows PowerShell:

$env:UNIGATEWAY_API_KEY = "<YOUR_UNIGATEWAY_API_KEY>"

查询用量

GET /v1/usage

查询参数

参数类型必填说明
limitinteger每页记录数,默认 50,取值范围为 1200
offsetinteger偏移量,默认 0,最大 100000。使用 cursor 时必须省略或设为 0
cursorstring上一页返回的 next_cursor。适用于连续拉取和大范围翻页。
charged_onlyboolean默认 true。未指定 status 时,仅返回 SUCCESSPARTIAL;设为 false 可查询全部状态。
statusstring精确筛选 SUCCESSFAILEDPARTIALUNKNOWN_MODELREFUNDED。指定后优先于 charged_only
model_idstring按 model ID 精确筛选。
modelstring按记录中的 model 名称精确筛选。
fromstring起始时间,ISO 8601 格式,包含该时间点。
tostring结束时间,ISO 8601 格式,包含该时间点。
is_streamboolean传入 truefalse,按是否为流式请求筛选。

fromto 例如 2026-07-01T00:00:00Z。时间范围也兼容 start_date / end_datestartDate / endDate;新接入优先使用 fromto

cURL 请求示例

以下请求查询指定时间范围内最多 100 条成功记录:

curl -G https://unigateway.ai/v1/usage \
  -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \
  --data-urlencode "limit=100" \
  --data-urlencode "from=2026-07-01T00:00:00Z" \
  --data-urlencode "to=2026-07-31T23:59:59Z" \
  --data-urlencode "status=SUCCESS"

查询失败、退款等全部状态时,删除 status 并传入 charged_only=false

curl -G https://unigateway.ai/v1/usage \
  -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \
  --data-urlencode "limit=100" \
  --data-urlencode "charged_only=false"

Python 分页示例

以下示例使用 Python 标准库拉取同一时间范围内的全部记录。金额字段保持为字符串,避免在传输阶段引入浮点精度误差。

import json
import os
from urllib.parse import urlencode
from urllib.request import Request, urlopen

API_URL = "https://unigateway.ai/v1/usage"
params = {
    "limit": 100,
    "from": "2026-07-01T00:00:00Z",
    "to": "2026-07-31T23:59:59Z",
    "status": "SUCCESS",
}
cursor = None

while True:
    page_params = {**params}
    if cursor:
        page_params["cursor"] = cursor

    request = Request(
        f"{API_URL}?{urlencode(page_params)}",
        headers={"Authorization": f"Bearer {os.environ['UNIGATEWAY_API_KEY']}"},
    )
    with urlopen(request) as response:
        page = json.load(response)

    for record in page["items"]:
        print(record["id"], record["model_name"], record["final_price"])

    if not page["has_more"]:
        break
    cursor = page["next_cursor"]

JavaScript 分页示例

以下示例适用于 Node.js 18 或更高版本。不要在浏览器代码中使用该示例,因为 API Key 只能保存在服务端环境中。

const apiUrl = new URL("https://unigateway.ai/v1/usage");
const params = {
  limit: "100",
  from: "2026-07-01T00:00:00Z",
  to: "2026-07-31T23:59:59Z",
  status: "SUCCESS",
};

let cursor;
do {
  const query = new URLSearchParams(params);
  if (cursor) {
    query.set("cursor", cursor);
  }

  apiUrl.search = query.toString();
  const response = await fetch(apiUrl, {
    headers: {
      Authorization: `Bearer ${process.env.UNIGATEWAY_API_KEY}`,
    },
  });
  if (!response.ok) {
    throw new Error(`Usage query failed: ${response.status} ${await response.text()}`);
  }

  const page = await response.json();
  for (const record of page.items) {
    console.log(record.id, record.model_name, record.final_price);
  }
  cursor = page.has_more ? page.next_cursor : undefined;
} while (cursor);

响应

响应为 JSON,itemscreated_at 从新到旧排列。不同模型使用相同的列表结构,每次调用对应一条独立记录,不会按模型分组或合并。

下面是包含 Claude、Seedance 和 gpt-image-2 的响应节选。为便于阅读,示例省略了值为 null 的字段和部分通用字段;示例金额仅用于说明响应结构,不代表模型当前价格。

{
  "items": [
    {
      "id": "usage_claude_example",
      "model_name": "claude-sonnet-5",
      "prompt_tokens": 90,
      "completion_tokens": 9,
      "cache_read_tokens": 0,
      "cache_write_tokens": 124264,
      "total_tokens": 99,
      "request_count": 1,
      "billing_mode": "PER_TOKEN",
      "original_price": "0.310930000000",
      "discount_percentage": "21",
      "savings_amount": "0.065295300000",
      "final_price": "0.245634700000",
      "settlement_currency": "USD",
      "cache_creation_tokens_5m": 124264,
      "cache_creation_tokens_1h": 0,
      "cache_write_breakdown": {
        "cacheCreationTokens5m": 124264,
        "cacheCreationTokens1h": 0,
        "aggregateCacheWriteTokens": 124264,
        "cacheWrite5mPricePerMillionApplied": "2.500000000000",
        "cacheWrite1hPricePerMillionApplied": "4.000000000000",
        "cacheWrite5mOriginalPrice": "0.310660000000",
        "cacheWrite1hOriginalPrice": "0.000000000000",
        "cacheWrite5mFinalPrice": "0.245421400000",
        "cacheWrite1hFinalPrice": "0.000000000000"
      },
      "currency": "USD",
      "status": "SUCCESS",
      "created_at": "2026-07-21T03:12:00.000Z"
    },
    {
      "id": "usage_seedance_example",
      "model_name": "doubao-seedance-2-0-260128",
      "prompt_tokens": 0,
      "completion_tokens": 324900,
      "total_tokens": 324900,
      "request_count": 1,
      "billing_mode": "PER_TOKEN",
      "original_price": "2.397762000000",
      "discount_percentage": "4.55",
      "savings_amount": "0.109098171000",
      "final_price": "2.288663829000",
      "settlement_currency": "USD",
      "media_usage": {
        "modality": "video",
        "resolution": "720p",
        "ratio": "16:9",
        "duration": 15
      },
      "seedance_media_pricing": {
        "tokenPricePerMillion": 7.38,
        "billableTokens": 324900,
        "inferenceMode": "ONLINE",
        "inputHasVideo": false,
        "inputVideoCount": 0,
        "resolution": "720p",
        "duration": 15,
        "ratio": "16:9"
      },
      "currency": "USD",
      "status": "SUCCESS",
      "created_at": "2026-07-21T03:11:00.000Z"
    },
    {
      "id": "usage_image_example",
      "model_name": "gpt-image-2",
      "prompt_tokens": 6618,
      "completion_tokens": 4354,
      "total_tokens": 10972,
      "request_count": 1,
      "billing_mode": "PER_TOKEN",
      "original_price": "0.183564000000",
      "discount_percentage": "35",
      "savings_amount": "0.064247400000",
      "final_price": "0.119316600000",
      "settlement_currency": "USD",
      "media_usage": {
        "modality": "image",
        "resolution": "1K",
        "size": "1024x1024",
        "quality": "auto",
        "imageCount": 1,
        "imageOutputUnits": 4354,
        "requestPath": "/v1/images/edits"
      },
      "currency": "USD",
      "status": "SUCCESS",
      "created_at": "2026-07-21T03:10:00.000Z"
    }
  ],
  "total": 3,
  "limit": 50,
  "offset": 0,
  "has_more": false,
  "next_cursor": null
}

顶层字段

字段类型说明
itemsarray<object>当前页的用量记录,按 created_at 从新到旧排列。
totalinteger / nulloffset 分页时符合当前筛选条件的记录总数;cursor 分页时为 null
limitinteger当前每页记录数。
offsetinteger / null当前 offset;cursor 分页时为 null
has_moreboolean是否还有下一页记录。
next_cursorstring / null下一页使用的不透明游标;没有下一页时为 null

通用字段

字段类型说明
idstring用量记录 ID。
token_id / token_namestring / null当前 API Key 的 ID 和名称。
model_id / model_namestring / nullmodel ID 和本次调用记录的 model 名称。
prompt_tokens / completion_tokensinteger / null输入和输出 token 用量。
cache_read_tokens / cache_write_tokensinteger / null缓存读取和缓存写入 token 用量。
total_tokensinteger / null记录中的总 token 数。缓存 token 通过独立字段返回,不要自行相加推导。
request_countinteger计费请求数。
duration_seconds / total_duration_secondsnumber / null时长计费或请求耗时信息。
first_token_latency_msinteger / null首 token 延迟,仅在适用时返回。
is_streamboolean / null是否为流式请求。
billing_modestring本条记录的计费方式:PER_TOKENPER_REQUESTPER_SECOND
original_pricestring折扣前金额。
discount_percentage / discount_sourcestring / null折扣百分比和折扣来源。
savings_amountstring相比原价节省的金额。
final_pricestring实际计费金额。
settlement_currencystring金额和已应用单价字段使用的结算币种,当前为 USD
currency / exchange_ratestring / null模型原始定价币种,以及换算为结算币种时使用的汇率。
status / error_reasonstring / null记录状态和异常原因。
created_atstringISO 8601 格式的记录时间。

original_pricefinal_pricesavings_amountdiscount_percentageexchange_rate 以及缓存价格字段均以十进制字符串返回。金额和已应用单价字段使用 settlement_currency,当前为 USDcurrency 保留模型的原始定价币种,exchange_rate 表示从原始定价币种换算到结算币种时使用的汇率。处理这些值时请使用高精度十进制类型,避免浮点精度误差。模型专用对象中的部分计数或单价字段可能是 JSON 数值,例如 seedance_media_pricing.tokenPricePerMillion

同一模型的 billing_mode 可能因请求类型或计费规则而不同,请始终使用当前记录返回的值,不要根据模型名称推断。

模型专用字段

模型专用字段不适用或数据不可用时,通常返回 null;部分历史记录可能返回只包含 0null 的对象。客户端应先判断对象及所需字段是否有有效值。

字段适用场景说明
cache_creation_tokens_5mClaude 缓存5 分钟缓存写入 token
cache_creation_tokens_1hClaude 缓存1 小时缓存写入 token
cache_write_breakdownClaude 缓存5 分钟和 1 小时缓存写入量、单价、原价及实收拆分
media_usage图像或视频分辨率、尺寸、质量、时长、画幅和媒体数量等本次请求规格
media_pricing_rule媒体计费本次请求匹配的公开计费条件和有效计费量
seedance_media_pricingSeedance 视频Seedance 本次请求的计费单价、有效计费量和媒体条件

Claude 记录仍会通过 cache_write_tokens 返回缓存写入总量。如果历史记录无法可靠区分 5 分钟和 1 小时缓存写入,两个拆分字段会返回 null,此时应使用 cache_write_tokens 读取总量。

分页

接口支持 offset 和 cursor 两种分页模式。连续拉取、自动化导出和大范围查询应优先使用 cursor;cursor 不返回或依赖 totaloffset,可避免客户端处理大记录总数或大偏移量时的 BigInt 与整数精度问题。

模式请求方式适用场景响应特征
offset传入 limitoffset小范围查询、人工指定页码。返回 total 和当前 offset
cursor首次不传 cursor,后续传入上一页 next_cursor推荐用于连续拉取、批量导出和断点恢复。totaloffsetnull

两种模式不能混用:使用 cursor 时,必须省略 offset 或将其设为 0

offset 分页

首次查询使用 offset=0;下一页按 offset + limit 计算。例如,每页 100 条时查询第 3 页:

curl -G https://unigateway.ai/v1/usage \
  -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \
  --data-urlencode "limit=100" \
  --data-urlencode "offset=200" \
  --data-urlencode "from=2026-07-01T00:00:00Z" \
  --data-urlencode "to=2026-07-31T23:59:59Z"

offset 最大为 100000。查询大范围记录时,不要通过持续增大 offset 翻页,应改用 cursor。

cursor 分页

首次请求不传 cursor。当响应 has_moretrue 时,保留完全相同的筛选条件,并将 next_cursor 原样传入下一次请求。不要解析、修改或复用其他查询返回的 cursor

curl -G https://unigateway.ai/v1/usage \
  -H "Authorization: Bearer $UNIGATEWAY_API_KEY" \
  --data-urlencode "limit=100" \
  --data-urlencode "from=2026-07-01T00:00:00Z" \
  --data-urlencode "to=2026-07-31T23:59:59Z" \
  --data-urlencode "cursor=<NEXT_CURSOR>"

cursor 分页模式下,响应中的 totaloffsetnull。需要恢复导出任务时,应保存最后成功处理页面的 next_cursor 以及完整筛选条件。

状态

状态含义
SUCCESS调用成功并生成正常用量记录
PARTIAL已获得部分可用的用量信息
FAILED调用失败
UNKNOWN_MODEL当前记录无法匹配模型计费信息
REFUNDED该记录已退款

默认查询仅返回 SUCCESSPARTIAL。查询失败或退款记录时,请传入对应的 status;查询全部状态时,请设置 charged_only=false 且不指定 status

错误处理与频率限制

HTTP 状态码常见原因处理方式
400查询参数格式、时间范围或参数组合无效。检查 limitoffsetcursorstatus 和 ISO 8601 时间格式;使用 cursor 时移除非零 offset
401API Key 缺失、无效或环境变量未加载。检查服务端的 UNIGATEWAY_API_KEY,重新加载应用配置后再次请求。
403API Key 已停用或没有访问权限。在 UniGateway 控制台检查该 API Key 的状态、访问控制和限额,再重新验证。
429查询频率超过限制。降低并发,使用指数退避重试,并从最后成功页面的 next_cursor 继续。
500服务暂时无法处理请求。使用有限次数的指数退避重试;持续失败时记录请求时间、筛选条件和错误响应以便排查。

每个 API Key 的用量查询频率上限为每分钟 120 次。轮询场景应按需延长间隔;批量导出优先使用 limit=100200 和 cursor 分页,避免同时对同一 API Key 发起大量请求。

常见问题

Q: 为什么查询结果中没有其他 API Key 的记录?

GET /v1/usage 只返回用于本次鉴权的 API Key 自身产生的记录。请使用需要核对的 API Key 发起请求,并确认服务端环境变量已切换为该密钥;接口不会合并同一账户下其他 API Key 的数据。

Q: 为什么刚完成的调用没有出现在结果中?

先确认请求使用的是目标 API Key,再扩大 fromto 的时间范围,并检查时间是否使用 ISO 8601 格式和正确时区。默认仅返回 SUCCESSPARTIAL;排查失败或退款记录时,传入对应的 status,或设置 charged_only=false 后重新查询。

Q: 金额字段为什么是字符串?

original_pricesavings_amountfinal_price 和相关缓存价格字段以十进制字符串返回,以避免浮点精度损失。应用侧应使用高精度十进制类型完成汇总和金额展示,不要直接将其转换为二进制浮点数。