API Design

REST resource modeling, which verbs are idempotent and why it matters, status codes, cursor vs offset pagination, versioning, and idempotency keys for safe retries.

APIRESTidempotencypagination

Model resources, not actions

URLs are nouns; verbs are HTTP methods. POST /trades, GET /portfolios/{id}, DELETE /watchlist/{symbol} — not /createTrade or /getPortfolio.

MethodUseIdempotent?Safe?
GETread
POSTcreate / non-idempotent action
PUTreplace (full)
PATCHpartial updateusually ❌
DELETEremove

Idempotent = doing it twice == doing it once. It matters because networks retry: a client that times out on PUT or DELETE can safely resend. POST isn't idempotent — which is why creating money-moving resources needs an idempotency key.

Status codes that signal correctly

  • 200/201/204 success (201 create, 204 no body).
  • 400 bad input · 401 unauthenticated · 403 authenticated-but-forbidden · 404 missing · 409 conflict · 422 validation · 429 rate-limited.
  • 500 us · 503 down/overloaded.

Return a consistent error body (code, message, maybe details) so clients can branch on code, not parse prose.

Pagination

  • Offset/limit (?page=3&size=20): simple, but slow deep in the list and skips/dupes rows when data shifts under you.
  • Cursor/keyset (?after=<opaque>): pass the last seen sort key; stable and fast at any depth. Prefer cursors for feeds and large/changing lists.

Versioning & idempotency keys

  • Versioning: /.../v1/... (or a header). Version when you make a breaking change; add fields additively otherwise.
  • Idempotency key: client sends a unique Idempotency-Key on a POST; the server records the result for that key and returns the same response on retry — so a retried "place trade" or "charge card" doesn't double-execute. (Razorpay webhooks in StockVision are verified + deduped for the same reason.)

Here's the retry that would have double-charged, made safe:

The key insight: the server answers "have I finished this key?" before doing any work — so the retry replays the stored answer instead of re-executing.

Design the failure path, not just the happy path

Seniors are judged on the unhappy path: what's the error contract, how does a client retry safely (idempotency), what's the rate-limit response (429 + Retry-After), and how does the API evolve without breaking old clients (additive changes + versioning)?