MOLTED EMAIL

Rate Limits

Monthly, daily, and hourly send limits and how to check your current usage.

Molted Email enforces rate limits across three time windows to protect deliverability and prevent abuse.

Rate limit windows

WindowReset cycle
MonthlyResets on the 1st of each month (UTC).
DailyResets at midnight UTC.
HourlyResets at the top of each hour (UTC).

Limits for each window depend on your plan (see Billing & Plans for prices and everything else a plan includes):

PlanMonthlyDailyHourly
Trial (before a plan is picked; sends are blocked)1005020
A paid plan's 3-day trial100 in total for the trial5020
Free3,000100 (ramps up, see Free plan ramp)40
Solo (and legacy Starter)10,0001,000200
Team (and legacy Growth)50,0005,0001,000
EnterpriseUnlimitedUnlimitedUnlimited
Expired000

The budget is per tenant, shared by every mailbox; there is no per-mailbox send allowance. A tenant with no quota row falls back to 100 a month, 10,000 a day and 1,000 an hour.

Checking usage

With an API key, check your current usage with GET /v1/agent/budget:

CLI
molted budget
curl
curl "https://mailbox.molted.email/v1/agent/budget" \
  -H "Authorization: Bearer YOUR_API_KEY"

MCP: check_budget (no parameters), or the molted://budget resource. A signed-in human gets the same send windows from GET /v1/me/usage (cookie session; an API key gets 401 there).

Response

Response (Solo)
{
  "tenantId": "tenant_abc123",
  "monthly": { "used": 420, "limit": 10000, "remaining": 9580 },
  "daily": { "used": 12, "limit": 1000, "remaining": 988 },
  "hourly": { "used": 3, "limit": 200, "remaining": 197 },
  "teamAddressSends": { "used": 4 },
  "inbound": { "used": 310, "limit": 5000, "remaining": 4690, "held": 0, "heldReason": null, "overAllowanceBehavior": "overage" },
  "negativeSignals": { "count": 0, "budget": 50, "remaining": 50 },
  "timestamp": "2026-10-03T12:00:00.000Z"
}

Past the monthly allowance on a plan with overage, monthly also carries inOverage, overageCount, hardCap and hardCapRemaining. negativeSignals is the tenant-wide count of bounces and complaints in the last 24 hours against its budget of 50 (see below). inbound is null when it can't be read.

Stored limits

