Rate Limiting
Client-side rate limiting protects against sending too many messages from a single inbox or to a single recipient. Limits use a sliding time window and are enforced on all send operations — sendMessage, replyToMessage, replyAllToMessage, and forwardMessage.
Per-Sender Limiting
Tracks messages by sender inbox ID. Each inbox has its own independent counter:
suspend fun perSenderExample() {
val client = AgentMailClient {
perSenderRateLimiter {
maxMessages = 10
window = 60.seconds
onLimitExceeded = RateLimitAction.STOP
}
}
// Each inbox is tracked independently — "inbox-a" and "inbox-b"
// each get their own 10-message window
client.sendMessage {
from = "inbox-a"
to = listOf("user@example.com")
subject = "From inbox A"
text = "This counts toward inbox-a's limit."
}
client.sendMessage {
from = "inbox-b"
to = listOf("user@example.com")
subject = "From inbox B"
text = "This counts toward inbox-b's limit."
}
client.close()
}
Per-Recipient Limiting
Tracks messages by recipient email address. Each address in to, cc, and bcc is checked independently:
suspend fun perRecipientExample() {
val client = AgentMailClient {
perRecipientRateLimiter {
maxMessages = 3
window = 30.seconds
onLimitExceeded = RateLimitAction.SKIP
}
}
// Each recipient address is tracked independently
val response = client.sendMessage {
from = "inbox-id"
to = listOf("alice@example.com", "bob@example.com")
subject = "Update"
text = "Both recipients are checked against their own limits."
}
if (response == null) {
println("Message skipped — a recipient hit their rate limit")
}
client.close()
}
Actions
When a limit is exceeded, the configured action determines what happens:
| Action | Behavior |
|---|---|
STOP |
Throws RateLimitExceededException (default) |
SKIP |
Returns null without sending |
DELAY |
Suspends until the window clears, then sends |
DELAY — Automatic Throttling
Use DELAY to automatically pace outgoing messages without dropping or failing:
suspend fun delayExample() {
val client = AgentMailClient {
perSenderRateLimiter {
maxMessages = 5
window = 10.seconds
onLimitExceeded = RateLimitAction.DELAY
}
}
// Sends 10 messages — the first 5 go immediately,
// then the coroutine suspends until the window clears
for (i in 1..10) {
client.sendMessage {
from = "inbox-id"
to = listOf("recipient@example.com")
subject = "Message #$i"
text = "Automatically throttled to 5 per 10 seconds."
}
println("Sent message #$i")
}
client.close()
}
STOP — Fail Fast
Use STOP to immediately halt when a limit is hit. Catch RateLimitExceededException to handle it:
suspend fun stopExample() {
val client = AgentMailClient {
perSenderRateLimiter {
maxMessages = 2
window = 10.seconds
onLimitExceeded = RateLimitAction.STOP
}
}
try {
repeat(5) { i ->
client.sendMessage {
from = "inbox-id"
to = listOf("recipient@example.com")
subject = "Message #${i + 1}"
text = "This will throw on the 3rd attempt."
}
}
} catch (e: RateLimitExceededException) {
println("Blocked: ${e.message}")
// Blocked: Rate limit exceeded for 'inbox-id' (2 per 10s)
}
client.close()
}
SKIP — Silent Drop
Use SKIP when it's acceptable to silently drop excess messages. Check for a null return to detect skipped sends — see the per-recipient example above.
Combining Both Limits
Per-sender and per-recipient limits can be used together. The sender limit is checked first, then each recipient:
suspend fun combinedExample() {
val client = AgentMailClient {
// Limit each inbox to 20 messages per minute
perSenderRateLimiter {
maxMessages = 20
window = 1.minutes
onLimitExceeded = RateLimitAction.DELAY
}
// Limit each recipient to 3 messages per minute
perRecipientRateLimiter {
maxMessages = 3
window = 1.minutes
onLimitExceeded = RateLimitAction.STOP
}
}
// Both limits are checked: sender limit first, then each recipient
client.sendMessage {
from = "inbox-id"
to = listOf("recipient@example.com")
subject = "Hello"
text = "Protected by both sender and recipient limits."
}
client.close()
}
Replies and Forwards
Rate limiting applies to all send operations, not just sendMessage:
suspend fun replyRateLimited() {
val client = AgentMailClient {
perSenderRateLimiter {
maxMessages = 5
window = 30.seconds
onLimitExceeded = RateLimitAction.SKIP
}
}
val messages = client.listMessages("inbox-id")
val message = messages.messages.first()
// Replies and forwards are also rate-limited
val response = client.replyToMessage(message) {
text = "Thanks for reaching out!"
}
if (response == null) {
println("Reply skipped — sender rate limit reached")
}
client.close()
}
Next Steps
- Configuration — full client configuration reference
- Messages — send, reply, and forward messages
- Monitoring — poll for new messages in real time