---
title: "Get email stats"
method: GET
path: "/api/apps/{app_id}/emails/stats"
---

# Get email stats

`GET /api/apps/{app_id}/emails/stats`

## Request

- Base URL: `https://app.base44.com`
- URL: `https://app.base44.com/api/apps/{app_id}/emails/stats`
- Auth: HTTP bearer

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Returns delivery and engagement counts for the emails the app sent, for each template and in total, over the last `days` days. Each count has a daily breakdown and the totals for the same number of days before, so you can compare periods.

Days are UTC. A send counts on the day it was sent, and a delivery, open or click on the day it happened, so a rate can include outcomes of emails sent before the period. Opens and clicks are counted only for emails sent with tracking on. Emails the app sends from its own email domain aren't counted yet, so an app that sends from its own domain gets zeros for those.

`templates` is paginated. Pass `next_cursor` back as `cursor`, with the same `days` and `limit`, to get the next page. Every page carries the same app-wide `totals`, `previous_totals` and `daily`.

Email stats need a paid workspace plan.

This is limited to 60 requests per minute per app for each workspace's personal API keys, so every key in a workspace shares one allowance. Some workspaces have a different limit. [List email templates](/api-reference/list-email-templates) shares it.

<Note>This endpoint accepts a personal API key belonging to a user with access to the app, including a read-only key. Workspace API keys are not accepted.</Note>

## Path parameters

- `app_id` string, required: ID of the app the emails belong to.

## Query parameters

- `days` integer: Number of days to count, from 1 to 90, ending today in UTC. Defaults to 30. The previous totals cover the same number of days just before.
- `limit` integer: Maximum number of entries in `templates` per page, from 1 to 200. Defaults to 50.
- `cursor` string: `next_cursor` from the previous page. Send the same `days` and `limit` with it. A cursor expires after 24 hours.

## Response `200`

The app's email stats.

- EmailStats: Delivery and engagement counts for the app's sent emails.
  - `days` integer, required: Number of days counted, as requested.
  - `templates` TemplateEmailStatsDoc[], required: Counts for each template that sent email in either period, ordered by `template_key`.
    - `template_key` string, required: Which template the counts are for: its `name`, or its `id` for emails sent by template ID. `_none` groups emails sent without a template. A deleted template keeps its entry for the emails it sent.
    - `totals` EmailStatTotalsDoc, required
      - `sent` integer, required: Emails sent.
      - `delivered` integer, required: Emails the recipient's mail server accepted.
      - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
      - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
      - `bounced` integer, required: Emails that bounced.
      - `spam` integer, required: Emails recipients marked as spam.
      - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
      - `delivered_rate` number, nullable, required: `delivered` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
      - `bounce_rate` number, nullable, required: `bounced` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
    - `previous_totals` EmailStatTotalsDoc, required
      - `sent` integer, required: Emails sent.
      - `delivered` integer, required: Emails the recipient's mail server accepted.
      - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
      - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
      - `bounced` integer, required: Emails that bounced.
      - `spam` integer, required: Emails recipients marked as spam.
      - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
      - `delivered_rate` number, nullable, required: `delivered` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
      - `bounce_rate` number, nullable, required: `bounced` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
    - `daily` EmailStatDayDoc[], required: One entry per day of the period, oldest first, with zeros on days with no activity.
      - `sent` integer, required: Emails sent.
      - `delivered` integer, required: Emails the recipient's mail server accepted.
      - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
      - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
      - `bounced` integer, required: Emails that bounced.
      - `spam` integer, required: Emails recipients marked as spam.
      - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
      - `day` string, date, required: The UTC day the counts are for.
    - `tracked` boolean, required: `false` when the template has no `{{unsubscribe_url}}` and recorded no opens or clicks in either period. Its `opened` and `clicked` zeros then mean opens and clicks weren't measured, not that nobody opened it.
  - `totals` EmailStatTotalsDoc, required
    - `sent` integer, required: Emails sent.
    - `delivered` integer, required: Emails the recipient's mail server accepted.
    - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
    - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
    - `bounced` integer, required: Emails that bounced.
    - `spam` integer, required: Emails recipients marked as spam.
    - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
    - `delivered_rate` number, nullable, required: `delivered` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
    - `bounce_rate` number, nullable, required: `bounced` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
  - `previous_totals` EmailStatTotalsDoc, required
    - `sent` integer, required: Emails sent.
    - `delivered` integer, required: Emails the recipient's mail server accepted.
    - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
    - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
    - `bounced` integer, required: Emails that bounced.
    - `spam` integer, required: Emails recipients marked as spam.
    - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
    - `delivered_rate` number, nullable, required: `delivered` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
    - `bounce_rate` number, nullable, required: `bounced` divided by `sent`, from 0 to 1, or `null` when nothing was sent.
  - `daily` EmailStatDayDoc[], required: One entry per day of the period, oldest first, with zeros on days with no activity. Covers every template, on every page.
    - `sent` integer, required: Emails sent.
    - `delivered` integer, required: Emails the recipient's mail server accepted.
    - `opened` integer, required: Opens, counting each open, so one recipient can count more than once.
    - `clicked` integer, required: Link clicks, counting each click, so one recipient can count more than once.
    - `bounced` integer, required: Emails that bounced.
    - `spam` integer, required: Emails recipients marked as spam.
    - `unsubscribed` integer, required: Recipients who unsubscribed through the email.
    - `day` string, date, required: The UTC day the counts are for.
  - `next_cursor` string, nullable, required: Pass it as `cursor` to get the next page of `templates`, or `null` on the last page.

## Other responses

- `400`: `cursor` is invalid or expired, or doesn't match the `days` and `limit` you sent with it.
- `401`: Missing or invalid credentials.
- `402`: The workspace's plan doesn't include email stats.
- `403`: You don't have access to this app, the app is blocked, or you used a workspace API key.
- `404`: App not found.
- `422`: `days` is outside 1 to 90, or `limit` is outside 1 to 200.
- `429`: Rate limit exceeded. The base limit is 60 requests per minute. See [Rate limits](/developers/references/apps-api/get-started/rate-limits) for the multiplier your plan gets.

## Changes

- **2026-10-21** `c23775445de2`: 1 breaking, 1 info
  - `query` request parameter `cursor` list-of-types was narrowed by removing types `null`
  - for the `query` request parameter `cursor`, the type was generalized from no type to `string`
- **2026-10-13** `06cfcce0bdd9`: 2 info
  - the security scope `read_only` was added to the endpoint's security scheme `PersonalAccessTokenAuth`
  - the security scope `full_access` was removed from the endpoint's security scheme `PersonalAccessTokenAuth`
- **2026-10-11** `80287813ab84`: 1 info
  - the endpoint scheme security `PersonalAccessTokenAuth` was added to the API
- **2026-10-10** `fc604cdcc3a2`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/idealspot/apis/base44-app-management-api/changes/api/apps/:app_id/emails/stats/get.md)

---

[API](https://skmtc.dev/idealspot/apis/base44-app-management-api.md) - [All operations](https://skmtc.dev/idealspot/apis/base44-app-management-api/llms.txt) - [OpenAPI document](https://skmtc.dev/idealspot/apis/base44-app-management-api/revisions/b5b4ebd82a1e?raw)
