新人 免费注册,送 10 次调用,最高 $1,免绑卡。

账单 API

将你自己工作区的用量与费用数据拉取到你自己的系统中,无需打开控制台。账单密钥是一种 只读 凭证,作用范围仅限一个工作区 - 它只能读取账单数据,不能做别的事。

⚠

账单密钥可以读取其工作区中记录的每一次请求,包括之后已被删除或吊销的密钥,但它不能调用任何模型,也不能创建、修改或删除 API 密钥。 在此之外的任何地方使用它都会返回 401 wrong_key_kind.

创建账单密钥

  1. 打开 Console → Billing API keys (仅工作区所有者可用)。为其命名,可选择限制到 IP 白名单或设置到期时间,然后复制密钥 - 它以 sk-syn-bill- 并且只会显示一次。
  2. 将其作为 Bearer token 调用下方各端点。 可随时在同一页面吊销它 - 吊销会立即使其失效,且不影响工作区中的任何其他内容。
  3. 密钥与其创建者绑定:如果该创建者不再是工作区所有者,密钥会停止工作并返回 403 key_owner_not_authorized.

认证

每次调用都需要 Authorization: Bearer sk-syn-bill-... ,并针对下方的 base URL 发起请求。账单密钥只能用于这些端点,而这些端点也只接受账单密钥 - 任何其他组合都会返回 401 wrong_key_kind.

Authorization: Bearer sk-syn-bill-...

Base URL: https://synthorai.io/api/v1/billing

ℹ

作用范围是整个工作区:其中的每一个推理密钥都包含在内,即使已被删除的密钥,其历史记录也仍可查询。 api_key_id 和 model 过滤条件只能缩小结果范围,绝不会扩大;每次查询都以该密钥自身的工作区为界。

ℹ

每次查询都在只读副本上执行,绝不会用主库。未配置副本的进程会返回 503 replica_unavailable 而不是静默回退到主库。

✓

Token 字段: 遵循 OpenAI 的约定 - prompt_tokens 是总输入 token 数,已经包含下面每一项缓存读取与缓存写入,而 completion_tokens 已经包含 reasoning_tokens。两者都不是在总数之外额外叠加的 - 它们是明细拆分,不是额外的 token。

prompt_tokens 中的缓存读取部分即 cached_tokens;缓存写入部分即 cache_write_tokens,在 /records 的行中会按 TTL 进一步拆分为 cache_write_5m_tokens 和 cache_write_1h_tokens。

✓

金额字段: cost_usd 是实际收取的金额。 byok_list_price_usd 是 BYOK 请求按标价本应花费的金额 - 非 BYOK 请求为 0。不存在 list_price_usd 这个字段。

ℹ

此处的“错误”指所有已到达 API 并被拒绝的请求 - 包括 402(余额不足)、403、429 等 4xx,以及上游失败。只有我方自己的内部基础设施拒绝会被排除,这与控制台的口径一致。拒绝请求在每台服务器上按 key、状态码、分钟最多记录一次,因此 402、403、429 的次数是下限;成功请求与扣费是完整的。

GET /usage - 汇总用量

GET /usage

按小时或按天预聚合的数据,适合对账和看板。数据来自小时汇总表,并补上自上次汇总以来新写入的请求,因此最近的时间桶不会整整落后一个汇总周期。

参数类型说明
start*string范围起点:RFC 3339 时间戳或类似 2026-09-01 的日期。
endstring范围终点。默认为当前时间。
granularitystring分桶粒度:hour 或 day。默认为 day。
group_bystring用于拆分行的维度:api_key、model,或两者都要(任意子集)。默认为两者都要。
api_key_idinteger按一个或多个 API 密钥 id 过滤。可重复传入;只会缩小范围,绝不会扩大。
modelstring按一个或多个模型 id 过滤。可重复传入。
limitinteger每页行数。默认 1000,上限 5000。
cursorstring不透明的分页游标,取自上一页响应中的 meta.next_cursor。

