Skip to content
STAGING — not production

The Physics of APIs: Idempotency, Rate Limits & State Machines

REST is not just CRUD. The physics of Idempotency (f(x) = f(f(x))), Leaky Bucket Rate Limiting, and why HATEOAS is a State Machine.

Beginner 40 min read Expert Version →

🎯 What You'll Learn

  • Prove Idempotency mathematically ($f(x) = f(f(x))$)
  • Implement Token Bucket Rate Limiting (Redis Lua Script)
  • Design APIs as Finite State Machines (HATEOAS)
  • Master Versioning Physics (Header-based vs URL-based)
  • Differentiate 429 vs 503 (Backpressure Physics)

Introduction

Most developers treat APIs as “JSON over HTTP”. This is wrong. An API is a Remote Procedure Call with Failure Semantics. When you send a request over a network, three outcomes are possible:

  1. Success.
  2. Failure.
  3. Unknown (The request was sent, but the connection dropped before response).

This “Unknown” state creates the need for Idempotency.


The Physics: Idempotency

Definition: An operation is idempotent if applying it multiple times has the same effect as applying it once. f(x)=f(f(x))f(x) = f(f(x))

  • GET: Idempotent (Read-only).
  • PUT: Idempotent (Replace state). x=5x=5. If I say x=5x=5 twice, xx is still 55.
  • POST: Not Idempotent (Create). If I say “Pay 10"twice,Ipay10" twice, I pay 20.

The Solution: Idempotency Keys. The client generates a UUID (v4) and sends it in the header: Idempotency-Key: <uuid>. The server stores the Key + Response in Redis. If the client retries, the server returns the stored response execution.


Rate Limiting: The Token Bucket

If an API accepts infinite requests, it will crash. We need Backpressure.

Fixed Window Counter (simple, common implementation):

  • Limit (LL): Max requests per window.
  • Window (WW): Time period (e.g., 1 second).

Redis Lua Implementation: To avoid race conditions (Get-then-Set), we use Lua to make the check atomic. This implements a fixed window counter — simpler than a true token bucket but has burst edge cases at window boundaries.

-- Redis Lua Script: Fixed Window Rate Limiter
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")

if current + 1 > limit then
    return 0 -- Rejected
else
    redis.call("INCR", key)
    redis.call("EXPIRE", key, 1) -- 1 second window
    return 1 -- Accepted
end
```diff

**Note:** A true Token Bucket tracks remaining tokens and refills at rate $R$, allowing bursts up to capacity $C$. The sliding window log algorithm (storing timestamps in a sorted set) eliminates the boundary-burst problem at the cost of more memory.

---

## HATEOAS: The API as a State Machine

**REST** stands for "Representational State Transfer".
Most APIs are just "RPC over JSON". True REST uses **HATEOAS** (Hypermedia As The Engine of Application State).

**Physics:**
The Server dictates the State Machine. The Client just follows transitions.

**Response Example:**
```json
{
  "order_id": 123,
  "status": "pending_payment",
  "links": [
    { "rel": "pay", "method": "POST", "href": "/orders/123/pay" },
    { "rel": "cancel", "method": "DELETE", "href": "/orders/123" }
  ]
}

If the status changes to paid, the pay link disappears. The Client never needs to know the logic “If pending, show pay button”. It just renders the links.


Practice Exercises

Exercise 1: Idempotency Fail (Beginner)

Task: Write a Python script that POST /charge twice without an Idempotency Key. Observation: You effectively double-charge the user. Fix: Add a UUID header and handle it on the server.

Exercise 2: The 429 Governor (Intermediate)

Task: Use ab (Apache Bench) to hammer your API. Action: Implement the Lua Token Bucket. Result: See requests succeed up to Limit, then fail with 429 immediately.

Exercise 3: State Machine Logic (Advanced)

Task: Design a “Traffic Light” API. Action: Return links for next_state. Rules: Red -> Green (links: next). Green -> Yellow (links: next). Yellow -> Red. Check: Ensure you cannot go from Red to Yellow.


Knowledge Check

  1. Which HTTP method is NOT idempotent?
  2. What is the formula for Idempotency?
  3. Why use Lua for Rate Limiting in Redis?
  4. What does HATEOAS stand for?
  5. Difference between 429 and 503?
Answers
  1. POST. (And PATCH, usually).
  2. f(x) = f(f(x)).
  3. Atomicity. Prevents race conditions between reading usage and incrementing it.
  4. Hypermedia As The Engine of Application State.
  5. 429: Client sending too fast. 503: Server is overloaded/down.

Summary

  • Idempotency: The safety net for network uncertainty.
  • Rate Limiting: Protecting the server from physics (infinite load).
  • HATEOAS: The API tells the Client what is possible.

Want to go deeper?

Weekly infrastructure insights for engineers who build trading systems.

Free forever. Unsubscribe anytime.

You're in. Check your inbox.

Questions about this lesson? Working on related infrastructure?

Let's discuss