GET /v1/ops/quotas returns the quota row stored for your workspace, as it is in the database (snake_case): tenant_id, daily_send_limit, hourly_send_limit and monthly_send_limit, plus the other per-workspace caps that row holds, such as overage_hard_cap, max_mailboxes, max_domains and storage_limit_bytes (a cap that isn't set is null). A workspace with no row gets { "tenant_id": "...", "daily_send_limit": 10000, "hourly_send_limit": 1000, "monthly_send_limit": 100 }, the fallback described above. Any valid API key can read it; there is no CLI command or MCP tool for it, and none of your keys can change it.

Use it to see the limits themselves. It says nothing about what you have used, and it doesn't show the Free ramp's current step. For usage and the limit in force right now, read GET /v1/agent/budget.

Free plan ramp

A new Free account's daily limit ramps up over its first two weeks (counted from when Free was activated): 20 a day in week 1, 50 a day in week 2, then the plan's 100 a day. During the ramp, daily.limit is the ramp step, daily.planLimit the plan's daily cap, and daily.freeRamp the details:

Response (Free, week 1)
{
  "daily": {
    "used": 12,
    "limit": 20,
    "remaining": 8,
    "planLimit": 100,
    "freeRamp": {
      "step": 0,
      "dailyLimit": 20,
      "startedAt": "2026-10-01T09:00:00.000Z",
      "complete": false,
      "held": false,
      "holdReason": null,
      "nextStepDailyLimit": 50,
      "nextStepAt": "2026-10-08T09:00:00.000Z"
    }
  }
}

The ramp only steps up while your sending is healthy. If your bounce rate is above 5% (from 20 sends on) or your complaint rate above 0.1%, or a mailbox was auto-paused for its reputation, the account stays at its current step (held: true, holdReason says which) until the rate recovers. Hitting the ramp's daily limit blocks with daily_limit_exceeded, like any daily limit. Accounts that were already on Free before the ramp shipped skip it.

Sends to your team are free

A send to a verified team address doesn't count toward the monthly or daily limits, the trial's total, or overage, on every plan including Free. It still counts toward the hourly limit, and every other check (suppression, dedupe, cooldown, consent, approvals) applies as usual, so an agent that emails its owner about an outage can't flood them.

Verified team addresses are your members' own verified emails, plus notification addresses you add and someone confirms through the link we email to them. An address that isn't confirmed yet counts like any other recipient. You can have 3 on Free, 10 on Solo and 25 on Team (members' emails included, and an address you removed keeps counting for 30 days). After a downgrade, your members' emails stay free and only the oldest confirmed addresses that fit the new limit do. We send at most 5 confirmation emails a day per workspace and 1 an hour per address, and a workspace that can't send yet (or any more) can't add addresses.

Team addresses

Add the addresses an agent may email for free, such as an on-call inbox:

molted team-addresses list
molted team-addresses add oncall@acme.com   # sends a confirmation link to that address
molted team-addresses remove <id>

Or GET / POST /v1/agent/notification-addresses and DELETE /v1/agent/notification-addresses/:id, with a key that covers all mailboxes. There is no MCP tool for team addresses. molted budget shows teamAddressSends for the month. A confirmation link is valid for 72 hours. Errors: 403 team_address_cap_reached (with the cap), 429 confirmation_rate_limited (with retryAfterSeconds), and 403 sends_blocked on a workspace that can't send. Hosted MCP (OAuth) apps can't add addresses (403 oauth_forbidden).

What happens when a limit is exceeded

When a window's limit is reached, subsequent send requests are blocked with a reason naming that specific window -- never the generic rate_limited (see Errors & Policy Blocks for when that one does show up, as a fallback):

{
  "requestId": "req_abc123",
  "status": "blocked",
  "reason": "hourly_limit_exceeded"
}

reason is one of hourly_limit_exceeded, daily_limit_exceeded, monthly_limit_exceeded, or overage_cap_exceeded, matching whichever window was hit. The request is not queued: wait for the window to reset or upgrade your plan.

On Solo, Team, the legacy Starter and Growth plans and Enterprise (once a paid plan's trial is over), passing the monthly allowance doesn't block: sends continue as billed overage up to a hard cap, twice the monthly allowance unless your account has its own cap. Past that the reason is overage_cap_exceeded. Free and trials have no overage, so the monthly allowance is a hard stop (monthly_limit_exceeded).

Two more tenant-wide stops:

  • free_sends_paused -- Molted has paused sending for every Free account (for example during abuse on the shared infrastructure). Temporary; paid plans are unaffected.
  • negative_signal_budget_exceeded -- 50 or more bounces plus complaints across the tenant in the last 24 hours blocks every send until the count drops.

What counts toward quota

Only a send that is actually accepted for delivery consumes quota, whether it was sent directly through the API, from a journey step, or as a scheduled follow-up -- all three draw from the same monthly/daily/hourly budget:

  • A send that is queued or scheduled (including a warmup-deferred slot).
  • A held send (mailbox autonomy level 1 or 2, or a per-thread risk hold) once a human approves it and it reaches the queue.
  • A journey send step or a scheduled follow-up sent at mailbox autonomy level 3 (auto-send). A journey step or follow-up held for approval (level 1 or 2) only consumes quota once a human approves it, same as any other held send.

Journey and follow-up sends split the limit into temporary and permanent reasons:

  • hourly_limit_exceeded / daily_limit_exceeded (and free_sends_paused) are temporary -- the window resets on its own, so the send is deferred and retried automatically once it does (a journey step re-enqueues and the run does not advance yet; a follow-up moves its scheduled_at out and stays pending). Nothing is recorded as blocked for a deferral, since nothing permanent happened.
  • monthly_limit_exceeded / overage_cap_exceeded are permanent for the rest of the billing period. A journey step is recorded across the send_requests row (flipped to blocked), a policy_decisions row with the reason, a mail.send.blocked audit event, and the journey.step_completed stream event's skipReason -- then the journey keeps advancing to its next step. A follow-up is recorded the same way (send_requests row blocked, a policy decision) and its own record is cancelled with cancel_reason set to the same reason, the same way a suppressed or billing-blocked follow-up is cancelled.

The following never consume quota, even though a send_requests row is created for them:

  • A send blocked by policy (suppression, cooldown, disengagement, an active opportunity, a paused tenant/mailbox, warmup or domain throttling, rate_limited itself).
  • An idempotent retry that hits an existing dedupeKey (returns the original result, duplicate).
  • A held send that is instead rejected, or that expires unanswered.

Sends can also be blocked for billing reasons:

  • trial_not_activated -- the account hasn't picked a plan yet.
  • subscription_expired -- the paid plan ended: cancelled, a paid trial that didn't convert, or a failed payment whose grace period ran out.

Storage quota

In addition to send rate limits, each plan has a storage quota that limits total message body and attachment storage. When the storage limit is exceeded, attachment uploads are refused with a plain 403 (there is no reason code):

{
  "statusCode": 403,
  "message": "Storage limit exceeded. Used: 1073741824 bytes, limit: 1073741824 bytes.",
  "error": "Forbidden"
}

Existing data remains accessible. See Storage & Attachments for details on storage limits and retention.

Request throttling

The API itself has no per-key request rate limit; the limits on this page are send limits. The exceptions are sign-up (3 an hour per IP) and sign-in and password reset (10 per 15 minutes per IP and per email), plus the per-route limits documented on their own pages (for example inbound hooks and alert test sends).

Opening a message in the portal or the phone app renders it on the server, so those views are limited too: GET /v1/me/messages/:id/view and GET /v1/me/send-approvals/:id/view (signed-in people only, never an API key) share a budget of 60 views a minute per person, counted on each API server. Over it, the API answers 429 with a Retry-After header and { "error": "rate_limited", "message": "Too many message views. Try again shortly.", "retryAfterSeconds": <n> }. Agents are not affected: they read messages through /v1/agent/threads/:id and /v1/agent/messages/:id/raw.

Related caps: per-sending-domain rate limits and the warmup schedule live on Domains.

Best practices

  • Monitor usage with molted budget (GET /v1/agent/budget) before large batch sends. A batch holds at most 500 sends.
  • Spread sends across hours to avoid hitting hourly limits.
  • If you regularly hit limits, consider upgrading your plan.