DDSA Solutions
Fundamentals7 min read·

API Design and REST Best Practices for Interviews

How to design REST APIs in system design interviews: resources, HTTP verbs, pagination, versioning, errors, idempotency, and rate-limit headers.

System design interviews often end with "design the API." You do not need to memorize every RFC — you need consistent resource naming, correct HTTP semantics, and error shapes that clients can rely on. This guide pairs with our rate limiter (429 responses) and URL shortener (POST /v1/urls, GET /{code}) examples.

REST in one paragraph

REST models your system as resources (nouns) identified by URLs. HTTP verbs express actions on those resources. State lives on the server; each request carries enough context (auth, body) to be understood independently. Good API design makes the resource graph obvious to a new engineer reading the docs.

Resource naming

  • Use plural nouns: /users, /posts, /conversations.
  • Nest for ownership: /users/{id}/posts — but avoid depth > 2.
  • Actions that are not CRUD: POST /posts/{id}/like or POST /orders/{id}/cancel as sub-resource.
  • Lowercase, hyphens for multi-word: /read-receipts.

Bad vs good

Avoid: GET /getUserPosts?userId=5. Prefer: GET /users/5/posts. Avoid verbs in paths unless the operation is not a resource (e.g. POST /search with body).

HTTP methods

MethodMeaningIdempotent?Safe?
GETRead resourceYesYes
POSTCreate or non-idempotent actionNoNo
PUTReplace entire resourceYesNo
PATCHPartial updateNo*No
DELETERemove resourceYesNo

*PATCH idempotency depends on implementation; mention Idempotency-Key header for POST payments and message sends.

Status codes that matter

CodeWhen
200 OKSuccessful GET, PUT, PATCH with body
201 CreatedPOST created resource; include Location header
204 No ContentDELETE success, empty body
400 Bad RequestValidation error — client fixable
401 UnauthorizedMissing or invalid auth
403 ForbiddenAuthenticated but not allowed
404 Not FoundResource does not exist
409 ConflictDuplicate create, version mismatch
429 Too Many RequestsRate limited — include Retry-After
500 Internal Server ErrorServer bug — do not leak stack traces

Pagination

Offset pagination (page=2&limit=20) is simple but slow on deep pages — the DB skips millions of rows. Cursor pagination (after=cursor_token) uses an indexed position and scales for feeds and message history.

  • Response: { data: [...], next_cursor: "eyJ...", has_more: true }
  • Cursor is opaque (base64 of last sort key + id).
  • Used in news feed and chat designs.

Versioning

  • URL path: /v1/users — clearest for public APIs.
  • Header: Accept: application/vnd.api+json;version=1 — keeps URLs stable.
  • Never break v1 without a new version; deprecate with sunset headers.

Error response contract

Return a consistent JSON shape so clients can branch on machine-readable codes:

  • { "error": { "code": "INVALID_SHORT_CODE", "message": "Human readable", "field": "code" } }
  • Log request_id on server; return request_id in body for support tickets.
  • Same idea as the rate limiter response contract.

Authentication sketch

Bearer JWT in Authorization header for mobile/SPA. API keys for server-to-server. OAuth for third-party access. Say where auth terminates: API gateway validates JWT, passes user_id to internal services.

Example: URL shortener API

  1. POST /v1/urls — body: { "url": "https://...", "customAlias": "optional" } → 201 { "shortUrl": "...", "shortCode": "abc12" }
  2. GET /v1/urls/{code} — redirect 302 or JSON for API clients
  3. DELETE /v1/urls/{code} — 204 if owner matches
  4. GET /v1/urls?cursor=... — list user's links (authenticated)

Rate-limit response headers

When returning 429, include machine-readable headers so clients back off correctly — same contract as our rate limiter design:

  • Retry-After: seconds until the client may retry.
  • X-RateLimit-Limit: max requests in the window.
  • X-RateLimit-Remaining: requests left in current window.
  • X-RateLimit-Reset: Unix timestamp when the window resets.

Idempotency for write endpoints

Advertisement

POST /payments and POST /messages must not double-charge or duplicate on retry. Clients send Idempotency-Key: <uuid> header; server stores key → response mapping for 24 hours. Duplicate key returns the original 201 without re-executing. Mention this for any create endpoint where failure mid-flight is plausible.

