# Get hourly outbound Apple Messages for Business statistics

`GET /v1/amb/stats/hourly`

Returns one row of outbound Apple Messages for Business counts and processing-latency percentiles per hour, gap-filled so a silent hour is a zero row rather than a missing one. Every count in a row is attributed to the hour its message was accepted, regardless of when a later event, such as a send failure, arrived. Use it where the daily grain hides the shape; a window spans at most 720 hours (30 days), and for anything longer use the daily endpoint.

There is no delivery receipt on this channel, so there is no `delivered` count or delivery-latency figure in any row. Each row carries its own `latency.processing` percentiles. Read `first_response` from the summary or a breakdown endpoint.

`from` and `to` are optional RFC 3339 instants, defaulting to the trailing 168 hours; each bound rounds down to the hour and remains inclusive. A request may span up to 30 days (720 rows). An excessive or reversed window returns `422`. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets; when the timezone's offset is not a whole number of hours, rows are read from finer-grained internal rollups and combined into hour boundaries automatically, with no parameter to select this. Historical dates are supported within the maximum window length; the requested dates are not shifted forward.

Set one dimension filter at most (`business_id`, `message_kind`, `intent`, `group`, `category`, or `tag`); more than one returns `422`.

## Code samples

**TypeScript**

```ts
const result = await bird.amb.stats.hourly({
  business_id: "abz_01krdgeqcxet5s7t44vh8rt9mg",
});
console.log(result);
```

Examples: [TypeScript](/docs/api/reference/get-amb-stats-hourly.ts.md) · [Python](/docs/api/reference/get-amb-stats-hourly.py.md) · [Go](/docs/api/reference/get-amb-stats-hourly.go.md) · [PHP](/docs/api/reference/get-amb-stats-hourly.php.md) · [CLI](/docs/api/reference/get-amb-stats-hourly.cli.md) · [MCP](/docs/api/reference/get-amb-stats-hourly.mcp.md) · [cURL](/docs/api/reference/get-amb-stats-hourly.curl.md)

## Example response `200`

```json
{
  "period": {
    "from": "2026-05-01",
    "to": "2026-05-25",
    "grain": "day",
    "data_as_of": "2026-05-25T14:03:10Z"
  },
  "attribution": "accepted_time",
  "data": [
    {
      "bucket": "2026-05-25",
      "counts": {
        "accepted": 4820,
        "sent": 4790,
        "send_failed": 30,
        "rejected": 4,
        "sent_rate": 0.9938,
        "send_failure_rate": 0.0062
      },
      "latency": {
        "processing": {
          "p50_ms": 610,
          "p95_ms": 2140,
          "p99_ms": 5380
        }
      }
    }
  ]
}
```

## Query parameters

