Thread-safe, zero-dependency rate limiters for Go: a token bucket (burst
- steady refill rate) and a sliding window log (max N requests per trailing duration).
Both limiters use only the Go standard library (sync, time) — no
third-party dependencies.
go get github.com/kasapdev/go-ratelimiterpackage main
import (
"fmt"
"time"
"github.com/kasapdev/go-ratelimiter"
)
func main() {
// TokenBucket: capacity of 5 tokens, refilling at 2 tokens/sec.
bucket := ratelimiter.NewTokenBucket(5, 2)
for i := 0; i < 7; i++ {
if bucket.Allow(1) {
fmt.Printf("request %d: allowed\n", i)
} else {
fmt.Printf("request %d: rate limited\n", i)
}
}
// SlidingWindow: at most 3 requests per 1-second trailing window.
window := ratelimiter.NewSlidingWindow(3, time.Second)
for i := 0; i < 5; i++ {
if window.Allow() {
fmt.Printf("sliding request %d: allowed\n", i)
} else {
fmt.Printf("sliding request %d: rate limited\n", i)
}
}
}A thread-safe token-bucket limiter. Holds up to capacity tokens and
refills at refillRate tokens per second, computed lazily from elapsed
wall-clock time on each call (no background goroutine or ticker).
func NewTokenBucket(capacity int, refillRate float64) *TokenBucket— creates a bucket that starts full.capacity <= 0is clamped to 1; negativerefillRateis clamped to 0.func (tb *TokenBucket) Allow(cost int) bool— attempts to consumecosttokens; returns whether they were available. Non-positivecostis always allowed and consumes nothing. Safe for concurrent use.func (tb *TokenBucket) Wait(ctx context.Context, cost int) error— blocks untilcosttokens are available, then consumes them. Returnsctx.Err()ifctxends first (consuming nothing),ErrCostExceedsCapacityifcostis larger than the bucket's capacity, orErrNoRefillif tokens are short and the refill rate is zero; the last two fail immediately since waiting could never succeed. Non-positivecostreturnsnilat once.
A thread-safe sliding-window-log limiter. Allows at most maxRequests
calls to Allow within any trailing period of window duration, tracking
and pruning individual request timestamps.
func NewSlidingWindow(maxRequests int, window time.Duration) *SlidingWindow— creates a new limiter.maxRequests <= 0is clamped to 1;window <= 0is clamped to 1 second.func (sw *SlidingWindow) Allow() bool— reports whether a new request is permitted right now, recording it if so. Safe for concurrent use.
go test ./...Concurrency correctness (including a dedicated test that hammers a
TokenBucket from 100 goroutines and asserts successful allows never
exceed capacity) can be verified with the race detector:
go test ./... -race -vMIT — see LICENSE.