在每一行中,api_key_id 与 api_key_name 只有在 group_by 包含 api_key 时才会出现,model 同理只有在包含 model 时才会出现。当某个桶没有延迟采样时,avg_latency_ms 可能为 null。

示例请求

curl "https://synthorai.io/api/v1/billing/usage?start=2026-09-01&end=2026-09-24&granularity=day" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{
  "data": [
    {
      "bucket_start": "2026-09-23T00:00:00Z",
      "bucket_end": "2026-09-24T00:00:00Z",
      "api_key_id": 42,
      "api_key_name": "prod-backend",
      "model": "claude-sonnet-5",
      "requests": 1280,
      "success_requests": 1265,
      "error_requests": 15,
      "errors_4xx": 12,
      "errors_429": 2,
      "errors_5xx": 1,
      "prompt_tokens": 512000,
      "completion_tokens": 98000,
      "cached_tokens": 210000,
      "cache_write_tokens": 8000,
      "reasoning_tokens": 4000,
      "cost_usd": 4.1732,
      "byok_list_price_usd": 0,
      "byok_requests": 0,
      "avg_latency_ms": 812,
      "final": true
    }
  ],
  "meta": {
    "generated_at": "2026-09-24T08:00:00Z",
    "data_complete_until": "2026-09-24T06:00:00Z",
    "timezone": "UTC",
    "currency": "USD",
    "next_cursor": null,
    "has_more": false
  }
}

GET /records - 逐请求行

GET /records

每个请求一行,用于增量同步到你自己的数据库。请按 cursor 同步,绝不要按时间同步,并按 record_id 对收到的行去重 - 原因见下方“避免时间间隙”。

参数类型说明
cursorstring不透明的同步游标(字符串),取自上一次调用响应中的 meta.next_cursor。cursor 与 start 只能提供一个;它没有范围上限。
startstring范围起点(RFC 3339 时间戳或日期)。cursor 与 start 只能提供一个;start/end 范围上限为 31 天,而 cursor 没有范围上限。
endstring范围终点。默认为当前时间。
api_key_idinteger按一个或多个 API 密钥 id 过滤。可重复传入;只会缩小范围,绝不会扩大。
modelstring按一个或多个模型 id 过滤。可重复传入。
limitinteger每页行数。默认 500,上限 1000。

示例请求

curl "https://synthorai.io/api/v1/billing/records?start=2026-09-24T00:00:00Z&limit=500" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{
  "data": [
    {
      "record_id": "rec_8f2a91c4",
      "request_id": "req_9f3c2b1a",
      "recorded_at": "2026-09-24T05:58:31Z",
      "completed_at": "2026-09-24T05:58:29Z",
      "api_key_id": 42,
      "api_key_name": "prod-backend",
      "model": "claude-sonnet-5",
      "status": "success",
      "status_code": 200,
      "prompt_tokens": 812,
      "completion_tokens": 194,
      "cached_tokens": 512,
      "cache_write_tokens": 64,
      "cache_write_5m_tokens": 64,
      "cache_write_1h_tokens": 0,
      "cost_usd": 0.00412,
      "byok_list_price_usd": 0,
      "is_byok": false,
      "is_stream": true,
      "duration_ms": 1340,
      "ttft_ms": 210
    }
  ],
  "meta": {
    "next_cursor": "eyJvIjoiODgxNDA5MCJ9",
    "has_more": false,
    "window_final": true,
    "safe_until": "2026-09-24T05:58:31Z",
    "latest_recorded_at": "2026-09-24T05:58:31Z",
    "data_complete_until": "2026-09-24T05:58:00Z"
  }
}
参数说明
next_cursor用于下一次调用的游标。始终存在。
has_more此时用该游标再次调用是否会返回更多行。
window_final只有在提供了 end 且它早于或等于 safe_until 时才为 true - 此时你要求的范围才可确保完整。否则不存在或为 false。
safe_until调用方可信为已定稿的最新 recorded_at;比这更新的行仍在被暂时保留。
latest_recorded_at所有行(无论是否已定稿)中最新的 recorded_at。
data_complete_until与 /freshness 返回的值相同 - 按小时汇总表完整到什么时间,用于与 /usage 交叉核对。
⚠

