Some limits come from Cloudflare, some from Amazon SES (only for domains that use it) and some from
Pylota Mail. Cloudflare’s and Amazon’s were read from their documentation on 2026-10-09 and can change.
pmail doctor reports the ones it can observe.
Account quota, set and raised by Cloudflare. It is not exposed to the Worker
Cloudflare
Queue backs off for up to 24 hours. An alert fires at 80% of PM_DAILY_SEND_QUOTA when you set it to your quota, otherwise on the first quota error (G3)
Mail domains per zone (routing + sending, including apex)
30
Cloudflare
Literal routing rules per domain (subdomain mail domains)
200
Cloudflare. So at most 200 addresses per subdomain mail domain. Apex domains use catch-all and have no limit
Literal routing rule matcher
90 characters
Cloudflare (Email Routing rules API, read 2026-10-09). An address on a subdomain mail domain longer than 90 characters is refused with 400 address_invalid
Catch-all
apex domains only
Cloudflare. This is why the platform domain must be a zone apex
Addresses per identity (all states)
20
Pylota Mail
Pending addresses per identity per domain
1
Pylota Mail
Address retirement grace
0–365 days, default 90
Pylota Mail
Forwarding test (inbound: forward)
The token must arrive within 10 minutes, otherwise forwarding is failed
At 9,000 the operator alert ses_identities_90pct fires and pmail doctor warns. At 10,000, adding a domain that needs an SES identity gets 422 transport_unavailable with details.reason = "ses_identity_limit". The count is the domains rows with ses_region set and not removed, plus the platform identity
When a pm-retired-{n} rule is full, the domain monitor opens the next one
Retired addresses bounced per deployment
75,000 (150 rules × 500)
Pylota Mail
Beyond it the oldest retired addresses leave the rules, and their mail is dropped without a bounce, like mail to an unknown address
SES API requests other than sends
1 per second per account and region; not adjustable
SES quotas (SES API sending quotas), read 2026-10-09
One deployment-wide token bucket (the SesControl Durable Object) admits one control-plane call per second. Domain create, PATCH and removal wait up to 5 s, then get 429 upstream_rate_limited with Retry-After; background checks wait up to 60 s, then retry later. Each SES domain’s daily identity check runs at a fixed time of day derived from a hash of its ID, so checks spread across the day (Domains on any DNS host §4.8)
Sending from the SES sandbox
200 messages per 24 hours, 1 per second, to verified addresses only
465 (TLS from the start) and 587 (STARTTLS) only. Any other port, 25 included: 400 smtp_port_not_allowed. A relay that offers no TLS: 422 smtp_tls_required, and the credentials are not sent
Cloudflare: “Workers cannot create outbound connections on port 25” (TCP sockets, read 2026-10-09); Pylota Mail
SMTP sends in parallel
4 per outbound consumer invocation
Cloudflare allows each invocation up to six connections waiting at once, and opening a socket counts (Workers limits, read 2026-10-09)
Connections per message
1, without pipelining
Pylota Mail
Timeouts
10 s to connect, 30 s per command, 60 s for the reply after the final dot
Pylota Mail. No reply after the final dot makes the send uncertain; it is never resent
Alignment probe
Before the first send and every day. On demand at most once a minute per domain (429 rate_limited). No probe back within 15 minutes is smtp_probe_timeout
On a deployment with billing on (Pylota Mail Cloud), the plan sets allowances for inboxes, sends, triage
analyses, custom domains, storage and seats. The table and the rules (holds, 402 billing_limit, top-ups,
resets) are in Plans and billing. Read your workspace’s live numbers with
GET /v1/usage. Self-hosted deployments have no plan limits unless the operator sets quotas in tenant policy.
10 per code; the token is burned after 10 failures
Sign-in requests per client IP (RL_SIGNIN)
10 per minute, keyed by CF-Connecting-IP, across sign-in, sign-up and waitlist requests
Link and code lifetime
10 minutes, single use
Two-step verification codes
5 attempts a minute per person. 10 failures in a row lock two-step sign-in for 15 minutes
Recovery codes
10 per person, each single use. Generating new ones invalidates the old
Google or GitHub sign-in
10 minutes from start to callback, single use
Waitlist
Unconfirmed entries are deleted after 7 days. An invitation’s sign-up link is valid for 7 days
New workspace on Free (Pylota Mail Cloud)
50 messages a day (tenant_daily_send_cap) for the first 7 days. The ramp lifts on day 7 if bounce and complaint rates are under the auto-pause thresholds, or at once on a paid plan. Above it: 429 daily_cap_reached