# Get selected email metrics

`POST /v1/email/stats/query`

Returns selected delivery, engagement, and latency metrics for the workspace. Combine filters, one grouping dimension, and a time grain for reports such as weekly deliveries by recipient domain for a campaign. Events are selected and bucketed by when they occurred. An open in the window can belong to a message sent earlier; later outcomes outside the window are excluded. Ungrouped requests return one summary; grouped pages retain each group's complete series. Counts estimate distinct identities, and undefined rates or empty latency samples return null.

Dates include whole local days. Instants include the quarter-hour containing the requested end. Responses echo normalized UTC bounds with an exclusive end. Follow cursors using the original body and replace the cursor fields. Requests can cover up to 365 local days or 720 hours for instant bounds, subject to available history. Unsupported combinations, unavailable history, or query-size limits return 422; a failed query returns no partial report.

Idempotency is best-effort replay. Responses up to 256 KiB may be replayed while retained; larger valid responses up to 4 MiB are read again on retry. A key does not freeze an analytics snapshot. SDK iterators use your explicit key for the first page and a fresh automatic key per continuation page, reusing that page's key across retries. For a fresh read, omit the key or use a new one.

## Code samples

**TypeScript**

```ts
for await (const group of bird.email.stats.query({
  from: "2026-08-03",
  to: "2026-08-16",
  metrics: ["delivered", "bounce_rate"],
  group_by: "recipient_domain",
  grain: "week",
  limit: 25,
})) {
  console.log(group.dimensions, group.metrics, group.series);
}
```

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

## Example response `200`

```json
{
  "data": [
    {
      "dimensions": {
        "template_id": "emt_01krdgeqcxet5s7t44vh8rt9mg",
        "ip_pool_id": "ipp_01krdgeqcxet5s7t44vh8rt9mg",
        "broadcast_id": "eb_01krdgeqcxet5s7t44vh8rt9mg"
      }
    }
  ],
  "period": {
    "grain": "quarter_hour"
  }
}
```

## Request body

- `from` (string, required): Inclusive start, as a calendar date or RFC 3339 instant. Use the same form for from and to. Instants round down to a local quarter-hour; use Z when timezone is supplied.
- `to` (string, required): Inclusive end. Dates include the whole local day; instants round down to a local quarter-hour and include that quarter-hour. Dates allow up to 365 local days; instants allow up to 720 hours, subject to available history. Preserve this original bound when following cursors.
- `timezone` (string): IANA timezone for dates and bucket boundaries. Defaults to UTC.
- `metrics` (array of string, required): Distinct metrics to return. Unselected metrics are absent.
- `group_by` (string)

  Group by this dimension. Omit for a single ungrouped summary with optional series.

  Possible values: `sending_domain`, `category`, `template_id`, `tag`, `recipient_domain`, `mailbox_provider`, `mailbox_provider_region`, `sending_ip`, `ip_pool_id`, `broadcast_id`, `country`, `region`, `city`, `agent_family`, `os_family`, `device_family`, `smtp_error_code`, `feedback_type`
- `grain` (string)

  Time buckets in the requested timezone. Weeks start on Monday; months start on the first day. Half days start at midnight and noon. Edge buckets count events inside the normalized period.

  Possible values: `quarter_hour`, `hour`, `half_day`, `day`, `week`, `month`
