---
term: 'API Rate Limiting & Throttling'
seoTitle: 'API Rate Limiting & Throttling: Algorithms, Headers, Backoff'
headline: 'What is API Rate Limiting & Throttling?'
slug: api-rate-limiting-throttling
category: api-realtime
shortDefinition: 'Rate limiting is a control that caps how many requests a client may make per window; throttling slows the excess instead of rejecting it.'
relatedTerms:
  - api-gateway-architecture
  - api-key-security
  - api-payload-optimization
contrastsWith:
  - api-gateway-architecture
aboutTerms:
  - 'API Rate Limiting'
  - 'API Throttling'
  - 'API Quotas'
faq:
  - question: 'What is API rate limiting?'
    answer: 'A control that caps how many requests a client may make within a time window — say, a thousand per hour per key. Requests over the cap are rejected, classically with HTTP 429 Too Many Requests. It protects the service from overload and abuse, keeps one heavy client from degrading everyone else, and makes capacity planning possible.'
  - question: 'What is the difference between rate limiting, throttling, and quotas?'
    answer: 'Three tools often blurred into one word. Rate limiting rejects excess requests outright. Throttling slows or queues them instead — the request eventually completes, later. A quota is the long-horizon allowance: requests per day or month, tied to a plan or bill. Short windows protect infrastructure; quotas define the business deal; throttling smooths the edges.'
  - question: 'What does error 429 mean and how do I fix it?'
    answer: 'You have exceeded the requests allowed in the current window. The fix is client-side discipline: read the Retry-After header if present and wait that long; otherwise retry with exponential backoff plus jitter. The wrong fix — immediate blind retries — makes the situation worse for you and everyone else, which is exactly what backoff exists to prevent.'
  - question: 'How does the token bucket algorithm work?'
    answer: 'A bucket holds tokens that refill at a steady rate; each request spends one. A full bucket lets a burst through — the bucket size is the burst allowance — while the refill rate enforces the sustained average. This burst-friendly shape is why token bucket is the default algorithm for user-facing APIs.'
  - question: 'Why is a fixed window rate limiter problematic?'
    answer: 'The boundary burst: with a limit of 100 per minute, a client can send 100 requests at 11:59:59 and 100 more at 12:00:01 — 200 in two seconds, all legal. Sliding-window algorithms weight the previous window to close the loophole, at slightly more bookkeeping. It is the classic reason "requests per minute" needs a definition of minute.'
  - question: 'What are the rate limit headers?'
    answer: 'Convention first: X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset tell clients their budget, what is left, and when it refills — popularized by major API providers. Standardization is arriving via the IETF RateLimit and RateLimit-Policy headers. Either way, publishing limits in headers is what turns rate limiting from a wall into a contract.'
  - question: 'What is exponential backoff with jitter?'
    answer: 'The polite retry: wait 1s, then 2s, 4s, 8s after successive failures — and add a random slice (jitter) to each wait. The jitter matters more than it looks: without it, every client that failed together retries together, hammering the recovering service in synchronized waves. Randomness breaks the thundering herd.'
  - question: 'Should limits be per IP, per API key, or per user?'
    answer: 'Layered, because each scope fails alone: per-IP stops anonymous floods but punishes offices behind one address; per-key maps limits to applications and plans; per-user stops one account monopolizing a shared app; per-endpoint protects expensive operations specifically. Production systems usually combine at least key- and endpoint-scoped limits.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'RFC 6585 — 429 Too Many Requests'
    url: 'https://www.rfc-editor.org/rfc/rfc6585'
  - name: '429 Too Many Requests — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429'
  - name: 'IETF RateLimit header fields draft'
    url: 'https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/'
  - name: 'Exponential backoff and jitter — architecture analysis'
    url: 'https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/'
  - name: 'Rate limiting — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Rate_limiting'
cta:
  title: 'Limits that protect you by default'
  text: 'Back4app applies request limits at the platform edge and lets you tune per-app policies without building a limiter: your APIs ship protected, and your clients get clean 429 semantics to back off against.'
  linkText: 'Start Free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-27'
translationKey: api-rate-limiting-throttling
---

**Rate limiting is a control that caps how many requests a client may make per window; throttling slows the excess instead of rejecting it.** Add the third sibling — the **quota**, a billing-period allowance — and you have the full vocabulary most explanations blur into one word. Together they are how APIs stay fair, solvent, and up.

## Key takeaways

| Question | Answer |
| --- | --- |
| Rate limit | Hard cap per short window — excess gets 429 |
| Throttle | Excess is slowed or queued, not refused |
| Quota | The long-horizon allowance — per day/month, per plan |
| The server's voice | 429 + Retry-After + rate-limit headers |
| The client's manners | Respect Retry-After; exponential backoff **with jitter** |

## What the 429 response and rate-limit headers mean

```text
HTTP/1.1 429 Too Many Requests        ← RFC 6585
Retry-After: 30                       ← wait this long (seconds or a date)
X-RateLimit-Limit: 1000               ← your budget this window
X-RateLimit-Remaining: 0              ← what's left
X-RateLimit-Reset: 1767024000         ← when it refills
RateLimit: "default";r=0;t=30         ← the emerging IETF standard form
```

