Skip to content
AgentMail
AgentMail
Advanced

Plans and Usage Tracking

Compare the limits of every plan, work within the Free plan's ramp, buy add-on capacity, and measure email activity and resource usage over time.

Compare limits across plans

Receiving email is free. Inboxes receive, store, and index mail without a billable event, so an inbound workflow costs nothing. Plans meter outbound sends and cap how many inboxes, domains, and members an organization can hold. Once an allowance is exhausted, creation is blocked.

LimitFreeDeveloperStartup
Monthly price$0$20$200
Annual price (20 percent off, billed yearly)Not offered$192$1,920
Inboxes310150
Domains010150
Members1210
Sends per 5 minutesNo published cap1001,500
Sends per day1001,00015,000
Sends per monthNo monthly cap10,000150,000
Mail storage3 GB10 GBNot published
New-sender recipient ramp5 recipients in day one, 10 in week oneNoneNone

Beyond the limits, Developer includes email support and Startup includes access to AgentMail’s SOC 2 report.

Read the live plan catalog

The canonical catalog, add-ons included, is published at an unauthenticated endpoint, the same source the Console and pricing page render from:

API
curl "https://api.agentmail.to/billing/catalog"

No Authorization header is needed, and the path works with or without the /v0 prefix. The response lists every self-serve plan with its price and entitlements, where null means the plan has no cap on that field, plus the add-ons with unit prices in cents. An add-on is purchasable when its sellable flag is true. Responses are cached for about five minutes.

Sample response (one plan and one add-on shown)
{
  "version": 1,
  "plans": [
    {
      "id": "developer",
      "name": "Developer",
      "description": "For developers making small scale applications",
      "monthly_price_usd": 20,
      "entitlements": {
        "max_inboxes": 10,
        "max_domains": 10,
        "max_memberships": 2,
        "max_five_minute_sends": 100,
        "max_daily_sends": 1000,
        "max_monthly_sends": 10000,
        "max_first_day_recipients": null,
        "max_first_week_recipients": null,
        "max_tracking_domains": 0
      },
      "available": true,
      "yearly": {
        "lookup_key": "developer_yearly",
        "price_usd": 192,
        "discount_percent": 20,
        "available": true
      }
    }
  ],
  "addons": [
    {
      "lookup_key": "addon_sends",
      "label": "Monthly sends",
      "unit": "block",
      "grants_per_unit": 1000,
      "sellable": true,
      "monthly": { "unit_amount": 200, "currency": "usd", "available": true },
      "yearly": {
        "lookup_key": "addon_sends_yearly",
        "unit_amount": 1920,
        "currency": "usd",
        "available": true
      }
    }
  ]
}

Read this endpoint instead of hardcoding plan numbers into your own tooling, so a price or limit change reaches you without a redeploy.

Check your organization’s effective limits

The limits live on the organization record, so read them from the API instead of assuming the catalog numbers. Use this before provisioning to catch resource exhaustion early, and alert when a count approaches its limit.

agentmail organizations get

The response pairs each resource count with its limit and tells you which plan the enforcement comes from:

  • inbox_count against inbox_limit and domain_count against domain_limit are the provisioning headroom. Compare them before a batch of creates.
  • billing_plan_id is the plan behind the numbers.
  • five_minute_send_limit and daily_send_limit are the send windows enforced for this organization.
  • A limit field that is absent means the organization has no cap on that resource.

These are the effective values enforcement uses, not copies of the catalog. Add-ons, support grants, and account state all adjust them, so an organization’s numbers can differ from the plan table above.

Sample response
{
  "organization_id": "1a2b3c4d-5e6f-4a1b-8c2d-3e4f5a6b7c8d",
  "authentication_type": "clerk",
  "authentication_id": "org_1a2b3c4d5e6f7a8b9c0d",
  "billing_plan_id": "free",
  "inbox_limit": 3,
  "domain_limit": 0,
  "five_minute_send_limit": 1000,
  "daily_send_limit": 100,
  "inbox_count": 2,
  "domain_count": 0,
  "tracking_allowed": false,
  "name": "Example organization",
  "clerk_organization_id": "org_1a2b3c4d5e6f7a8b9c0d",
  "created_at": "2026-08-10T09:15:24.312Z",
  "updated_at": "2026-08-24T18:02:41.559Z"
}

Start on the Free plan

