# Plans and Usage Tracking (/advanced/plans-and-usage)

<!-- agent-signals: reading_time_min: 10 · est_tokens: 3816 · updated: 2026-09-06 -->
Related: [Webhooks](/advanced/webhooks.md), [WebSockets](/advanced/websockets.md), [Agent safety](/advanced/safety.md), [Custom domains](/advanced/custom-domains.md), [Deliverability and warmup](/advanced/deliverability.md), [Build a multi-tenant platform](/advanced/multi-tenant.md)



# Track plan limits and usage

AgentMail plans meter outbound sends and cap how many inboxes, domains, and members an organization holds, while receiving email carries no billable event. Read the live catalog and your organization's effective limits before provisioning, then query the metrics endpoints to count email events and track running resource totals.

## Do this

```bash
# 1. Read the live plan and add-on catalog. No auth header needed.
#    null on an entitlement means the plan has no cap on that field.
curl "https://api.agentmail.to/billing/catalog"

# 2. Read the limits actually enforced for your organization.
curl "https://api.agentmail.to/v0/organizations" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
# -> billing_plan_id, inbox_count and inbox_limit, domain_count and domain_limit,
#    five_minute_send_limit, daily_send_limit. An absent limit field means no cap.

# 3. Count email events over a window: sent and bounced, 1-hour buckets, last 24 hours.
curl "https://api.agentmail.to/v0/metrics/events?event_types=message.sent,message.bounced&period=3600" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

# 4. Read current resource totals. The newest usage point carries the total right now.
curl "https://api.agentmail.to/v0/metrics/usage?descending=true&limit=1" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

## SDK

Install: `npm install -g agentmail-cli` (CLI), `npm install agentmail` (TypeScript), `pip install agentmail` (Python).

* Effective limits: CLI `agentmail organizations get`, TypeScript `client.organizations.get()`, Python `client.organizations.get()`
* Event counts: TypeScript `client.metrics.queryEvents({ eventTypes, period })`, Python `client.metrics.query_events(event_types=[...], period=...)`
* Usage totals: TypeScript `client.metrics.queryUsage({ usageTypes, period })`, Python `client.metrics.query_usage(usage_types=[...], period=...)`
* One inbox: TypeScript `client.inboxes.metrics.queryEvents("example@agentmail.to", { eventTypes })` and `queryUsage`, Python `client.inboxes.metrics.query_events(inbox_id=..., event_types=[...])` and `query_usage`
* One pod: TypeScript `client.pods.metrics.queryUsage("<pod_id>", { usageTypes })` and `queryEvents`, Python `client.pods.metrics.query_usage(pod_id=..., usage_types=[...])` and `query_events`

Client setup and the full surface reference: /integrations/sdks-and-cli

## Facts

* Receiving email is free. Inboxes receive, store, and index mail without a billable event. Plans meter outbound sends and cap inboxes, domains, and members, and creation is blocked once an allowance is exhausted.
* Free plan: $0 per month, 3 inboxes, 0 domains, 1 member, 100 sends per day, no published five-minute cap, no monthly send cap, 3 GB mail storage, no annual option. The 100-per-day quota works out to about 3,000 emails per month.
* Developer plan: $20 per month or $192 per year (20 percent off, billed yearly), 10 inboxes, 10 domains, 2 members, 100 sends per 5 minutes, 1,000 sends per day, 10,000 sends per month, 10 GB mail storage, email support, no recipient ramp.
* Startup plan: $200 per month or $1,920 per year (20 percent off, billed yearly), 150 inboxes, 150 domains, 10 members, 1,500 sends per 5 minutes, 15,000 sends per day, 150,000 sends per month, mail storage not published, SOC 2 report access, no recipient ramp.
* Console sign-up starts an organization on Free with no credit card. CLI agent-flow sign-up starts it on the Agent plan: 1 inbox, 0 domains, 1 member, 10 sends per day, no five-minute or monthly cap, no recipient ramp.
* Agent plan numbers are pre-verification limits, not a tier to stay on. Verifying with the 6-digit code emailed at sign-up moves the organization to Free at no cost, and Agent-plan limit errors carry a `fix` pointing at that verification call.
* Free plan recipient ramp: at most 5 distinct recipients in the organization's first 24 hours and at most 10 cumulative in its first week. The ramp lifts entirely after 7 days. Upgrading to a paid plan removes it immediately, not at the window boundary.
* The ramp counts people, not addresses or messages. Addresses that emailed one of your inboxes first never count. Plus-tag forms and Gmail dot variants of one mailbox occupy a single slot. Recipients are counted when the send is attempted, even if the send fails later.
* Send quotas are counted per organization, across all inboxes. Each recipient counts as one send: a message with three addresses across `to`, `cc`, and `bcc` consumes three sends. Replies are outbound sends.
* Windows are fixed, not rolling. The daily window resets at midnight UTC. The monthly quota uses a fixed 30-day window, not the calendar month.
* Crossing 80 percent of the monthly send allowance triggers a one-time usage warning email for that window.
* Distributing sends across inboxes does not change the organization quota, but mailbox providers throttle per address, so 10 emails each from 100 inboxes lands better than 1,000 from one inbox.
* Safe retries: pass `client_id` on create operations so a retry cannot duplicate a resource. For sends, track the `message_id` already sent, or stage the message as a draft and send the draft.
* Add-ons, paid plans only, bought self-serve in the Console under Billing: +1 inbox for $2 per month, +1 custom domain for $2 per month, +1,000 monthly sends for $2 per month. Annual units cost $1.60 per month per unit billed yearly, the same 20 percent discount.
* Add-on increases are charged pro rata and take effect on the very next API call. Decreases take effect at the end of the billing period, and a pending decrease can be cancelled before then.
* Send add-ons recompute the daily limit as one tenth and the five-minute limit as one hundredth of the new effective monthly allowance, never below the plan's base values. Five send blocks on Developer yield 15,000 per month, 1,500 per day, and 150 per five minutes.
* Example add-on bill: a Developer organization with 15 inboxes and one extra domain pays $20 + $10 + $2 = $32 per month.
* Plan and add-on limits are enforced at three points: inbox creation, domain creation, and send.
* Enterprise usage-based agreements are hand-negotiated (contact `support@agentmail.cc`). The standard entitlement model has no fixed inbox, domain, member, five-minute, daily, or monthly cap, and agreements unlock everything in Startup plus unlimited inboxes, bulk discounts, a white-label platform, EU region cloud, bring-your-own-cloud deployment, dedicated IPs, OIDC and SAML SSO, an SLA, and a dedicated Slack support channel.
* `GET https://api.agentmail.to/billing/catalog` is unauthenticated (the path works with or without the `/v0` prefix), lists every self-serve plan and add-on, uses `null` for a field with no cap, prices add-on units in cents, marks purchasable add-ons with `sellable: true`, and is cached for about five minutes.
* `GET /v0/organizations` returns the effective enforced values, adjusted by add-ons, support grants, and account state: `billing_plan_id`, `inbox_count` with `inbox_limit`, `domain_count` with `domain_limit`, `five_minute_send_limit`, `daily_send_limit`. An absent limit field means no cap on that resource.
* Metrics queries require the `metrics_read` permission. A key created without a `permissions` object holds every permission. A key created with one holds only the operations set to `true`, and the key's resource scope still applies.
* Metrics windows default to the last 24 hours. History goes back 90 days. A future `end` is clamped to the current time.
* `GET /v0/metrics/events` params, all optional and in the query string: `event_types` (comma separated, omit to count every type), `start` and `end` (RFC 3339), `period` (whole seconds, 1 through 86400, omit for one total per type), `limit` (buckets per type, 1 through 1000, default 1000), `descending` (default `false`, oldest first).
* Valid `event_types`: `message.received`, `message.received.spam`, `message.received.blocked`, `message.received.unauthenticated`, `message.sent`, `message.delivered`, `message.bounced`, `message.complained`, `message.rejected`, `message.opened`, `domain.verified`. These are the same events webhooks deliver.
* Event responses map each queried type to `{ timestamp, count }` buckets. A bucket's timestamp is the end of its window, the newest bucket closes at the query time, and a type with no matching events returns an empty list.
* An event window spanning more than 1000 buckets is still served, truncated to the 1000 buckets nearest the oldest edge (nearest the newest edge with `descending=true`).
* `GET /v0/metrics/usage` takes `usage_types` in place of `event_types`. The other params keep the event query's names, types, and defaults, except `period` defaults to 600, so a bare call returns the last 24 hours at 10-minute resolution.
* Usage types by scope: inbox has `storage_bytes`, `message_count`, `thread_count`. Pod adds `inbox_count` and `domain_count`. Organization adds `pod_count`. Omit `usage_types` to get every type valid for the scope.
* Usage responses map each type to `{ timestamp, value }` points where `value` is the cumulative total at that moment. The series holds flat between changes. The newest point is stamped at the query time with the current total, so `descending=true` with `limit=1` reads usage right now.
* Scoped paths: `GET /v0/pods/{pod_id}/metrics/events`, `GET /v0/pods/{pod_id}/metrics/usage`, `GET /v0/inboxes/{inbox_id}/metrics/events`, `GET /v0/inboxes/{inbox_id}/metrics/usage`. Params, permission, and response shapes are identical at every scope. A pod- or inbox-scoped key on the unscoped paths automatically reads its own pod or inbox.
* A `401` on a metrics call means the API key is missing or invalid.

