# How to Configure Sliding Window Rate Limiting for High‑Volume Campaigns in Listmonk

> Configure sliding window rate limiting in Listmonk to manage high-volume campaign traffic efficiently. Set app.message_sliding_window to true and define duration and rate in config.toml.

- Repository: [Kailash Nadh/listmonk](https://github.com/knadh/listmonk)
- Tags: how-to-guide
- Published: 2026-05-19

---

**Enable sliding window rate limiting in Listmonk by setting `app.message_sliding_window = true` in [`config.toml`](https://github.com/knadh/listmonk/blob/main/config.toml) along with `app.message_sliding_window_duration` and `app.message_sliding_window_rate` to cap burst traffic during high‑volume campaigns.**

High‑volume email campaigns can overwhelm downstream SMTP servers and third‑party APIs if not properly throttled. Listmonk provides a **sliding window rate limiting** mechanism that controls message bursts over configurable time intervals, independent of the standard per‑minute counters. This guide explains how to configure and tune these settings based on the actual implementation in the Listmonk source code.

## How Listmonk Handles Rate Limiting

Listmonk throttles outbound messages using two distinct mechanisms that work in tandem.

### Per‑Minute Send Rate (Standard Throttling)

The default throttling uses a `ratecounter.RateCounter` defined in `Config.MessageRate` to track messages sent per minute. This counter resets every 60 seconds and respects the configured per‑minute limit. While effective for general pacing, this method can still allow short bursts that temporarily overload downstream services.

### Sliding Window Rate Limiting (Burst Control)

For deterministic burst protection, Listmonk implements a sliding window algorithm in [`internal/manager/pipe.go`](https://github.com/knadh/listmonk/blob/main/internal/manager/pipe.go). When enabled, the manager counts messages within a continuous time window (`SlidingWindowDuration`) and enforces a hard cap (`SlidingWindowRate`). If the count reaches the limit before the window expires, the pipeline sleeps for the remaining duration, ensuring no interval exceeds the configured capacity.

## Configuring Sliding Window Parameters

Sliding window settings are defined in **[`models/settings.go`](https://github.com/knadh/listmonk/blob/main/models/settings.go)** as `AppMessageSlidingWindow`, `AppMessageSlidingWindowDuration`, and `AppMessageSlidingWindowRate`. These map to TOML configuration keys under the `[app]` section.

Add the following to your [`config.toml`](https://github.com/knadh/listmonk/blob/main/config.toml) file:

```toml
[app]
message_sliding_window = true               # Enable sliding window mode

message_sliding_window_duration = "10s"     # Time window duration (e.g., 10s, 1m)

message_sliding_window_rate = 500           # Maximum messages allowed per window

```

These values are parsed during initialization (see [`cmd/init.go`](https://github.com/knadh/listmonk/blob/main/cmd/init.go)) and populated into the `manager.Config` struct in **[`internal/manager/manager.go`](https://github.com/knadh/listmonk/blob/main/internal/manager/manager.go)**:

```go
cfg := manager.Config{
    // ...
    SlidingWindow:         true,
    SlidingWindowDuration: 10 * time.Second,
    SlidingWindowRate:     500,
}

```

## The Sliding Window Implementation in pipe.go

The core enforcement logic resides in **[`internal/manager/pipe.go`](https://github.com/knadh/listmonk/blob/main/internal/manager/pipe.go)** within the `NextSubscribers` method (approximately lines 89‑120). The manager tracks the window start time and message count, resetting when the duration expires and pausing when limits are exceeded.

The algorithm works as follows:

```go
// internal/manager/pipe.go – excerpt from the message loop
hasSliding := p.m.cfg.SlidingWindow &&
    p.m.cfg.SlidingWindowRate > 0 &&
    p.m.cfg.SlidingWindowDuration.Seconds() > 1

if hasSliding {
    diff := time.Since(p.m.slidingStart)

    // Reset the window when it expires
    if diff >= p.m.cfg.SlidingWindowDuration {
        p.m.slidingStart = time.Now()
        p.m.slidingCount = 0
    }

    // Increment and enforce the limit
    p.m.slidingCount++
    if p.m.slidingCount >= p.m.cfg.SlidingWindowRate {
        wait := p.m.cfg.SlidingWindowDuration - diff
        p.m.log.Printf("messages exceeded (%d) for the window (%v). Sleeping for %s.",
            p.m.slidingCount, p.m.cfg.SlidingWindowDuration, wait)
        p.m.slidingCount = 0
        time.Sleep(wait)
    }
}

```

When `slidingCount` reaches `SlidingWindowRate`, the pipeline invokes `time.Sleep(wait)` for the remainder of the window duration, effectively pacing the campaign to prevent downstream overload.

## Monitoring and Verification

Once configured, start Listmonk with the updated configuration:

```bash
./listmonk --config config.toml

```

During campaign execution, the application logs rate‑limiting events:

```

2026/05/19 12:34:56 messages exceeded (500) for the window (10s). Sleeping for 9.8s.

```

This log entry confirms that the sliding window rate limiting is active and temporarily pausing the pipeline to respect the configured burst limits.

## Summary

- **Sliding window rate limiting** prevents burst overloads by capping messages within continuous time intervals rather than relying solely on per‑minute averages.
- Configure the feature via `app.message_sliding_window`, `app.message_sliding_window_duration`, and `app.message_sliding_window_rate` in [`config.toml`](https://github.com/knadh/listmonk/blob/main/config.toml).
- The enforcement logic lives in **[`internal/manager/pipe.go`](https://github.com/knadh/listmonk/blob/main/internal/manager/pipe.go)**, where the manager pauses execution when message counts exceed the window capacity.
- This mechanism is essential for high‑volume campaigns requiring tighter pacing than standard per‑minute throttling can provide.

## Frequently Asked Questions

### What is the difference between sliding window and per‑minute rate limiting in Listmonk?

**Per‑minute rate limiting** tracks messages in 60‑second buckets using a `RateCounter`, which can allow bursts at the start of each minute. **Sliding window rate limiting** uses a continuous time window (e.g., 10 seconds) and enforces a hard cap on messages sent within that rolling interval, preventing bursts regardless of when they occur.

### How do I calculate optimal values for the sliding window duration and rate?

Divide your target maximum throughput by your desired burst tolerance. For example, to send 3,000 messages per minute with no more than 500 messages in any 10‑second burst, set `message_sliding_window_duration = "10s"` and `message_sliding_window_rate = 500`. Adjust based on your SMTP provider's rate limits and your infrastructure capacity.

### Will enabling sliding window rate limiting slow down my campaigns?

Only if your campaign attempts to exceed the configured `message_sliding_window_rate` within the specified duration. The pipeline simply pauses for the remainder of the window when the limit is hit, then resumes automatically. This intentional throttling protects downstream services from overload and prevents hard rejections that could delay delivery further.

### Where can I verify that sliding window rate limiting is active?

Check the application logs for messages containing "messages exceeded" and "Sleeping for", which indicate the sliding window logic in **[`internal/manager/pipe.go`](https://github.com/knadh/listmonk/blob/main/internal/manager/pipe.go)** has engaged. Additionally, ensure the settings in **[`models/settings.go`](https://github.com/knadh/listmonk/blob/main/models/settings.go)** correctly map to your [`config.toml`](https://github.com/knadh/listmonk/blob/main/config.toml) values and appear in the startup configuration validation.