Your sign-up path decides the plan you start on:

  • Signing up through the Console starts your organization on Free. Free needs no credit card.
  • Signing up through the CLI agent flow starts it on the Agent plan: 1 inbox, 0 domains, 1 member, and 10 sends per day, with no five-minute or monthly cap and no recipient ramp. These are pre-verification limits, not a tier you stay on. Verifying with the 6-digit code emailed at sign-up moves the organization to Free at no cost, and limit errors on the Agent plan carry a fix that points at that verification call.

Free’s enforced send quota is the daily one. Over a month it works out to about 3,000 emails, the number the pricing page advertises.

Send within the recipient ramp

Organizations on the Free plan start with a cap on how many different people they can cold-email, separate from the send quotas:

  • at most 5 distinct recipients in the organization’s first 24 hours
  • at most 10 distinct recipients, cumulative, in its first week

After 7 days the ramp lifts entirely. Paid plans have no ramp, and upgrading removes the cap immediately rather than at the window boundary. The Agent plan is also exempt, since its 10-per-day send cap is the control there.

The cap counts people, not addresses or messages:

  • Addresses that have emailed one of your inboxes first never count. Your agent can always answer its inbound mail.
  • Variants of one mailbox count once: plus-tag forms like you+test@example.com and Gmail dot variants all occupy a single slot.
  • Recipients are counted when the send is attempted, so a send that fails later still used its slots.

If your first week involves emailing more than a handful of new contacts, upgrade before the launch instead of engineering around the ramp.

Handle rate limits and buy add-ons

Two kinds of limit apply to every outbound send:

  • A period quota caps the total sends accepted in a window (daily and monthly).
  • A rate limit protects a short burst window (five minutes).

Both apply at once, so meeting a monthly forecast does not guarantee that a burst will be accepted. Estimate against your busiest window, not just your monthly total.

How sends are counted:

  • Quotas are counted per organization, across all of its inboxes.
  • Each recipient counts as one send. A message with three addresses across to, cc, and bcc consumes three sends.
  • Replies are outbound sends. Include them in your estimate.

The windows are fixed rather than rolling. The daily window resets at midnight UTC, and the monthly quota uses a fixed 30-day window, not the calendar month. When your organization crosses 80 percent of its monthly send allowance, AgentMail emails you a one-time usage warning for that window.

When a send is rejected for a quota or rate limit, the API returns HTTP 429 with the error code rate_limit_exceeded. The body names the window that bound, its cap, and the upgrade that raises it, and the response carries three headers: Retry-After (seconds until the window resets), ratelimit-limit (the cap), and ratelimit-reset (same seconds as Retry-After).

429 Sample response
{
  "name": "RateLimitError",
  "code": "rate_limit_exceeded",
  "message": "Daily send limit exceeded",
  "fix": "Your plan's daily send limit is 100, and resets in 28800s (also returned in the Retry-After header). Wait for the window to reset, or upgrade at https://console.agentmail.to/dashboard/upgrade — Developer raises it to 1000 for $20.00/month.",
  "resource": "send",
  "window": "daily",
  "limit": 100,
  "upgrade_url": "https://console.agentmail.to/dashboard/upgrade",
  "docs": "https://docs.agentmail.to/errors#rate_limit_exceeded"
}

Handle a 429 by honoring Retry-After and backing off exponentially (the SDKs retry these for you). Branch on window before you let a retry loop run: a five-minute rejection clears in minutes, while retrying before a daily or monthly quota resets will not create additional capacity, so treat those as terminal for the window and alert instead.

Habits that keep high-volume agents inside the limits:

  • Distribute sends across inboxes. Your organization’s quota does not change, but mailbox providers throttle per address, so 10 emails each from 100 inboxes lands better than 1,000 from one.
  • Make retries safe. Pass client_id on create operations so a retry cannot duplicate a resource, covered in errors and idempotency. For sends, track the message_id of what you already sent, or stage the message as a draft and send the draft, so a retry cannot email the same person twice.
  • Watch usage before launches. The Console and the usage metrics both show send volume against your window.

Developer and Startup customers can buy extra capacity on top of their plan, one unit at a time, instead of jumping tiers:

  • +1 inbox for $2 per month
  • +1 custom domain for $2 per month
  • +1,000 monthly sends for $2 per month

Buy as many units of each as you need. A Developer organization that wants 15 inboxes and one extra domain pays $20 + $10 + $2 = $32 per month. Add-ons are available on the paid plans, so a Free organization that needs a custom domain upgrades to Developer first.