The client half of the contract — the code every SDK consumer eventually needs:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// The client half of rate limiting: back off, with jitter, then retry
async function withBackoff(fn, attempt = 0) {
  try {
    return await fn();
  } catch (e) {
    if (e.code !== 155 || attempt >= 4) throw e;   // 155: request limit hit
    const wait = 2 ** attempt * 500 + Math.random() * 200;
    await new Promise((r) => setTimeout(r, wait)); // exponential + jitter
    return withBackoff(fn, attempt + 1);
  }
}
const results = await withBackoff(() => query.find());
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The client half of rate limiting: back off, with jitter, then retry
Future<ParseResponse> withBackoff(Future<ParseResponse> Function() fn,
    [int attempt = 0]) async {
  final response = await fn();
  if (response.success || attempt >= 4) return response;
  final wait = (1 << attempt) * 500 + Random().nextInt(200);
  await Future.delayed(Duration(milliseconds: wait)); // exponential + jitter
  return withBackoff(fn, attempt + 1);
}
final response = await withBackoff(() => query.query());
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The client half of rate limiting: back off, with jitter, then retry
func withBackoff<T>(_ attempt: Int = 0,
                    _ fn: @escaping () async throws -> T) async throws -> T {
  do { return try await fn() }
  catch {
    guard attempt < 4 else { throw error }
    let wait = pow(2.0, Double(attempt)) * 0.5 + .random(in: 0...0.2)
    try await Task.sleep(for: .seconds(wait))   // exponential + jitter
    return try await withBackoff(attempt + 1, fn)
  }
}
let results = try await withBackoff { try await query.find() }
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The client half of rate limiting: back off, with jitter, then retry
suspend fun <T> withBackoff(attempt: Int = 0, fn: suspend () -> T): T {
  return try {
    fn()
  } catch (e: ParseException) {
    if (attempt >= 4) throw e
    val wait = (1L shl attempt) * 500 + Random.nextLong(200)
    delay(wait)                                 // exponential + jitter
    withBackoff(attempt + 1, fn)
  }
}
val results = withBackoff { query.find() }
```

## Token bucket vs. leaky bucket vs. fixed window vs. sliding window

| Algorithm | Bursts | Accuracy | Memory | Verdict |
| --- | --- | --- | --- | --- |
| Token bucket | Allowed, up to bucket size | Good | Tiny | **The API default** — bursty humans, steady average |
| Leaky bucket | Smoothed into a constant drip | Good | Small | Traffic *shaping* — steady output, added latency |
| Fixed window | Boundary burst: 2× limit at edges | Weak | Tiny | Simple, and famously gameable |
| Sliding window | Controlled | Best | Modest | The scale compromise most platforms land on |

```mermaid
flowchart LR
  accTitle: Token bucket rate limiting
  accDescr: Tokens refill a bucket at a steady rate; each request spends one token, allowing bursts up to the bucket size while enforcing a sustained average, and requests arriving to an empty bucket receive a 429.
  R["Refill:<br/>10 tokens/sec"] --> B["Bucket<br/>capacity 100"]
  Q["Request"] --> B
  B -->|"token available"| A["Allowed"]
  B -->|"bucket empty"| D["429 + Retry-After"]
```

One distributed footnote the diagrams skip: with many API servers, the bucket must live somewhere shared — typically an in-memory store doing atomic increments — and you choose between strict accuracy (every check hits the store) and speed (local counters, loose sync). Most platforms accept slightly-loose limits as the price of latency; contractual quotas get the strict treatment.

## Scoping: who exactly is limited?

Per-**IP** stops anonymous floods and DDoS absorption-leakage, but an office NAT makes hundreds of users one address. Per-**key** maps limits to applications and pricing plans — the workhorse scope. Per-**user** keeps one account from monopolizing a shared app's key. Per-**endpoint** prices expensive operations honestly — search and export endpoints deserve tighter budgets than health checks. Production systems layer them; the composite question is always "which budget did this request spend?"

## Common use cases

- **Public API protection** — the canonical case: fair budgets per key, published in headers, enforced at the edge.
- **Tier enforcement** — free vs. paid plans differing precisely in quota and burst allowance.
- **Abuse and scraping defense** — tight anonymous limits, generous authenticated ones.
- **Cost control on expensive paths** — LLM calls, exports, searches: per-endpoint budgets that reflect real cost.
- **Client-side self-defense** — backoff and request coalescing against *other people's* limits, which your integrations must respect to stay unbanned.

## Reject or throttle? A decision matrix

| Rate limit (reject) when… | Throttle (slow/queue) when… |
| --- | --- |
| Clients can retry intelligently | The work must eventually happen |
| Protecting interactive capacity | Smoothing batch and background load |
| The contract is requests-per-window | The contract is fairness of service |
| Fast feedback beats delayed success | Delayed success beats an error |
| Facing anonymous or untrusted traffic | Facing your own internal producers |

And the meta-rule: whatever you choose, **publish it** — limits in documentation and headers turn a frustrating wall into an engineering contract clients can build against.

## Limitations and trade-offs

- **Limits are blunt instruments.** A request is not a cost unit — one cheap read and one monster export both spend "1." Cost-aware limiting (points per operation) is the refinement, at complexity's price.
- **Distributed accuracy costs latency.** Strict global counters serialize on a shared store; loose local ones over-admit at the margins. Pick per guarantee, not per fashion.
- **429s punish the innocent retry loop.** Clients without jitter synchronize into thundering herds; your limiter's design must assume the worst client, because it will meet them.
- **Throttling hides overload.** Queued requests smooth graphs while building invisible backlog; queues need bounds and shed policies or they become outage delays.
- **Limits are product decisions wearing ops clothing.** Budgets, tiers, and burst allowances shape user experience and revenue — set them with the pricing page open, not just the dashboard.

## Rate limiting on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Protection is a platform default rather than a project: request limits apply at the edge per app and plan, expensive operations can be gated behind Cloud Code functions with their own checks, and clients receive clean 429 semantics — which the SDK-side backoff pattern in the code tabs above turns into resilient behavior instead of failed screens. You tune policies; you don't build the limiter.
