Rate Limiting and Throttling in Kotlin: How to Protect Your API from Overloads

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

When Your API Cries "SOS": Introduction to Rate Limiting and Throttling

\n\n

Imagine: you've written a perfect Kotlin application. It's fast, elegant, uses coroutines and Flow. You deploy it to production, and then — bam! — one "kind" user (or, worse, an attacker) starts hammering your endpoint with thousands of requests per second. The server crashes, the database cries, and other users get 503 Service Unavailable.

\n\n

Sound familiar? This is exactly why rate limiting and throttling mechanics exist. In this article, we won't just talk about theory, but will break down how to implement these patterns in Kotlin with practical examples.

\n\n

We'll write code that will actually protect your backend, whether it's a Ktor application, Spring Boot, or pure coroutines. Let's go!

\n\n

Rate Limiting vs Throttling: What's the Difference (and Why Does It Matter)?

\n\n

Let's start with the basics to avoid confusion with terminology. Although these concepts are often used as synonyms, there is a nuance between them.

\n\n
    \n
  • Rate Limiting — is a hard limit. "You can only make 100 requests per minute. After the 101st — you'll get 429 Too Many Requests and a ban until the next minute." This is about a limit.
  • \n
  • Throttling — is a softer regulation. "You cannot send requests faster than once every 200 ms. If you send faster — we'll queue them or simply ignore them." This is about speed.
  • \n
\n\n

In the Kotlin world, we most often implement rate limiting at the API level (middleware) and throttling at the level of specific operations (for example, to avoid spamming an external server with requests).

\n\n

Algorithms Every Kotlin Developer Should Know

\n\n

Before writing code, let's choose a tool. There are three classic algorithms that fit perfectly into Kotlin architecture.

\n\n

1. Token Bucket — The Most Popular

\n

Imagine a bucket into which N new tokens drip every second. The bucket has a maximum capacity (burst). To process a request, you need to take a token. If there are no tokens — the request is rejected.

\n

Pros: Allows small bursts of load, simple to implement.

\n\n

2. Leaky Bucket — Smoothing Peaks

\n

A bucket with a hole. Requests go into the bucket, and the "hole" releases them at a constant rate. If the bucket overflows — requests are discarded.

\n

Pros: Perfectly evens out the load, prevents peaks.

\n\n

3. Sliding Window — Precision Without Jerks

\n

Instead of rigid boundaries (like in "1 minute"), we look at a sliding time window. More complex to implement, but more accurately reflects the real load.

\n\n

For Kotlin projects, Token Bucket is the sweet spot between simplicity and efficiency. That's what we'll implement.

\n\n

Writing Code: Rate Limiter in Kotlin from Scratch (Without Libraries)

\n\n

Let's create a simple but working Rate Limiter using coroutines and Mutex. This will be a thread-safe version of Token Bucket.

\n\n
import kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport kotlin.time.Duration\nimport kotlin.time.TimeSource\n\nclass TokenBucketRateLimiter(\n    private val maxTokens: Int,\n    private val refillRate: Long, // tokens per second\n    private val timeSource: TimeSource = TimeSource.Monotonic\n) {\n    private var tokens: Long = maxTokens.toLong()\n    private var lastRefillTimestamp: Long = timeSource.markNow().elapsedNow().inWholeMilliseconds\n    private val mutex = Mutex()\n\n    suspend fun tryAcquire(): Boolean {\n        mutex.withLock {\n            refillTokens()\n            return if (tokens > 0) {\n                tokens--\n                true\n            } else {\n                

Blogs

Book Recommendations