Request validation

Return 400 with field-level errors for malformed JSON or missing required fields — not 500. Validate URL schemes on shortener create (https only unless enterprise). Enforce max body size at the API gateway before traffic hits app servers, which ties to load balancer and gateway placement in your diagram.

Example: news feed endpoints

  1. POST /v1/posts — create post → 201 { post_id, created_at }
  2. GET /v1/feed?cursor=...&limit=20 — home timeline → 200 { posts, next_cursor }
  3. POST /v1/users/{id}/follow — follow user → 204
  4. DELETE /v1/users/{id}/follow — unfollow → 204

Example: chat endpoints

  1. POST /v1/conversations/{id}/messages — body: { text, client_msg_id } → 201 { server_msg_id }
  2. GET /v1/conversations/{id}/messages?after=cursor — history sync → 200
  3. POST /v1/conversations/{id}/read — update read receipt → 204

PUT vs PATCH in practice

PUT replaces the entire resource — client sends full object; missing fields become null. PATCH sends a partial diff (JSON Merge Patch or JSON Patch). Use PUT for small resources with stable shape; PATCH for large profiles where clients update one field. In interviews, saying "PATCH /users/me with { display_name }" beats debating RFCs.

Filtering, sorting, and sparse fieldsets

  • GET /posts?author_id=5&sort=-created_at — filter and sort via query params.
  • GET /users?fields=id,name — reduce payload on mobile.
  • Cap limit= at 100 server-side even if client asks for 10,000.
  • Document which filters are indexed — unindexed filters become full table scans.

Rate limiter API surface

If designing a standalone rate limiter service, expose internal and admin APIs:

EndpointPurpose
POST /v1/checkInternal: { key, limit, window } → { allowed, remaining }
GET /v1/rules/{api_key}Admin: view configured limits
PUT /v1/rules/{api_key}Admin: update tier limits
GET /healthLoad balancer readiness probe

Content type and validation

Require Content-Type: application/json on POST/PATCH bodies. Reject unsupported types with 415 Unsupported Media Type. Return 413 Payload Too Large when body exceeds gateway limit — validate before parsing into app memory. For the URL shortener, reject non-http(s) schemes and private IP ranges in the url field with 400 and a clear error code.

CORS and public APIs

Browser clients need Access-Control-Allow-Origin on API responses. Preflight OPTIONS requests hit before POST from another domain. Mention CORS only if the prompt includes a web SPA — mobile apps use native HTTP and skip CORS. API gateway often centralises CORS rules.

Webhooks (when the interviewer asks)

Instead of polling, register POST /v1/webhooks on your platform; you POST to customer URLs on events (payment.succeeded, message.received). Sign payloads with HMAC; retry with exponential backoff; return 2xx quickly from receiver. Out of scope for most 45-minute designs unless the product is an API platform.

Common API mistakes in interviews

MistakeBetter approach
Verbs in URLs (/createUser)POST /users
200 on POST create201 with Location header
500 on validation failure400 with field errors
Offset pagination on billion-row feedCursor pagination
No idempotency on payment POSTIdempotency-Key header
Leaking stack traces in errorsGeneric 500 + request_id in logs only

What to say in the last five minutes

List 5–6 endpoints with methods and status codes for the system you just designed. Mention cursor pagination for feeds or chat history, 429 with Retry-After for abuse paths, and idempotency keys for creates. Point auth termination at the API gateway. That is sufficient API depth for most loops.

Mock interview checklist

  1. Resources are plural nouns; no verbs in paths.
  2. Correct status codes (201 create, 204 delete, 409 conflict).
  3. Pagination strategy matches data scale (cursor for feed/chat).
  4. Consistent error JSON with machine-readable codes.
  5. Rate limit headers on 429 for public APIs.
  6. Version prefix /v1/ on all external routes.

Closing summary

In interviews, spend five minutes listing 4–6 endpoints with methods, status codes, and one pagination strategy. Mention idempotency keys for writes that must not duplicate. That demonstrates API thinking without drowning in OpenAPI details.

More in this series