Skip to main content
Version: 0.2

Retry & Rate Limiting

SMTP is not always reliable: connections drop, providers throttle, and downstream mail servers temporarily refuse delivery. SMTPSender handles these cases for you with configurable retries and a built-in token-bucket rate limiter.

Retry Logic

Retries use exponential backoff. With the defaults (MaxRetries=3, RetryDelay=1s, RetryBackoff=2.0), failed sends are retried after 1s, 2s, and 4s before giving up.

config := email.SMTPConfig{
Host: "smtp.gmail.com",
Port: 587,
Username: "you@gmail.com",
Password: "app-password",
UseTLS: true,
MaxRetries: 5, // up to 5 retries
RetryDelay: 500 * time.Millisecond,
RetryBackoff: 2.0, // 0.5s, 1s, 2s, 4s, 8s
}

Only transient failures (network errors, 4xx SMTP responses) are retried. Permanent failures — invalid recipient, authentication failure, message rejected — fail immediately.

Rate Limiting

Most SMTP providers cap how many emails per second they accept from a single sender. Set RateLimit in messages-per-second; SMTPSender blocks each Send until a token is available.

config := email.SMTPConfig{
Host: "smtp.gmail.com",
RateLimit: 5, // up to 5 messages/second
}

The default is 10/s. A value of 0 disables rate limiting entirely.

Context Timeouts

Every Send takes a context.Context. Use it to bound how long an individual send may take, or to cancel work when the request that triggered the send is done.

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := mailer.Send(ctx, to, subject, body); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("send timed out")
}
return err
}