SDE Path

APIs 101: REST, RPC, Idempotency & Pagination

13 min read

An API is the contract between a client and a server: the client sends a request in an agreed shape, and the server promises a response in another. Almost every system design connects its boxes with APIs, so nailing the vocabulary pays off constantly.


Three common styles

  • REST — model everything as resources addressed by URLs (/users/42, /users/42/orders) and act on them with HTTP verbs. Simple, cacheable, ubiquitous. A great default for CRUD-style services.
  • RPC (for example, gRPC) — call a function on the server (getUser(42)) as if it were local. Compact binary payloads make it ideal for fast internal service-to-service calls.
  • GraphQL — the client sends a query describing exactly the fields it wants, and the server returns just those. It solves over-fetching and under-fetching for rich clients, at the cost of more server complexity and trickier HTTP caching.

Rule of thumb: REST for public resource APIs, gRPC between your own microservices, GraphQL when many different clients each need a different slice of the same data.


HTTP verbs

  • GET — read a resource; no side effects.
  • POST — create a resource, or trigger an action.
  • PUT — replace a resource at a known location.
  • PATCH — partially update a resource.
  • DELETE — remove a resource.

Status codes (by family)

  • 2xx success — 200 OK, 201 Created, 204 No Content.
  • 3xx redirect — 301 Moved Permanently, 304 Not Modified (cache).
  • 4xx client error — 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 429 Too Many Requests.
  • 5xx server error — 500 Internal Server Error, 503 Service Unavailable.

Returning the right code lets clients, caches, and load balancers behave correctly without parsing your response body.


Idempotency

An operation is idempotent if performing it once or many times leaves the same result. This matters because networks retry: a client whose request times out cannot tell "it never arrived" from "the response was lost."

  • GET — idempotent (reading twice changes nothing).
  • PUT — idempotent (setting a field to the same value twice is the same as once).
  • DELETE — idempotent (deleting an already-deleted row still ends deleted).
  • POSTnot idempotent by default (two POSTs can create two orders).

To make POST safe to retry, use an idempotency key: the client attaches a unique key; the server records it and, on a retry with the same key, returns the original result instead of doing the work again. This is exactly how payment APIs avoid double-charging.


Pagination

Returning a million rows in one response is a bad idea. Two common approaches:

  • Offset pagination?limit=20&offset=40 ("skip 40, then give 20"). Easy, and it can jump to page N, but it is slow for deep pages (the database still scans the skipped rows) and can skip or duplicate items when data changes mid-scroll.
  • Cursor pagination?limit=20&after=<cursor>, where the cursor encodes the last item seen (an id or timestamp). Fast and stable under inserts, but you cannot jump directly to page 500.

Prefer cursors for large, changing, infinite-scroll feeds; offset is fine for small admin tables.


Versioning

APIs change, but clients should not break. Common strategies:

  • URL versioning/v1/users, /v2/users. Obvious and easy to route.
  • Header versioning — a version carried in the Accept header. Cleaner URLs, but less visible.

Whichever you pick, treat removing or renaming a field as a breaking change and give clients a migration window.


Basic rate limiting

To protect the service from abuse and noisy neighbors, cap how many requests a client may make — say 100 requests per minute per API key. When a client exceeds the limit, return 429 Too Many Requests, ideally with a Retry-After header. Common algorithms are the token bucket and the sliding window; the core idea is a per-client counter over a time window.


In the interview

  • Explain why a verb is idempotent, then mention idempotency keys for safe POST retries.
  • Default to cursor pagination for feeds and call out the deep-offset problem.
  • Return precise status codes — 400 vs 401 vs 404 vs 409 — it signals real maturity.

Check yourself

5 questions — graded when you submit.

Question 1

Which HTTP method is NOT idempotent by default?

Question 2

A client's POST times out and it cannot tell whether the order was created. How do you make a safe retry possible?

Question 3

For an infinite-scroll feed over frequently changing data, which pagination is better?

Question 4

Which status code best signals that a client has exceeded its rate limit?

Question 5

When is GraphQL most justified over REST?

Finished reading?

Sign in to save your progress across lessons.