时间基准: /usage 按 completed_at 分桶(即请求完成的时间)。/records 的 start/end 过滤则按 recorded_at 过滤(即行被写入的时间,在高负载下可能滞后于完成时间)。要将 /records 与 /usage 对齐,请自行按 completed_at 而非 recorded_at 对同步到的行分桶。

GET /records/export - 流式导出

GET /records/export

过滤条件与 /records 相同,以换行分隔的 JSON(application/x-ndjson)流式返回 - 专为大批量补齐历史数据设计,而非分页列表。每个工作区同一时间只能有一个导出流;第二个会收到 429 too_many_concurrent_requests。

示例请求

curl -N "https://synthorai.io/api/v1/billing/records/export?cursor=eyJvIjoiODgxNDAzMiJ9" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{"record_id":"rec_8f2a91c4","request_id":"req_9f3c2b1a","model":"claude-sonnet-5","status":"success","cost_usd":0.00412}
{"record_id":"rec_8f2a91c5","request_id":"req_9f3c2b1b","model":"claude-sonnet-5","status":"success","cost_usd":0.00398}
{"_meta":{"next_cursor":"eyJvIjoiODgxNDA5MSJ9","rows":2,"complete":true,"window_final":true,"generated_at":"2026-09-24T06:00:02Z"}}

该流的最后一行是一个 _meta 对象:

参数说明
next_cursor用于下一次导出调用的游标。
rows此次流式传输中写入的记录数。
completefalse 表示该流在耗尽整个范围之前就已停止 - 请用 cursor=next_cursor 继续。
window_final与 /records 中含义相同:只有在导出的 end 早于或等于 safe_until 时才为 true。
generated_at该行的写入时间。
stopped_becausecomplete 为 false 时出现:row_cap、scan_cap(已扫完单次允许的最大 id 范围但结果未满,常见于过滤条件很窄时)、time_cap、query_error 或 window_not_final。

GET /summary - 初步统计与异常

GET /summary

总量、按费用排名的模型与密钥,以及该范围的错误率,再加上一个 anomalies 列表(high_error_rate、rate_limited、data_not_final、rollup_delayed),每一项都带有 info 或 warning 的严重级别以及一段易读的说明文字。尚未定稿范围内的数字仍可能变动 - 见下文。

参数类型说明
startstring范围起点。默认为终点前 30 天。
endstring范围终点。默认为当前时间。
api_key_idinteger按一个或多个 API 密钥 id 过滤。可重复传入;只会缩小范围,绝不会扩大。
modelstring按一个或多个模型 id 过滤。可重复传入。

models_count 与 keys_count 给出 top_models 与 top_keys 背后的总数,而这两个列表最多列出按费用排名前 20 的项目。范围默认为 end 之前的 30 天,用于 start 被省略的情况。

示例请求

curl "https://synthorai.io/api/v1/billing/summary?start=2026-09-17&end=2026-09-24" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{
  "data": {
    "totals": { "requests": 48210, "cost_usd": 132.55, "error_rate": 0.021 },
    "models_count": 6,
    "keys_count": 3,
    "top_models": [ { "model": "claude-sonnet-5", "cost_usd": 88.10, "byok_list_price_usd": 0 } ],
    "top_keys": [ { "api_key_id": 42, "api_key_name": "prod-backend", "cost_usd": 61.30, "byok_list_price_usd": 0 } ],
    "anomalies": [
      { "type": "data_not_final", "severity": "info", "message": "The last 2 hours of this range are not yet final." }
    ]
  },
  "meta": {
    "generated_at": "2026-09-24T06:00:02Z",
    "data_complete_until": "2026-09-24T04:00:00Z"
  }
}

