Rate Limiting and Resilience

By the Syncfusion DevTutorials team · Last updated

Protect your API from too much traffic with rate limits and 429 Too Many Requests, and make clients cope with slow or failing services using retries and backoff.

Rate limiting controls how many requests a caller can make within a period of time.

Resilience helps applications continue working when services are slow, overloaded, or temporarily unavailable. Instead of failing immediately, a resilient client waits, retries carefully, and avoids making a bad situation worse. Together, rate limiting and resilience protect both the API and its users.

Watch One Client Flood the API

A partner application contains a bug. Instead of loading the menu once, it repeatedly calls GET /menu in a loop. Press Run.

Program.cs — no limit at all

Results:

  • All six requests receive 200 OK.
  • /stats reports: {"requests": 6}

Nothing slows the client down. Nothing protects the API. A small bug may generate thousands or millions of requests. This can increase costs, slow responses, overload servers, and affect other customers.

In simple words: the API needs rules that prevent one caller from using unlimited resources.

TL;DR

  • A rate limit restricts how many requests a caller may make during a time period.
  • Going over the limit should return 429 Too Many Requests.
  • Include the Retry-After header, to tell the client when it may try again.
  • Apply limits per user, API key, or client.
  • Fixed windows are simple but can be bursty.
  • Token buckets handle bursts more smoothly.
  • Retry temporary failures using exponential backoff and jitter.
  • Never retry 400 Bad Request, and never retry non-idempotent POST operations without an Idempotency-Key.

A Real-World Example

Imagine a shop selling a limited-edition product. A sign at the entrance says: One per customer.

The goal is fairness. Without the rule, one person buys everything, and everyone else gets nothing. Rate limiting serves the same purpose. It ensures fair usage, stable performance, and protection from bugs and abuse.

Requests are counted within a time window. Requests over the limit receive 429 Too Many Requests and a Retry-After value.

What You Will Learn

In this lesson, you will learn:

  1. Why APIs use rate limits.
  2. How fixed-window rate limiting works.
  3. How to limit requests per API key.
  4. How token bucket rate limiting handles bursts.
  5. How resilient clients retry safely.
  6. When retries should and should not happen.

A Fixed Window Rate Limit

A fixed-window limiter counts requests during a fixed period. Example rule: 3 requests every 60 seconds. The first three requests succeed. The fourth request exceeds the limit.

Program.cs — fixed window limit

Requests Within the Limit

At 10s, 20s and 30s, the responses are 200 OK. The remaining requests decrease: 2, 1, 0.

Request Over the Limit

At 40s, the response is 429 Too Many Requests with Retry-After: 20. The client must wait until the 60-second window ends.

New Window

At 61s, the counter resets. Requests are allowed again, and the response is 200 OK.

A 429 Too Many Requests response is normal behavior. Well-behaved clients read the Retry-After header, pause their requests, and try again later.

Rate Limits Per API Key

Rate limits should apply to each caller separately. The activity of one user should not affect the access of another user.

Program.cs — a limit per API key

Example:

Asha's Key

The limit is 3 requests. The results are 200, 200, 200, 429. The response includes Retry-After: 52.

Swiggo's Key

It uses a completely separate counter. Even after Asha reaches her limit, 200 OK still succeeds for Swiggo.

Missing Key

The API cannot identify the caller, so the response is 401 Unauthorized. Without an identity, limits cannot be enforced properly.

Token Bucket Rate Limiting

Fixed windows have a weakness. A caller could send 3 requests at second 59 and 3 requests at second 60, and effectively make six requests almost at once.

A token bucket gives smoother behavior.

How It Works

The bucket stores tokens. Example: Capacity: 3 tokens and Refill: 1 token per second

Each request uses one token. No token means no request.

TokenBucket.cs — bursts and refill

Example:

Second 0

The bucket has 3 tokens. The results are ✓ ✓ ✓ ✗.

Second 1

One token returns. The result is ✓.

Second 2

One more token returns. The results are ✓ ✗ ✗.

Second 6

The bucket has refilled completely. Another burst is allowed.

Token buckets allow occasional bursts while they enforce a long-term rate.