The flow is self-serve in the Console, under Billing: drag the slider for the resource you are adding, review the prorated amount for the rest of the billing period, and pay. Increases are charged pro rata and take effect instantly, enforced on the very next API call. Decreases are scheduled for the end of the billing period, and you can cancel a pending decrease any time before it takes effect. Annual subscriptions buy annual add-on units at the same 20 percent discount, which works out to $1.60 per month per unit billed yearly.

Send add-ons raise more than the monthly number. The daily and five-minute limits are recomputed from the new effective monthly allowance, at one tenth per day and one hundredth per five minutes, and never drop below the plan’s base values. Five send blocks on Developer, for example, make the allowance 15,000 per month, 1,500 per day, and 150 per five minutes.

Add-on limits are enforced at the same three points as plan limits: inbox creation, domain creation, and send.

When add-ons are not enough, choose the upgrade path based on what is about to run out:

  • If your forecast fits the next listed allowance, move to that plan.
  • If you need more members, move to the plan whose member allowance covers your team, or arrange an enterprise agreement.
  • If Startup’s allowances do not cover the required burst, daily volume, monthly volume, or resource count, arrange an enterprise usage-based agreement with negotiated rates and limits.

The usage-based model is for hand-negotiated enterprise workloads, not a self-serve selection. Its standard entitlement model has no fixed inbox, domain, member, five-minute, daily, or monthly cap, though a negotiated agreement can still set limits for deliverability or operational reasons. Enterprise agreements also 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. Contact support@agentmail.cc for enterprise pricing and limits. Add-ons are not a substitute for a negotiated delivery policy when your required sending pattern exceeds the Startup plan’s limits.

Measure your usage

AgentMail keeps two read-only series about your account, and each answers a different kind of question:

  • Event counts tell you what happened during a time window: how many emails were sent, delivered, bounced, or received, grouped by event type and time bucket. Reach for them to investigate whether activity changed during a period.
  • Usage totals tell you what a scope holds over time: running totals of storage, messages, threads, inboxes, domains, and pods. A point is the total at that time, not the change since the previous point.
QuestionQueryResult
How many delivery or mailbox events happened during a window?Event countsEvent types mapped to { timestamp, count } buckets
How much of a resource exists over time?Usage totalsUsage types mapped to { timestamp, value } points

Both queries share the same time controls. They default to the last 24 hours, and history goes back 90 days: a start older than that is rejected, and a future end is clamped to the current time.

Every metrics query requires the metrics_read permission:

  • A key created without a permissions object holds every permission, metrics_read included.
  • A key created with a permissions object holds only what the object sets to true, so include "metrics_read": true. A permission omitted from the object or set to false is denied.
  • The key’s resource scope still applies. metrics_read lets a key read metrics within its own organization, pod, or inbox scope, not beyond it.

Count email events over a window

Results come back grouped by event type and time bucket. This example counts sent and bounced mail in one-hour buckets over the last 24 hours.

Every parameter is optional, and all of them go in the query string:

curl "https://api.agentmail.to/v0/metrics/events?event_types=message.sent,message.bounced&period=3600" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"
ParamTypeWhat it means
event_typesstring[]The event types to count, comma separated in the URL. Omit it to count every type.
startRFC 3339 timestampBeginning of the window. Defaults to 24 hours ago, and must be within the last 90 days.
endRFC 3339 timestampEnd of the window. Defaults to now. A future value is clamped to now.
periodintegerBucket size as a whole number of seconds, 1 through 86400. Omit it for a single total per event type across the window.
limitintegerThe most buckets returned per event type, 1 through 1000. Defaults to 1000.
descendingbooleanNewest buckets first. Defaults to false, oldest first.

event_types accepts these values:

  • message.received and its flagged variants message.received.spam, message.received.blocked, and message.received.unauthenticated for inbound mail
  • message.sent, message.delivered, message.bounced, message.complained, and message.rejected for the stages of outbound mail
  • message.opened for the first open of a tracked message
  • domain.verified for a custom domain completing verification

These are the same events webhooks deliver, and the event table describes when each one fires.

The response has one key per queried event type, each holding { timestamp, count } buckets. How to read them:

  • A bucket’s timestamp is the end of its window, so the 10:00:00Z bucket below counts events between 09:00 and 10:00.
  • The newest bucket closes at the query time instead of on the grid. That bucket is still filling, which is why its timestamp is not a round number.
  • Buckets where nothing happened are skipped, so quiet hours show up as gaps rather than zeros, and a type with no matching events at all comes back as an empty list.
  • Without period, each type gets a single bucket holding its total for the whole window.