GET /freshness - 数据完整度

GET /freshness

返回 data_complete_until, latest_recorded_at, safe_until 和 pipeline_lag_seconds - 在把刚刚拉取的范围视为已定稿之前,请先轮询这个端点。safe_until 是调用方可信为已完整的最新 recorded_at;比它更新的行可能仍在到达中。

示例请求

curl "https://synthorai.io/api/v1/billing/freshness" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{
  "data": {
    "data_complete_until": "2026-09-24T04:00:00Z",
    "latest_recorded_at": "2026-09-24T05:58:31Z",
    "safe_until": "2026-09-24T05:58:31Z",
    "pipeline_lag_seconds": 47
  },
  "meta": { "generated_at": "2026-09-24T06:00:02Z" }
}

GET /balance - 工作区当前可用余额

GET /balance

返回工作区当前可用余额,与控制台显示的可用余额一致。专为轮询设计:每 30 到 60 秒查一次就足够。它有独立的速率限制,轮询不会占用数据类端点的额度;余额为零时照常返回。

示例请求

curl "https://synthorai.io/api/v1/billing/balance" \
  -H "Authorization: Bearer sk-syn-bill-..."

示例响应

{
  "data": {
    "available_usd": 1284.37,
    "debt_usd": 0,
    "source": "live",
    "voucher": null,
    "scheduled_credits": [
      { "amount_usd": 250, "release_at": "2026-10-01T00:00:00Z" }
    ],
    "scheduled_credit_total_usd": 250
  },
  "meta": { "generated_at": "2026-09-24T06:00:02Z", "currency": "USD" }
}
参数说明
available_usd当前请求实际扣费所用的通用余额,最低为 0。在途请求结算后可能短暂回升;统计消费请用 /usage 或 /records。
debt_usd欠款:超出已充值额度的用量,没有欠款时为 0。充值会先抵扣欠款,剩余部分才计入 available_usd。
sourcelive:读自实时账本。delayed:实时账本暂时无法读取,数值来自数据库副本,最多可能落后约 30 秒。
voucher活动/代金券额度,仅用于图像生成(见 applies_to),限所列模型、在 expires_at 之前可用;其他请求只使用 available_usd,券额不计入 available_usd。没有时为 null;过期后 active 为 false。
scheduled_credits已约定、分期到账的额度(amount_usd、release_at);每一期在 release_at 之后约 10 分钟内计入余额,之前不可使用。scheduled_credit_total_usd 为其合计。

错误

每个错误都是 {"error": {"type", "message", "hint"}}:

{
  "error": {
    "type": "range_too_large",
    "message": "the requested range exceeds this endpoint's cap",
    "hint": "narrow start/end to at most 31 days, or sync /records with a cursor instead"
  }
}
类型HTTP 状态码含义
invalid_parameter400某个查询参数缺失、格式错误或超出范围。
range_too_large400请求的时间范围或 id 窗口超出该端点允许的上限;hint 字段会说明具体上限。
start_too_recent400start/end 时间窗的开始时间晚于 safe_until,起点边界尚未稳定。请按 Retry-After 头稍后重试,或改用 cursor 同步。
wrong_key_kind401该凭证不是账单密钥,或账单密钥被用在了这些端点之外。
authentication_error401密钥缺失、无效、已过期,或被其 IP 白名单拦截。
key_owner_not_authorized403该密钥的创建者已不再是工作区所有者。账单密钥仅限所有者本人使用,所有权转移的那一刻起就会停止工作;新所有者必须创建自己的密钥。
rate_limited429该工作区每分钟请求数超过 60 次。请在 Retry-After 响应头指示的时间之后重试。
too_many_concurrent_requests429该工作区并发请求超过 3 个,或已有一个导出正在进行。
replica_unavailable503该进程未配置只读副本;API 绝不会回退到主库。
query_timeout503查询超过了 10 秒的语句超时限制或 15 秒的截止时限。请缩小范围后重试。
internal_error500意外的服务器错误。请重试,如果持续出现请反馈。