- `filters` (object): Predicates on the context recorded for each event. Dimensions combine with AND. Unsupported metric and dimension combinations return 422, including for an empty workspace.
- `filters.sending_domain` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.sending_domain.include` (array of string)
- `filters.sending_domain.exclude` (array of string)
- `filters.category` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.category.include` (array of string)
- `filters.category.exclude` (array of string)
- `filters.template_id` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.template_id.include` (array of string)
- `filters.template_id.exclude` (array of string)
- `filters.tag` (object): Select one case-sensitive tag name. A name without values requires that tag to exist. Exclude-only predicates retain events without that tag. Include and exclude together accept at most 20 distinct normalized values with no overlap.
- `filters.tag.name` (string, required)
- `filters.tag.include` (array of string)
- `filters.tag.exclude` (array of string)
- `filters.recipient_domain` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.recipient_domain.include` (array of string)
- `filters.recipient_domain.exclude` (array of string)
- `filters.mailbox_provider` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.mailbox_provider.include` (array of string)
- `filters.mailbox_provider.exclude` (array of string)
- `filters.mailbox_provider_region` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.mailbox_provider_region.include` (array of string)
- `filters.mailbox_provider_region.exclude` (array of string)
- `filters.sending_ip` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.sending_ip.include` (array of string)
- `filters.sending_ip.exclude` (array of string)
- `filters.ip_pool_id` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.ip_pool_id.include` (array of string)
- `filters.ip_pool_id.exclude` (array of string)
- `filters.broadcast_id` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.broadcast_id.include` (array of string)
- `filters.broadcast_id.exclude` (array of string)
- `filters.country` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.country.include` (array of string)
- `filters.country.exclude` (array of string)
- `filters.region` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.region.include` (array of string)
- `filters.region.exclude` (array of string)
- `filters.city` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.city.include` (array of string)
- `filters.city.exclude` (array of string)
- `filters.agent_family` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.agent_family.include` (array of string)
- `filters.agent_family.exclude` (array of string)
- `filters.os_family` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.os_family.include` (array of string)
- `filters.os_family.exclude` (array of string)
- `filters.device_family` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.device_family.include` (array of string)
- `filters.device_family.exclude` (array of string)
- `filters.smtp_error_code` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.smtp_error_code.include` (array of string)
- `filters.smtp_error_code.exclude` (array of string)
- `filters.feedback_type` (object): Match recorded values using include or exclude. Include values combine with OR; exclusions remove matches. Missing values survive exclude-only predicates. Supply a nonempty array; at most 20 distinct values across both arrays are accepted after normalization, with no overlap.
- `filters.feedback_type.include` (array of string)
- `filters.feedback_type.exclude` (array of string)
- `sort` (string)

  Grouped requests only. Rank groups by this selected metric; defaults to the first metrics entry. Undefined values sort last in either direction. Ties use the dimension value ascending, with null last.

  Possible values: `sends_accepted`, `accepted`, `processed`, `delivered`, `bounced`, `hard_bounced`, `soft_bounced`, `admin_bounced`, `block_bounced`, `undetermined_bounced`, `complained`, `deferred`, `rejected`, `oob_bounces`, `opens`, `opens_non_prefetched`, `clicks`, `unsubscribes`, `unique_opens`, `unique_opens_non_prefetched`, `unique_clicks`, `confirmed_unique_opens`, `confirmed_unique_opens_non_prefetched`, `effective_delivered`, `all_bounces`, `delivery_rate`, `bounce_rate`, `complaint_rate`, `deferral_rate`, `open_rate`, `click_rate`, `unsubscribe_rate`, `oob_rate`, `processing_p50_ms`, `processing_p95_ms`, `processing_p99_ms`, `total_p50_ms`, `total_p95_ms`, `total_p99_ms`
- `order` (string)

  Grouped requests only. Defaults to desc.

  Possible values: `asc`, `desc`
- `limit` (integer): Grouped requests only. Maximum groups per page; defaults to 25. Each group retains its complete series.
- `starting_after` (string): Grouped requests only. Opaque next_cursor from the previous response. Mutually exclusive with ending_before.
- `ending_before` (string): Grouped requests only. Opaque prev_cursor for backward navigation, or refresh_cursor to read groups before the anchor in the current sort order. Mutually exclusive with starting_after.

## Response body