Sample response
{
  "message.sent": [
    { "timestamp": "2026-08-25T09:00:00Z", "count": 36 },
    { "timestamp": "2026-08-25T10:00:00Z", "count": 18 },
    { "timestamp": "2026-08-25T10:41:12.583Z", "count": 3 }
  ],
  "message.bounced": []
}

Compare the message.sent, message.delivered, and message.bounced buckets for the same window to watch delivery health. Sends that stop turning into deliveries, or a growing bounce count, are an early sign of a deliverability problem.

Requests that push past the limits behave like this:

  • With a period set, a window that spans more than 1000 buckets is still served, but only for the 1000 buckets nearest the oldest edge (nearest the newest edge with descending=true). Use a larger period, or shift start and end to page through the rest.

GET /v0/metrics without the /events suffix is a deprecated alias for this query, at every scope. Existing calls keep working, but point new code at /v0/metrics/events and the queryEvents and query_events methods.

Track what a scope holds over time

The usage query returns running totals. Use it when the question is about amounts rather than activity: how much storage the mail takes up, or how many messages, threads, inboxes, domains, or pods exist. This example reads daily storage and message totals.

curl "https://api.agentmail.to/v0/metrics/usage?usage_types=storage_bytes,message_count&period=86400" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

usage_types takes the place of event_types. The other parameters keep the names, types, and defaults of the event query, with one difference: period here defaults to 600, so a call with no parameters returns the last 24 hours at 10-minute resolution.

Which usage types carry data depends on the scope you query:

Queried scopeUsage types
Inboxstorage_bytes, message_count, thread_count
PodThe inbox types plus inbox_count and domain_count
OrganizationThe pod types plus pod_count

Omit usage_types to get every type valid for the queried scope. A requested type that does not apply to the scope is dropped from the response without an error, so asking an inbox for pod_count returns no such key.

The response has one key per usage type, each holding { timestamp, value } points where value is the cumulative total at that moment. Between changes the series holds flat rather than skipping buckets, so any point reads as the total at that time. The newest point is stamped at the query time and carries the current total, which makes descending=true with limit=1 the quickest way to read usage right now. A type the scope holds nothing of comes back as an empty list.

Sample response
{
  "storage_bytes": [
    { "timestamp": "2026-08-25T00:00:00Z", "value": 453786 },
    { "timestamp": "2026-08-25T10:41:14.207Z", "value": 560307 }
  ],
  "message_count": [
    { "timestamp": "2026-08-25T00:00:00Z", "value": 134 },
    { "timestamp": "2026-08-25T10:41:14.207Z", "value": 168 }
  ]
}

To judge inbox_count or domain_count against the caps your plan enforces, read them next to your organization’s effective limits.

Query one pod or inbox

The paths above read at your key’s scope. An organization key gets numbers for the whole organization, while a key scoped to a pod or inbox automatically gets its own pod or inbox. To pin a query to one pod or inbox explicitly, put its id in the path:

  • GET /v0/pods/{pod_id}/metrics/events and GET /v0/pods/{pod_id}/metrics/usage read one pod
  • GET /v0/inboxes/{inbox_id}/metrics/events and GET /v0/inboxes/{inbox_id}/metrics/usage read one inbox

The parameters, permission, and response shapes are the same at every scope, and the SDK methods keep their names under client.inboxes.metrics and client.pods.metrics:

# events for one inbox
curl "https://api.agentmail.to/v0/inboxes/example@agentmail.to/metrics/events?event_types=message.received&period=3600" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

# usage for one pod
curl "https://api.agentmail.to/v0/pods/<pod_id>/metrics/usage?usage_types=inbox_count&period=86400" \
  -H "Authorization: Bearer $AGENTMAIL_API_KEY"

A pod_id comes from creating or listing pods, covered in multi-tenant email.

Sample response
{
  "inbox_count": [
    { "timestamp": "2026-08-25T00:00:00Z", "value": 3 },
    { "timestamp": "2026-08-25T10:41:16.331Z", "value": 3 }
  ]
}

To match the query to the check:

  • For capacity monitoring, query usage on the scope where the limit or resource decision applies, then compare consecutive points to see whether the totals are growing.
  • For an investigation into event volume, request the relevant event types with a period that matches the incident window instead of deriving activity from usage totals.
  • Use the inbox path to investigate a single inbox and the pod path to check a whole tenant, so the result covers that pod’s inboxes without mixing in another pod’s.

Next Steps

Was this page helpful?Suggest editsRaise issue