速率限制与上限

防护措施值
速率限制每个工作区每分钟 60 次请求,为全实例共享的滑动窗口。
并发数每个工作区在每台服务器上最多 3 个并发请求,导出流在每台服务器上每个工作区最多 1 个。
余额轮询/balance:每个工作区每分钟 60 次、同时最多 2 个请求,与其他端点分开计数。
范围上限/usage:hour 粒度 31 天,day 粒度 366 天。/summary:最多 366 天。/records 与导出:最多 31 天。
分页上限/usage 每页最多返回 5,000 行,/records 最多 1,000 行。导出以每页 2,000 行流式传输,并在 1,000,000 行处停止 - 可用 cursor 继续。单次导出扫过 2,000,000 个记录 id 后也会停止(stopped_because=scan_cap),并会按数据库负载自动放慢;用 cursor 继续。
缓存每个响应都带有 Cache-Control: no-store。

绝不会返回: channel_id, upstream_request_id, usage_raw, other, content, ip_address, user_agent, cost_detail.

避免时间间隙

请求完成后会异步写入日志(通常几秒钟,积压时会更久),而 /usage 与 /summary 读取的按小时汇总表是按批次写入的。最近几个小时的数据总是有一点不完整。/usage 和 /summary 会补上自上次汇总以来新写入的请求(meta.live_tail 为 true);汇总落后太多时不再补(meta.live_tail 为 false),最近的时间桶是不完整的。

data_complete_until 取以下两者中较早的一个:按小时汇总表的水位线减去当前的处理延迟,或尚未被汇总的最早请求 - 再减去 30 分钟的安全余量,并向下取整到小时。

ℹ

已定稿的桶是稳定的,只有一个例外:每日对账任务如果发现偏差,仍可能在 48 小时内修正某个小时的数据。/records 永远是某次请求精确数字的权威来源。

每个 /usage 行都带有一个 final 标志: true ,即当其所在的桶结束时间早于或等于 data_complete_until (/freshness 返回的值)时成立。已定稿的桶不会再变化。

  • 聚合数据: 按 (bucket_start, api_key_id, model) 将每一行 upsert 到你自己的存储中,并在某个桶仍为 final: false 时重新拉取它。绝不要用新总量减去旧总量的方式来推算增量。
  • 原始行: 按 cursor 同步,绝不要按时间同步。请保留 next_cursor,并在下一次调用时从它继续,同时按 record_id(每行稳定的不透明 id)对收到的行去重,以防某次续传重复送达同一行。晚于约 5 分钟的行会被暂时保留 - safe_until 即最新已记录行的时间减去 5 分钟 - 因此一个仍在提交中的行绝不会跑到已发出的游标位置之前,也就不会有数据落入间隙。统计请求数时,请按 request_id 去重计数。
  • 限定窗口: 如果你查询的是 start/end 范围而不是按 cursor 同步,只有在 window_final 为 true 时才可信其为完整(只有当你提供了 end 且它早于或等于 safe_until 时才为 true)。如果 window_final 为 false,请稍后用相同范围再次调用,或改为按 cursor 同步。时间窗的开始时间必须不晚于 safe_until;更晚的开始时间会返回 400 start_too_recent,并带 Retry-After 头。

推荐的集成方式

  • 每晚账单处理: 每天调用一次 /usage,并使用 granularity=day;对上一次运行中仍为 final: false 的桶重新拉取。
  • 逐请求明细: 每小时调用一次 /records,将 cursor 设为你存储的上一次 next_cursor,并在 has_more 为 true 时持续翻页,同时按 record_id 对收到的行去重。
  • 仪表盘: 对当前可见范围调用 /summary,而不是自己聚合 /records - 它已经附带了异常列表。

需要以编程方式签发或管理普通 API 密钥,而不是读取账单数据?请参阅 Provisioning Keys。