## Not supported

* The Free plan cannot buy add-ons. A Free organization that needs a custom domain upgrades to Developer first.
* The enterprise usage-based model is not a self-serve selection.
* Retrying before a daily or monthly quota resets does not create additional capacity. Treat those windows as terminal and alert. A five-minute rejection clears in minutes.
* Meeting a monthly forecast does not guarantee a burst is accepted. The five-minute rate limit applies at the same time as the period quotas.
* Event responses do not emit zero-count buckets. Quiet periods appear as gaps, not zeros.
* Requesting a usage type the queried scope does not carry returns no error. The key is simply absent from the response.
* The usage query does not truncate oversized windows the way the event query does. A window over 1000 buckets is rejected with `validation_error`. With the default `period` of 600 the cap is just under seven days.
* `GET /v0/metrics` without the `/events` suffix is a deprecated alias at every scope. Existing calls keep working, but point new code at `/v0/metrics/events` (`queryEvents`, `query_events`).
* Add-ons are not a substitute for a negotiated delivery policy when the required sending pattern exceeds the Startup plan's limits.

## Errors

| Error                 | HTTP | Cause                                                                                                                                                                                                                  | Fix                                                                                                                                                                                                                                                                                                                               |
| --------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `limit_exceeded`      | 403  | An inbox or domain create hit the plan's resource cap                                                                                                                                                                  | Delete a resource, buy an add-on, or upgrade. The body's `fix` names the cheapest plan that raises the cap, and the `x-agentmail-resource`, `x-agentmail-limit`, and `x-agentmail-remaining` headers carry the same facts                                                                                                         |
| `rate_limit_exceeded` | 429  | A send exceeded the five-minute, daily, or monthly window, or the Free plan recipient ramp                                                                                                                             | Honor `Retry-After` (seconds until reset, mirrored in `ratelimit-reset`, cap in `ratelimit-limit`). Branch on `window`: five-minute clears in minutes, daily and monthly are terminal for the window. For ramp rejections, replies to inbound mail are always allowed, or upgrade, or wait for the lift time named in the message |
| `missing_permission`  | 403  | The key's permissions whitelist lacks `metrics_read`                                                                                                                                                                   | Use a key created without a `permissions` object, or one that sets `"metrics_read": true`                                                                                                                                                                                                                                         |
| `validation_error`    | 400  | A metrics param is out of range: `start` over 90 days old or in the future, `end` before `start`, `period` or `limit` outside range or fractional, an unknown `event_types` value, or a usage window over 1000 buckets | The `errors` array names the field. Correct it, or raise `period` or narrow the window                                                                                                                                                                                                                                            |

## Verify

```bash
curl "https://api.agentmail.to/v0/organizations" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
```

`200` with `billing_plan_id` and paired counts and limits confirms the key works and shows provisioning headroom. Compare `inbox_count` with `inbox_limit` before a batch of creates.

## Related

* /advanced/errors - the error code reference and idempotent retries with `client_id`
* /advanced/webhooks - react to the same events on delivery instead of polling counts
* /advanced/multi-tenant - where a `pod_id` comes from and per-tenant usage metering
* /advanced/deliverability - what a rising bounce count points to
* /integrations/sdks-and-cli - client setup and the SDKs' built-in `429` retries
* /quickstart - get an API key, including the CLI agent sign-up flow