Resilience and Retries

Sometimes requests fail because of temporary problems, such as network issues, timeouts, service restarts, and short outages. Retrying immediately can make the situation worse. Instead, clients should retry gradually.

Exponential Backoff

Wait longer after each failure. Example: Attempt 1 → wait 100ms, Attempt 2 → wait 200ms, Attempt 3 → wait 400ms

The delay doubles each time.

Jitter

Add a small random delay. Example: 107ms, 223ms, 412ms

This prevents thousands of clients from retrying at the same moment.

Retry delays grow after each failure. Jitter spreads retries across time to prevent spikes.

Retry.cs — backoff and Retry-After

Example:

Timeout

Attempt 1 ends with Timeout, and the client waits 107ms. Attempt 2 ends with Timeout, and the client waits 223ms. Attempt 3 returns 200 OK, which is a success.

Retry-After

If the server returns 429 Too Many Requests with Retry-After: 2, the client waits 2000ms.

The instruction of the server takes priority over the calculated backoff delay.

Circuit Breakers

Sometimes retries are not enough. A service may stay unavailable for a long time. Continuing to send requests only creates more load.

A circuit breaker stops requests for a while.

Typical behavior:

  1. Several failures occur.
  2. The circuit opens.
  3. Requests fail immediately.
  4. After a waiting period, one test request is allowed.
  5. If it succeeds, normal traffic resumes.

The benefits are that it protects struggling services, reduces unnecessary traffic, and recovers more quickly after outages.

When You Should Not Retry

Some failures will never succeed if they are retried.

Do Not Retry Validation Errors

Example: 400 Bad Request

The request itself is invalid. Repeating it produces the same result.

Be Careful with POST Requests

Consider POST /payments. The payment may have succeeded even if the response was lost. A blind retry may charge the customer twice.

Only retry POST requests when they use an Idempotency-Key, which makes sure that duplicate requests are handled safely.

Never retry a payment or a similar operation unless the request is idempotent. Retries should be safe, predictable, and controlled.

Rate Limiting in ASP.NET Core

Real ASP.NET Core applications include built-in rate limiting support. Common limiter types include Fixed Window, Sliding Window, and Token Bucket.

Rate limits can be partitioned by API key, user, IP address, or custom identifiers. Applications can also configure 429 responses, retry information, and different limits for different endpoints.

How to Choose a Limit

Rate limits should be based on real usage patterns. Example: if normal users make 20 requests per minute, then 100 requests per minute might be a reasonable limit.

Limits that are too strict frustrate legitimate users. Limits that are too high give little protection. Monitor usage before you decide.

Set limits using real production data, not guesses. Review traffic patterns regularly, and change the limits when necessary.

Where You See This

  • Public APIs return 429 Too Many Requests when limits are exceeded.
  • Login systems limit failed sign-in attempts.
  • Mobile apps retry temporary network failures.
  • Cloud SDKs use backoff and jitter automatically.
  • Payment APIs rely on idempotency keys for safe retries.

Common mistakes

SymptomCauseFix
One client slows the API for everyoneNo rate limitingAdd limits per user or API key
Clients keep receiving 429 repeatedlyRetry-After is ignoredWait the specified amount of time
Customers are charged twicePOST requests are retried unsafelyUse Idempotency-Key or avoid retries
Outages become worse under loadClients retry immediatelyUse backoff, jitter, and circuit breakers
Invalid requests are retried repeatedly400 responses are treated as temporary failuresRetry only transient failures such as 429, 503, or timeouts
Legitimate users are blocked too oftenLimits are too lowReview real usage and adjust limits

Exercises

What to Remember

  • A rate limit stops one caller from taking everything. Count per key.
  • When a caller is over the limit, send 429 Too Many Requests with Retry-After and a Problem Details body.
  • A fixed window is simple. A token bucket allows short bursts and fills up slowly again.
  • Clients retry temporary failures with exponential backoff and jitter. They also obey Retry-After.
  • Never retry a 400, and never retry a POST without an Idempotency-Key.

Next: API Monitoring and Logging. You will learn how to watch your API in production, so that you see the floods, the 429s and the slow calls before your customers do.