- `data` (array of object, required)
- `data.dimensions` (object, required): Contains the requested group_by property, including a null value when context is missing. Ungrouped results use an empty object.
- `data.dimensions.sending_domain` (nullable string): Recorded sending domain value. Null represents missing context and differs from an empty string.
- `data.dimensions.category` (nullable string): Recorded category value. Null represents missing context and differs from an empty string.
- `data.dimensions.template_id` (nullable string): Recorded template id value. Null represents missing context and differs from an empty string.
- `data.dimensions.tag` (nullable string): Recorded tag value. Null represents missing context and differs from an empty string.
- `data.dimensions.recipient_domain` (nullable string): Recorded recipient domain value. Null represents missing context and differs from an empty string.
- `data.dimensions.mailbox_provider` (nullable string): Recorded mailbox provider value. Null represents missing context and differs from an empty string.
- `data.dimensions.mailbox_provider_region` (nullable string): Recorded mailbox provider region value. Null represents missing context and differs from an empty string.
- `data.dimensions.sending_ip` (nullable string): Recorded sending ip value. Null represents missing context and differs from an empty string.
- `data.dimensions.ip_pool_id` (nullable string): Recorded ip pool id value. Null represents missing context and differs from an empty string.
- `data.dimensions.broadcast_id` (nullable string): Recorded broadcast id value. Null represents missing context and differs from an empty string.
- `data.dimensions.country` (nullable string): Recorded country value. Null represents missing context and differs from an empty string.
- `data.dimensions.region` (nullable string): Recorded region value. Null represents missing context and differs from an empty string.
- `data.dimensions.city` (nullable string): Recorded city value. Null represents missing context and differs from an empty string.
- `data.dimensions.agent_family` (nullable string): Recorded agent family value. Null represents missing context and differs from an empty string.
- `data.dimensions.os_family` (nullable string): Recorded os family value. Null represents missing context and differs from an empty string.
- `data.dimensions.device_family` (nullable string): Recorded device family value. Null represents missing context and differs from an empty string.
- `data.dimensions.smtp_error_code` (nullable string): Recorded smtp error code value. Null represents missing context and differs from an empty string.
- `data.dimensions.feedback_type` (nullable string): Recorded feedback type value. Null represents missing context and differs from an empty string.
- `data.metrics` (object, required): Selected metric values. Counts are nonnegative approximate distinct counts. Period uniques and rates are computed independently of buckets; summing bucket or group values does not reconstruct period totals. Zero means a supported empty population; undefined rates and empty latency samples are null.
- `data.metrics.sends_accepted` (integer): Distinct sends accepted by Bird, counted by email identity.
- `data.metrics.accepted` (integer): Distinct message recipients accepted by Bird.
- `data.metrics.processed` (integer): Distinct message recipients processed for delivery.
- `data.metrics.delivered` (integer): Distinct message recipients with a delivery event.
- `data.metrics.bounced` (integer): Distinct message recipients with bounced events.
- `data.metrics.hard_bounced` (integer): Distinct message recipients with hard bounced events.
- `data.metrics.soft_bounced` (integer): Distinct message recipients with soft bounced events.
- `data.metrics.admin_bounced` (integer): Distinct message recipients with admin bounced events.
- `data.metrics.block_bounced` (integer): Distinct message recipients with block bounced events.
- `data.metrics.undetermined_bounced` (integer): Distinct message recipients with undetermined bounced events.
- `data.metrics.complained` (integer): Distinct message recipients with complained events.
- `data.metrics.deferred` (integer): Distinct message recipients with a deferral event.
- `data.metrics.rejected` (integer): Distinct message recipients rejected before provider delivery.
- `data.metrics.oob_bounces` (integer): Distinct out-of-band bounce events.
- `data.metrics.opens` (integer): Distinct open events, including prefetched opens.
- `data.metrics.opens_non_prefetched` (integer): Distinct open events excluding prefetched opens. An absent prefetch flag counts as false.
- `data.metrics.clicks` (integer): Distinct click events.
- `data.metrics.unsubscribes` (integer): Distinct unsubscribe events.
- `data.metrics.unique_opens` (integer): Distinct message recipients with an open event.
- `data.metrics.unique_opens_non_prefetched` (integer): Distinct message recipients with a non-prefetched open event.
- `data.metrics.unique_clicks` (integer): Distinct message recipients with a click event.
- `data.metrics.confirmed_unique_opens` (integer): Distinct message recipients with an open or click event, deduplicated across both.
- `data.metrics.confirmed_unique_opens_non_prefetched` (integer): Distinct message recipients with a non-prefetched open or click event, deduplicated across both.
- `data.metrics.effective_delivered` (integer): Delivered recipients less out-of-band bounce events, calculated as `max(delivered - oob_bounces, 0)`.
- `data.metrics.all_bounces` (integer): In-band bounced recipients plus out-of-band bounce events, calculated as `bounced + oob_bounces`.
- `data.metrics.delivery_rate` (nullable number): Ratio of `effective_delivered / (delivered + bounced)`, from 0 to 1. Null when `delivered + bounced` is zero.
- `data.metrics.bounce_rate` (nullable number): Ratio of `all_bounces / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero.
- `data.metrics.complaint_rate` (nullable number): Ratio of `complained / effective_delivered`. Uncapped and can exceed 1 when complaints and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.metrics.deferral_rate` (nullable number): Ratio of `deferred / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero.
- `data.metrics.open_rate` (nullable number): Ratio of `unique_opens_non_prefetched / effective_delivered`. Uncapped and can exceed 1 when opens and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.metrics.click_rate` (nullable number): Ratio of `unique_clicks / effective_delivered`. Uncapped and can exceed 1 when clicks and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.metrics.unsubscribe_rate` (nullable number): Ratio of `unsubscribes / effective_delivered`, using distinct unsubscribe events as the numerator. Uncapped and can exceed 1. Null when `effective_delivered` is zero.
- `data.metrics.oob_rate` (nullable number): Ratio of `oob_bounces / (delivered + bounced)`, using distinct out-of-band bounce events as the numerator. Uncapped and can exceed 1. Null when `delivered + bounced` is zero.
- `data.metrics.processing_p50_ms` (nullable integer): Processing latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.metrics.processing_p95_ms` (nullable integer): Processing latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.metrics.processing_p99_ms` (nullable integer): Processing latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.metrics.total_p50_ms` (nullable integer): Delivery latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.metrics.total_p95_ms` (nullable integer): Delivery latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.metrics.total_p99_ms` (nullable integer): Delivery latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series` (array of object): Present when grain is requested; absent otherwise.
- `data.series.bucket` (string, required): Nominal bucket start as a UTC RFC 3339 instant.
- `data.series.metrics` (object, required): Selected metric values. Counts are nonnegative approximate distinct counts. Period uniques and rates are computed independently of buckets; summing bucket or group values does not reconstruct period totals. Zero means a supported empty population; undefined rates and empty latency samples are null.
- `data.series.metrics.sends_accepted` (integer): Distinct sends accepted by Bird, counted by email identity.
- `data.series.metrics.accepted` (integer): Distinct message recipients accepted by Bird.
- `data.series.metrics.processed` (integer): Distinct message recipients processed for delivery.
- `data.series.metrics.delivered` (integer): Distinct message recipients with a delivery event.
- `data.series.metrics.bounced` (integer): Distinct message recipients with bounced events.
- `data.series.metrics.hard_bounced` (integer): Distinct message recipients with hard bounced events.
- `data.series.metrics.soft_bounced` (integer): Distinct message recipients with soft bounced events.
- `data.series.metrics.admin_bounced` (integer): Distinct message recipients with admin bounced events.
- `data.series.metrics.block_bounced` (integer): Distinct message recipients with block bounced events.
- `data.series.metrics.undetermined_bounced` (integer): Distinct message recipients with undetermined bounced events.
- `data.series.metrics.complained` (integer): Distinct message recipients with complained events.
- `data.series.metrics.deferred` (integer): Distinct message recipients with a deferral event.
- `data.series.metrics.rejected` (integer): Distinct message recipients rejected before provider delivery.
- `data.series.metrics.oob_bounces` (integer): Distinct out-of-band bounce events.
- `data.series.metrics.opens` (integer): Distinct open events, including prefetched opens.
- `data.series.metrics.opens_non_prefetched` (integer): Distinct open events excluding prefetched opens. An absent prefetch flag counts as false.
- `data.series.metrics.clicks` (integer): Distinct click events.
- `data.series.metrics.unsubscribes` (integer): Distinct unsubscribe events.
- `data.series.metrics.unique_opens` (integer): Distinct message recipients with an open event.
- `data.series.metrics.unique_opens_non_prefetched` (integer): Distinct message recipients with a non-prefetched open event.
- `data.series.metrics.unique_clicks` (integer): Distinct message recipients with a click event.
- `data.series.metrics.confirmed_unique_opens` (integer): Distinct message recipients with an open or click event, deduplicated across both.
- `data.series.metrics.confirmed_unique_opens_non_prefetched` (integer): Distinct message recipients with a non-prefetched open or click event, deduplicated across both.
- `data.series.metrics.effective_delivered` (integer): Delivered recipients less out-of-band bounce events, calculated as `max(delivered - oob_bounces, 0)`.
- `data.series.metrics.all_bounces` (integer): In-band bounced recipients plus out-of-band bounce events, calculated as `bounced + oob_bounces`.
- `data.series.metrics.delivery_rate` (nullable number): Ratio of `effective_delivered / (delivered + bounced)`, from 0 to 1. Null when `delivered + bounced` is zero.
- `data.series.metrics.bounce_rate` (nullable number): Ratio of `all_bounces / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero.
- `data.series.metrics.complaint_rate` (nullable number): Ratio of `complained / effective_delivered`. Uncapped and can exceed 1 when complaints and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.series.metrics.deferral_rate` (nullable number): Ratio of `deferred / (delivered + bounced)`, capped at 1. Null when `delivered + bounced` is zero.
- `data.series.metrics.open_rate` (nullable number): Ratio of `unique_opens_non_prefetched / effective_delivered`. Uncapped and can exceed 1 when opens and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.series.metrics.click_rate` (nullable number): Ratio of `unique_clicks / effective_delivered`. Uncapped and can exceed 1 when clicks and deliveries fall in different windows. Null when `effective_delivered` is zero.
- `data.series.metrics.unsubscribe_rate` (nullable number): Ratio of `unsubscribes / effective_delivered`, using distinct unsubscribe events as the numerator. Uncapped and can exceed 1. Null when `effective_delivered` is zero.
- `data.series.metrics.oob_rate` (nullable number): Ratio of `oob_bounces / (delivered + bounced)`, using distinct out-of-band bounce events as the numerator. Uncapped and can exceed 1. Null when `delivered + bounced` is zero.
- `data.series.metrics.processing_p50_ms` (nullable integer): Processing latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series.metrics.processing_p95_ms` (nullable integer): Processing latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series.metrics.processing_p99_ms` (nullable integer): Processing latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series.metrics.total_p50_ms` (nullable integer): Delivery latency at the 50th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series.metrics.total_p95_ms` (nullable integer): Delivery latency at the 95th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `data.series.metrics.total_p99_ms` (nullable integer): Delivery latency at the 99th percentile, in integer milliseconds from Bird acceptance. One sample per logical event; null when no eligible sample exists.
- `period` (object, required): Normalized half-open period. The response end is exclusive; replay the original inclusive request bounds when following cursors.
- `period.from` (string, required): Inclusive normalized start as a UTC instant.
- `period.to` (string, required): Exclusive normalized end as a UTC instant.
- `period.timezone` (string, required): Timezone used to normalize bounds and buckets.
- `period.grain` (nullable string, required): Requested grain, or null when no series was requested.
- `data_as_of` (nullable string, required): Always null for this endpoint. It does not report a refresh boundary, claim completeness, or record request time.
- `next_cursor` (nullable string, required): Pass as starting_after for the next grouped page. Null when no next page exists or the request is ungrouped.
- `prev_cursor` (nullable string, required): Pass as ending_before for the previous grouped page. Null when no previous page exists or the request is ungrouped.
- `refresh_cursor` (nullable string, required): Anchor for the first group. Pass as ending_before to read groups sorting before it. Null for empty or ungrouped results. Ranking can change between reads; refresh by repeating the original query.

## 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)
