API Design Review: A Staff-Level Capstone
Learn how to review an API design in five passes, find 13 planted flaws in a ticket-booking API, and practise the follow-up questions interviewers ask.
API design review: common questions
How do you review an API design?
Challenge the need first, lint the contract against your guidelines, review it as the provider (secure, buildable, evolvable) and as a consumer (clear, short, no jargon), then verify the implementation with contract and security tests.
Why should a team write API design guidelines?
They settle recurring choices like paging, errors and naming once, so APIs stay consistent across teams, newcomers design well faster, and a linter can check the rules automatically.
Related chapters
- Start here in System Design
- How a system design interview is graded
- The delivery framework: five phases and a clock
- Numbers to know: what one machine can really do
- System Design Concepts: CAP, SQL vs NoSQL, Message Queues
- SQL vs NoSQL: How to Choose a Database (System Design)
- Traffic Jumps 10×: What Breaks First? (System Design)
- Proxy vs Load Balancer vs API Gateway (System Design)
- CAP Theorem Explained Simply, with Examples
- Why Use a Message Queue? System Design Explained
- Message Queue vs Pub/Sub vs Kafka: Differences
- Polling vs Long Polling vs SSE vs Webhooks (System Design)
- API design in System Design
- REST vs GraphQL vs gRPC: Choosing an API (System Design)
- API Goals Canvas: Design APIs from the Consumer
- REST API Design: Resources, Methods, Status Codes
- HTTP Semantics, Richardson Maturity Model and HATEOAS
- GraphQL Explained: Over-fetching, Schema, N+1 Problem
- RPC and gRPC Explained: Protobuf, HTTP/2, When to Use
- API Design Best Practices: 9 Principles with Examples
- API Payload Design: Names, Types, Links and Errors
- Mapping Database Tables to API Resources, Step by Step
- API Design for Complex Schemas: Trees, Polymorphism, Bookings
- API Pagination, Idempotency Keys, Errors and Versioning
- Network-Efficient APIs: HTTP Caching, ETags, Field Selection
- Async APIs: 202 Accepted, Webhooks and Bulk Operations
- API Versioning and Evolution Without Breaking Clients
- API Contracts: OpenAPI, Contract Testing and Docs
- API Security: Authentication, Authorization, Rate Limiting
- OAuth 2, OpenID Connect and Scopes for APIs
- OWASP API Top 10 Explained: BOLA, SSRF and More
- Running APIs: Gateways, Mesh, Rate Limits, Canaries
- API Troubleshooting Interview: Slow APIs and Unexpected Errors
- Spotify Web API: Resources, IDs, Payloads and Paging
- Spotify Web API: OAuth PKCE, Scopes, Quotas, Rate Limits
- Spotify Web API Changes: Real API Evolution Lessons
- Database-heavy designs in System Design
- Design a Distributed Key-Value Store (System Design Interview)
- Design a Hotel Reservation System (System Design Interview)
- Design a Digital Wallet (System Design Interview)
- Design S3-like Object Storage (System Design Interview)
- Games in System Design
- Shard City: Learn Database Sharding by Playing a Game
- System Design Case Studies: Flash Sale, Chat, ChatGPT
- Flash Sale System Design: Who Gets the Last Item
- Food Delivery App System Design: Which Database?
- Cache Stampede: Why the Database Fell Over and the Fix
- Live Streaming System Design: Scaling for a Final
- ChatGPT System Design: Streaming LLM Responses
- RAG System Design: Product Q&A on Sale Day
- IRCTC Tatkal System Design: Booking Under Extreme Load
- Group Chat System Design: WebSockets and Fan-Out
- Authentication in System Design: Sessions, JWT, OAuth
- Authentication Methods Compared: Sessions, JWT, OAuth
- JWT in System Design: Sessions, Revocation, Refresh Tokens
- JWT Interview Questions and Answers
- Life of a session, step by step
- JWT Structure Explained: Header, Payload, Signature
- Forge lab: why verification must be strict
- JWT vs Session Authentication: Which to Use
- The validation pipeline: where a bad request dies
- How to Revoke a JWT: Blocklists and Short Expiry
- Refresh Tokens and Rotation Explained
- Logout in depth: one device, all devices, SSO
- JWT Security Best Practices
- OAuth 2.0 and OpenID Connect Explained
- Where to Store JWT: localStorage vs HttpOnly Cookie
- JWT in Microservices: API Gateway vs Service Validation
- Microservices, keys, and trust boundaries
- Sender-constrained tokens: making a stolen token useless
- Authentication vs Authorization Middleware
- Failure explorer: what breaks when X fails
- Capacity planner: sessions vs JWT vs hybrid
- Edge cases catalog
- Test it, measure it, migrate to it
- Anti-patterns to reject in a design review
- Design checklist and cheat sheet
Keep learning
Overview
Challenge needs Provider review Consumer review POST /createBooking POST /events/123/holds then POST /bookings The verb belongs in the method. The path names a resource, so every resource works the same way. principle 1 John can type Priya’s ID into the body and book on her account. Who is calling is decided by the token, never by the payload. API security GET /events?city=pune&from=2026-11-01&limit=20&cursor=evt_456 Nothing to change. Plural noun, filters as query parameters, a bounded page and a cursor. This line is a decoy: reviews also confirm what is right. GET /events/123/activity?offset=40&limit=20 (live feed) GET /events/123/activity?limit=20&cursor=act_981 New rows land at the top every second, so “skip 40” shifts and the fan sees repeats or misses rows. A cursor says “after this item”. cursor paging POST /bookings/9/payments\nIdempotency-Key: 5f1c… (server stores the first result) The phone loses signal, the app retries and Priya pays twice. With a key, the second call returns the stored result instead of charging again. idempotency keys Caches, retries, dashboards and client libraries all read the status code. A 200 tells them everything worked. problem details Column names and number codes are the provider’s view. Consumers need words they understand, and the table can change without breaking them. names and types GET /events/123 → 200, no Cache-Control, no ETag Cache-Control: public, max-age=60\nETag: "e123-v7" The event page is public and read by thousands when sales open. Without cache headers every view hits the origin. caching and ETags DELETE /bookings/9 (handler checks “logged in”) allow only if booking.owner == token.sub, or role is admin Logged in is not the same as allowed. John changes 8 to 9 and cancels Priya’s booking: broken object-level authorization, the top API risk. OWASP API Top 10 GET /events/123/seats?limit=100000 One request can pull the whole seat map and tie up the database. Every input needs a bound. resource consumption Webhook-Signature: t=1793577600,v1=<HMAC-SHA256 of t.body>\n+ event id for dedupe, retries with backoff Without a signature, anyone who learns the partner’s URL can post fake cancellations. signed webhooks Old mobile apps stay installed for months. Give machine-readable notice, a long window and a migration guide, and watch v1 traffic before turning it off. Deprecation and Sunset POST /admin/refunds (same gateway, any valid token works) separate admin API, staff-only identity, scope admin:refunds Customers can guess admin paths. Function-level checks (role or scope) must guard every admin action, not just a hidden menu. Lab: review the draft. Click each line you think is wrong. · review complete 0.5rem 0.7rem 0 0.6rem 0.6rem Fine as it is caught by checked in Reveal all Start over Protocol fits each caller: REST for public, gRPC inside, events for partners Nouns in paths, methods carry the action, status codes are standard Safe and idempotent methods used as HTTP defines them If GraphQL: query depth and cost limits, persisted queries If gRPC: deadlines, retries only on idempotent calls, proto field numbers never reused Consistent names, cases and shapes across all endpoints Money as integer minor units + ISO 4217 currency, ISO 8601 times, errors as problem details Cache headers on public reads, ETag + If-Match on updates OpenAPI is the source of truth, linted in CI, contract-tested against the code Right OAuth flow per client, narrow scopes, tokens validated fully Gateway quotas, canary releases, RED metrics per endpoint Need challenged first: ask why until the real goal shows Guidelines written down and a linter runs them on every change Security tests prove John can’t read Priya’s booking How do you stop double booking on retries? How would partners get notified of cancellations? How do you version without breaking the mobile app? Most change is additive, which needs no new version: new optional fields, new endpoints. Clients ignore unknown fields. For a true break we ship /v2 beside /v1, send Deprecation and Sunset headers, watch v1 traffic by app version, and force-upgrade only the oldest builds. Why REST outside and gRPC inside? Outside, callers are browsers, apps and partners we don’t control: REST over HTTP gives caching, easy debugging and universal tooling. Inside, we own both ends: gRPC gives typed contracts, compact binary messages, streaming and deadlines that pass down the call chain. How do you stop scalper bots? Layers, because no single control is enough: per-user and per-IP rate limits at the gateway (429 + Retry-After), a ticket limit per account per event, short seat holds that expire, a waiting room when sales open, and bot detection at the edge. The API enforces the same limits as the website, so bots gain nothing by calling it directly. What do you cache? Where do you check that John can’t cancel Priya’s booking? In the booking service, on every request, by comparing the booking’s owner with the token’s subject. The gateway checks the token and scope, but only the service knows who owns booking 9. And a security test proves it. 0.6rem 0.8rem
Capstone: a staff-level API design review
The seventeen chapters before this one gave you the parts. This one puts them to work the way a senior engineer does: you get a draft API for the ticket site, you review it in five passes, you find the planted mistakes, and you end with the design you would defend in an interview.
Why APIs get reviewed, and why teams write guidelines
An API is cheap to change on a whiteboard and expensive once apps ship against it. A review moves the mistakes to the cheap end. Design is only one stop in the life of an API: is an API the answer? the contract and evolve v1 after v2 Reviews happen at several of these stops, not only after design. With many teams, the same questions come back every week: how do we page lists, what does an error look like, how do we name dates? Guidelines answer them once, so reviews argue about the product instead of style. Good guidelines have three layers: Reference rules the what Allowed methods, status codes and headers, path format, error shape, paging, and a shared vocabulary. Use-case recipes the how “To create something, do this.” “To page a list, do this.” Ready to copy, with examples. Design process the steps How to go from a need to a contract: goals canvas, draft, lint, review, test. Keep them alive: write down why each rule exists, version them, build them with the people who use them, and allow a documented exception. A rulebook enforced by an “API police” gets ignored.
The five passes of a review
Challenge the need why? who? context? against guidelines Provider view secure, buildable, evolvable Consumer view clear, short, no jargon Verify the build tests prove it Lint checks the form Paths, methods, status codes, names, types, required fields, a security scheme on every operation, breaking changes. Mostly automatic. People check the substance Does it meet the need? Is it safe and buildable? Would a stranger understand it? A linter can’t answer these. Provider view: does the flow meet every need, is every operation secured, can the backend deliver it at the expected speed, will it grow? Consumer view: pretend you know nothing. Are names free of internal jargon, are only the truly needed inputs required, is the flow short, do errors say what to do? Then show it to a real consumer. Verify: contract tests prove the code matches the spec, and security tests prove each user sees only their own data.
Review lab: the draft ticket API
A teammate posts this draft for the booking flow. Thirteen lines hide a problem and one is fine. Each answer names the review pass that should catch it. Notice the spread: lint catches the form errors cheaply, but the two most dangerous ones, ownership and admin access, only show up when someone tests the running code.
The corrected design
Every fix from the lab, assembled. Three audiences, three styles, one set of scopes and limits: Public REST apps and web Cacheable reads, idempotent writes, OAuth with PKCE. Internal gRPC service to service Typed contracts, mTLS in the mesh, deadlines on every call. Partner webhooks other companies Signed events, retries, dedupe by event id. Two details that set a staff answer apart: the client never sends the amount to pay (the server computes it from the booking), and a seat hold with an expiry separates “choosing seats” from “paying”, so a slow card form never locks seats forever.
The staff-level checklist
One line per habit, grouped by the five parts of this course. Each links back to where it is taught. 0.5rem 0 0
Interview pacing: five minutes or twenty
API step in a system design round About 5 minutes. List 4–6 core endpoints, name the protocol, call out one or two hard parts. Then move on to data and scale. API design deep dive About 20 minutes. The interviewer wants the contract itself: shapes, errors, paging, retries, auth, versioning and limits. “Public REST over HTTPS: GET /events with a cursor, GET /events/:id cached at the CDN, POST /events/:id/holds, then POST /bookings with an Idempotency-Key. The user comes from the OAuth token. Internally the booking service talks gRPC to inventory and payments. The tricky bit is double booking: the hold is a conditional write, so the second buyer gets a 409.” 0–3 min consumers and goals resources and endpoints payloads, errors, paging retries, auth, limits versioning, webhooks, ops A 20-minute deep dive, in five slices. Leave time for questions. “Consumers are fans on the app, venue managers like Asha, and partner sites. I’ll design the fan booking flow first, then partners. Errors are RFC 9457 problem details, money is integer paise plus a currency code, lists use cursors capped at 100. Every write takes an Idempotency-Key. Scopes split fans, venue managers and staff, and ownership is checked in the service. Breaking changes go to /v2 with Deprecation and Sunset headers, and partners get signed webhooks.”
Follow-up questions interviewers ask
Answer in two or three sentences, name the mechanism, then offer to go deeper. 0.5rem 0 0
Where to go next
Next, three chapters replay the whole course on a real API: the Spotify Web API , with GitHub, Stripe and Meta filling the gaps. Then practise the same review on full designs. The hotel reservation design (opens in a new tab) has the same hold, double-booking and idempotency problems with rooms instead of seats, and the digital wallet design (opens in a new tab) pushes money handling, idempotent transfers and auditing further. Run the five passes on each before reading the answer. “Before I call this done, I’d review it in five passes: is this the real need, does it pass our lint rules, is it secure and buildable, would a new developer understand it, and do tests prove John can’t touch Priya’s booking.” Every chapter of this course mapped to a real endpoint at Spotify, GitHub, Stripe or Meta. The course, mapped to real APIs
More courses
- Jobs, linked to lessons — Browse jobs where every requirement links to the chapter that teaches it. See how ready you are, how long the gap takes to close, and which listings look stale.
- Match a job post to courses — Paste a job post. Each skill is matched to a track here and put in learning order, foundations first.
- React from open source with patterns — Patterns extracted from Material UI, Radix, React Aria, React Hook Form, Redux Toolkit, TanStack, and Next.js — not slides about hooks.
- Database in depth — How a query becomes work. Indexes, isolation, locks, and the plans that explain why a page is slow — not an ORM tour.
- Build a real API with NestJS — One ticket-booking API, built in order: a plain Node server first, then NestJS modules, validation, a real database, and the features real clients lean on.
- Microservices — Microservice patterns one diagram at a time: how to split a monolith, how services talk, and how data stays consistent without distributed transactions.
- Kafka — A beginner-to-senior path through event logs, reliable delivery, production operations, and disaster recovery.
- AI engineering for software engineers — Eight free reads, in order, for software engineers moving into AI: when to build an agent, how to build one, how to test it, and how the model works underneath.
- Frontend system design — How a UI is sliced when it is a product, not a page: ownership of state, cache, routes, and what happens when the network lies.
- TypeScript in depth — The type system as a design tool. Inference, narrowing, generics, and the errors that mean the model is wrong — not a catalog of utility types.
- CSS in depth — See the box, then the page. Visual chapters from the box model through Flexbox surprises, Grid, selectors, and units, then backgrounds, shapes, and visual effects, each with a playground.
- Accessibility — Foundations first (WCAG in plain words, semantic HTML, lists, alt text), then hands-on HTML labs, a full pass of WAI-ARIA 1.3, and a final fix-the-page challenge.
- Docker — What an image is, why layers cache, and how containers find each other on a network.
- Redis — Fifteen Redis patterns, one picture each: cache, session, lock, counter, rate limiter, queue, and when Redis is the wrong tool.
- Kubernetes — Kubernetes from zero to operators: why it exists, Pods, Deployments and Services, then configuration, networking, storage, scheduling, scaling and security.