# SMS stats API

[Metrics 仪表盘](/docs/guides/sms/tracking-and-metrics)上的每个数值都来自 SMS stats API。你可以在自定义仪表盘、数据仓库或健康检查中使用同样的聚合数据。这些只读的工作区级端点需要一个对 `sms` 作用域有读取权限的 API 密钥。在相同的时间范围和筛选条件下，响应结果与仪表盘一致。

类型化方法已在 [TypeScript](/docs/sdks/typescript)、[Python](/docs/sdks/python)、[PHP](/docs/sdks/php) 和 [Go](/docs/sdks/go) SDK 的 `sms.stats` 下提供，[`bird` CLI](/docs/cli) 将它们暴露为 `bird sms stats`，智能体可以通过 `sms_stats_*` [MCP tools](/docs/ai/mcp-server) 访问。完整的请求和响应模式详见 [API 参考](/docs/api/reference/get-sms-stats-summary)。

## 聚合与时间序列

三个端点覆盖仪表盘顶部的内容：

- **`GET /v1/sms/stats/summary`** 返回整个时间窗口的一行聚合数据：生命周期计数（accepted、sent、delivered、undelivered、failed、rejected、expired）、派生的 `delivery_rate` 和 `failure_rate`，以及处理、投递和总延迟的百分位数（p50、p95、p99）。传入 `compare=previous_period` 后，响应还会包含前一个等长窗口的数据及其变化量。
- **`GET /v1/sms/stats/daily`** 和 **`GET /v1/sms/stats/hourly`** 按天或按小时返回生命周期计数，每个时段一行。速率和延迟是整窗口的数值，因此应从 `/summary` 读取，而非从各时段分桶中读取。

比率以**小数**返回，因此 `delivery_rate` 为 `0.9739` 表示 97.39%。分母为零的比率值为 **`null`**，这样一个没有接受任何消息的时段会报告 `delivery_rate`，而不是显示为 `0`。数据基于消息的**发送时间**。今天确认投递的消息如果昨天被接受，则计入昨天。因此近期窗口在投递报告尚未全部到达时会低估 `delivered`，最后几个小时的数据应视为临时值而非最终值。

计数使用近似的去重消息聚合。一条消息在其进展过程中可能出现在多个生命周期状态中，因此状态计数不互斥，不能相加作为消息总数。使用 accepted 消息数作为出站比率的分母。这些运营聚合数据不是计费账单；如需对账，请使用消息和计费记录。

## 选择时间窗口

`from` 和 `to` 接受日历日期（`YYYY-MM-DD`）或 RFC 3339 时间戳，各端点支持的格式不同：

| 端点       | 边界                           | 最大窗口                        |
| ---------- | ------------------------------ | ------------------------------- |
| `/summary` | 两端均为日期，或两端均为时间戳 | 365 天，或时间戳模式下 720 小时 |
| `/daily`   | 日历日期                       | 365 天                          |
| `/hourly`  | RFC 3339 时间戳                | 720 小时（30 天）               |

时间戳边界精确到小时，因此滚动 24 小时窗口只需一次请求。在 `/summary` 上，混用日期和时间戳会返回 `422`。

将 `timezone` 设为 IANA 标识符（如 `America/New_York`），即可在该时区而非 UTC 下计算边界和省略的默认值。设置 `timezone` 后，Bird 会拒绝时间戳边界中的数字 UTC 偏移量（如 `+05:45`）。请改用 `Z` 时间戳或日历日期。

## 明细

七个端点按单一维度拆分相同的投递数据：

- **发往哪里**：`/countries`（目的国家）和 `/carriers`（处理该消息的运营商）。
- **发送了什么**：`/originators`（消息发出的发送方地址）、`/categories` 和 `/tags`。
- **结果如何**：`/statuses`（每个有活动的生命周期状态一行）和 `/error-codes`。

它们都位于 `/v1/sms/stats/` 下。国家、运营商、发送方、类别、标签和错误码的行按 `sort` 排序，并受 `limit` 限制（默认 50，最大 200）。响应中包含 `total`，即窗口内的不同值数量，据此可判断结果是否被截断。排序指标为零分母速率的行排在最后。状态明细最多七行，且没有 `sort` 或 `limit` 参数。

六个排名明细还可以为每行返回一条短时间序列。设置 `include_trend=true` 并选择 `trend_grain=daily` 或 `hourly`。趋势要求 `limit` 不超过 50，窗口不超过 90 天（按天分桶）或 720 小时（按小时分桶）。

`sort` 在数量明细中默认为 `accepted`，在 `/error-codes` 中默认为 `failed`。`/error-codes` 端点按 Bird 的归一化失败原因分组，而非原始运营商代码。其值也可用于 [`GET /v1/sms/messages`](/docs/api/reference/list-sms-messages) 上的 `error_code` 筛选器，将某行关联到对应的消息。

`/tags` 只统计带标签的消息，携带多个标签的消息在每个标签下各计一次。因此其各行之和不等于该时段的总数。请将一个 `/tags` 结果与另一个进行对账，而非使用 `/summary`。

`/statuses` 返回状态和计数，而非完整的投递块。每行统计处于该生命周期状态的消息数；一条消息可出现在多行中。

## 接收消息

`/v1/sms/stats/inbound/` 下还有六个端点，用于统计你的号码收到的消息而非发出的消息：`/summary`、`/daily` 和 `/hourly` 用于汇总和序列，`/countries`、`/operators` 和 `/numbers` 用于明细。它们接受与出站端点相同的窗口和时区参数。

与出站系列有两点不同。每行只有一个简单的 `received` 计数，没有投递块或比率，因为入站消息没有投递生命周期可供聚合。`/operators` 端点**排除了运营商未上报发送方运营商的消息**，因此其各行之和可能小于同一时段的 `/inbound/summary`。请使用汇总作为工作区总计；运营商行仅覆盖有上报运营商的消息。

## 后续步骤

[SMS 分析](/products/sms/analytics)将此报告与活动审查和投递调查关联起来。

- [Metrics](/docs/guides/sms/tracking-and-metrics)：查看这些数值所渲染的仪表盘。
- [SMS 日志](/docs/guides/sms/sms-log)：查看聚合数据背后的消息。
- [Events](/docs/guides/sms/events)：消费聚合数据背后的事件流。

## Related resources

- [What does a delivery receipt tell you?](/explained/sms/what-is-a-delivery-receipt) (answer)
- [Operate messaging reliably](/learn/paths/reliability) (course)

[Get an implementation brief](/learn/workspace?topic=sms-reporting)