- `from` (string): Start of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. The window may not exceed 30 days (720 hours). Defaults to 168 hours (7 days) before `to` when omitted.
- `to` (string): End of the window (RFC 3339 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. The window may not exceed 30 days (720 hours). Defaults to the current hour when omitted.
- `timezone` (string): IANA timezone identifier used to group statistics, for example `Asia/Kathmandu`. The default is UTC. Day and hour boundaries, including the default window when `from` and `to` are omitted, follow this timezone. When this parameter is set, pass `from` and `to` as calendar days or `Z` instants instead of timestamps with explicit UTC offsets.
- `business_id` (string): Restrict the statistics to a single business. Mutually exclusive with the other dimension filters (`message_kind`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `business_id` key on a row of the per-business breakdown.
- `message_kind` (string)

  Restrict the statistics to a single message content kind. Mutually exclusive with the other dimension filters (`business_id`, `intent`, `group`, `category`, `tag`); only one may be set per request. Matches the `message_kind` key on a row of the per-content-kind breakdown.

  Possible values: `text`, `attachment`, `rich_link`, `quick_reply`, `list_picker`, `time_picker`, `form`, `apple_pay`, `authenticate`, `imessage_app`, `interactive`
- `intent` (string): Restrict the statistics to a single entry-point intent. Mutually exclusive with the other dimension filters (`business_id`, `message_kind`, `group`, `category`, `tag`); only one may be set per request. Matches the `intent` key on a row of the per-intent breakdown.
- `group` (string): Restrict the statistics to a single entry-point group. Mutually exclusive with the other dimension filters (`business_id`, `message_kind`, `intent`, `category`, `tag`); only one may be set per request. Matches the `group` key on a row of the per-group breakdown.
- `category` (string): Restrict the statistics to a single message category. Mutually exclusive with the other dimension filters (`business_id`, `message_kind`, `intent`, `group`, `tag`); only one may be set per request. Matches the `category` key on a row of the per-category breakdown.
- `tag` (string): Restrict the statistics to a single tag. Use `name` to match any value of a tag, or `name:value` for a specific pair (for example `campaign:spring_launch`). Mutually exclusive with the other dimension filters (`business_id`, `message_kind`, `intent`, `group`, `category`); only one may be set per request. A row of the per-tag breakdown carries the same pair in its single `tag` key.

## Response body

- `period` (object, required): The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.
- `period.from` (string, required): Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain. Historical starts are preserved; the maximum request length does not impose a historical cutoff.
- `period.to` (string, required): Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant on the hour grain.
- `period.grain` (string, required)

  The bucket grain of the series, either `day` or `hour`.

  Possible values: `day`, `hour`
- `period.data_as_of` (nullable string): Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable.
- `attribution` (string, required)

  Always `accepted_time` for outbound statistics.

  Possible values: `accepted_time`, `event_time`
- `data` (array of object, required): One row per day or hour in chronological order. Buckets with no activity contain zero counts.
- `data.bucket` (string, required): The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.
- `data.counts` (object, required): Outbound Apple Messages for Business counts for the requested scope, attributed to when each message was accepted. Apple Messages for Business has no delivery receipt, so there is no `delivered` count anywhere in this API: `sent` is the last outbound state Bird observes for a message. Very large counts are close estimates rather than exact tallies. Rates are computed once here, clamped to 1, and null when nothing was accepted.
- `data.counts.accepted` (integer, required): Distinct messages accepted for sending after admission checks. This is the denominator for `sent_rate` and `send_failure_rate`.
- `data.counts.sent` (integer, required): Distinct messages handed off to Apple.
- `data.counts.send_failed` (integer, required): Distinct accepted messages that Apple refused or that exhausted their send attempts. See `last_error.code` on the message for the reason; a refused charge is not a send failure, it is `rejected`.
- `data.counts.rejected` (integer, required): Distinct messages refused before any send attempt, because the destination has no price, the wallet could not fund the send, or the content cannot be sent yet. Rejected messages are never charged and are not counted in `accepted`, so the total addressed is `accepted + rejected`. Excluded from `send_failure_rate`, which covers send failures only.
- `data.counts.sent_rate` (nullable number, required): Share of accepted messages Apple acknowledged, computed as `sent / accepted`. Null when no messages were accepted in scope. This stands where other channels report a delivery rate.
- `data.counts.send_failure_rate` (nullable number, required): Share of accepted messages that failed to send, computed as `send_failed / accepted`. Null when no messages were accepted in scope.
- `data.latency` (object, required): Processing-latency percentiles in milliseconds for the requested scope, from acceptance to Apple handoff. Apple Messages for Business has no delivery receipt, so there is no `delivery` or `total` member beside `processing`. Conversation response timing is reported separately in `first_response`. Always present; every percentile is null when no qualifying message in scope has a measurement.
- `data.latency.processing` (object, required): Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family. All three are null when no qualifying event contributed a measurement.
- `data.latency.processing.p50_ms` (nullable integer, required): Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement.
- `data.latency.processing.p95_ms` (nullable integer, required): 95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.
- `data.latency.processing.p99_ms` (nullable integer, required): 99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement.

## Related resources

- [Should I use a Bird SDK or call the API directly?](/explained/platform/should-i-use-an-sdk-or-call-the-api-directly) (answer)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=api-basics)
