Defining APIs first
Why sketching the API contract before the architecture produces better system designs, and how to define endpoints, parameters, and responses in the first 10 minutes.
TL;DR
- Define the API before drawing any architecture boxes. The API is a living requirements document that forces clarity about inputs, outputs, and data flow.
- Use the 3-step sketch: list operations, define request/response shapes, identify key fields. This takes 3 to 5 minutes and anchors every architecture decision.
- Each endpoint reveals hidden requirements: pagination tokens imply cursor state, immediate write acknowledgments imply async fan-out, fast reads imply caching.
- REST naming in interviews is a safe default. Use RPC-style naming only for action-heavy operations that don't map cleanly to resources.
- The API drives your data model. The nouns in your responses become entities, and the query patterns in your GETs become access patterns that shape your schema.
You're Designing the Wrong Data Flow
Picture this. You're 10 minutes into "Design Twitter." You've drawn a write service, a fan-out service, a timeline cache, and a notification pipeline. The interviewer asks: "How does a user load their home timeline?" You freeze. You realize your fan-out service writes tweet IDs to a cache, but you never defined what the timeline API actually returns. Does it return full tweet objects or just IDs that need hydration? Does it include author profiles inline or require a separate lookup? Is it cursor-paginated or offset-paginated?
Your beautiful architecture doesn't answer these questions because you never asked them. You designed the plumbing before deciding what flows through it.
I've watched this happen in dozens of mock interviews. The candidate draws an impressive architecture diagram, but when the interviewer traces a single request through it, the data doesn't actually flow. Fields are missing. Response shapes don't match what the frontend needs. Pagination doesn't exist.
The fix: write the API first. Before a single box on the whiteboard, spend 3 to 5 minutes sketching the core endpoints. The API is the contract your system must fulfill, and it's the fastest way to discover what you're actually building.
Architecture without API is guesswork
If you can't describe the request and response for your core operations, you don't understand your system yet. The API sketch is what turns a vague prompt into concrete engineering requirements. Skip it and you'll spend the interview retrofitting an architecture that doesn't serve the real data flow.
APIs as Living Requirements Documents
An API endpoint isn't just a URL. Each one is a mini requirements document that tells you:
- What data goes in. The request body reveals what the caller knows and controls.
- What data comes out. The response shape reveals what the system must compute, store, and assemble.
- Who calls it. The authentication model (user token vs service key) reveals the trust boundary.
- How often it's called. Read endpoints are called orders of magnitude more than writes, which drives caching and scaling decisions.
When you write GET /timeline/{user_id}?cursor=abc&limit=20, you've just discovered four requirements without trying:
- Timelines are per-user (partitioning hint)
- Pagination is cursor-based (you need cursor state somewhere)
- There's a default page size (20 items, which affects cache sizing)
- The caller identifies users by ID (not username, so you need a lookup if the frontend has usernames)
My recommendation: treat the API sketch as your first architecture artifact. It's the one thing you can write before drawing any boxes, and it makes every subsequent decision easier.
The API acts as a bridge between scoping and architecture. Without it, you're jumping from vague requirements straight to boxes and arrows, and that's where designs go sideways.
The 3-Step API Sketch
You don't need a polished OpenAPI spec. You need enough to trace data through your system. Here's the process I use every time.
Step 1: List the core operations
After scoping, you know the 2 to 4 core user actions. Turn each one into an operation. For "Design Twitter":
Core operations:
1. Post a tweet โ write operation
2. Read home timeline โ read operation (high volume)
3. Follow a user โ write operation (low volume)
4. Like a tweet โ write operation (medium volume)
That's it. Four operations. Don't go hunting for edge cases yet. Search, trending topics, notifications, DMs: all out of scope unless the interviewer specifically asked for them.
Step 2: Define request and response shapes
For each operation, write the HTTP method, path, request body, and response. Be specific about field names.
POST /tweets
Request: { user_id, content (280 chars max), media_ids[]? }
Response: { tweet_id, created_at }
Notes: Responds immediately. Fan-out happens async.
GET /timeline/{user_id}?cursor={token}&limit=20
Response: { tweets: [{ tweet_id, author_id, author_name,
content, created_at, like_count }],
next_cursor }
Notes: Must be fast (< 200ms P99). Cursor-based pagination.
POST /follows
Request: { follower_id, followee_id }
Response: { status: "created" } or 409 Already Following
POST /tweets/{tweet_id}/likes
Request: { user_id }
Response: { status: "created" } or 409 Already Liked
Notice what just happened. The timeline response includes author_name inline. That's a denormalization decision you've already made, and you haven't even drawn your schema yet.
Step 3: Identify key fields and hidden requirements
Look at each endpoint and ask: "What does this field imply about my system?"
| Field / Pattern | Hidden Requirement |
|---|---|
cursor parameter | Need cursor state, ordered data, efficient range queries |
author_name in timeline | Denormalized author data, or a join/hydration step |
like_count in timeline | Either precomputed counters or a COUNT query per tweet |
media_ids[] optional | Media upload is a separate flow, tweets reference media by ID |
409 Already Following | Need uniqueness constraint on the follow relationship |
Immediate tweet_id return | Write is acknowledged before fan-out completes |
For your interview: say "Each of these fields tells me something about my data model and architecture. Let me trace through the implications." Then walk through 2 to 3 of the most interesting ones.
The 3-5 minute rule
The entire API sketch should take 3 to 5 minutes. You're not writing production docs. You're creating a contract that your architecture must satisfy. If you're spending more than 5 minutes, you're over-polishing. Write the operations, the shapes, and the key fields, then move to the diagram.
REST vs RPC Naming in Interviews
This comes up less than you'd think, but it's good to have a clear stance.
REST (resource-oriented) works when your operations map to CRUD on resources. Most interview systems fit this pattern:
GET /tweets/{id} โ Read a tweet
POST /tweets โ Create a tweet
DELETE /tweets/{id} โ Delete a tweet
GET /users/{id}/followers โ List followers
RPC-style (action-oriented) works when the operation is an action that doesn't map cleanly to a resource:
POST /rides/estimate โ Not creating an "estimate" resource
POST /payments/refund โ Reversing a payment, not creating a "refund" object
POST /search/query โ Running a query, not CRUDing a search
My recommendation: default to REST naming in interviews. It's universally understood, it maps cleanly to HTTP methods, and interviewers expect it. Switch to RPC-style only when the operation is clearly an action (estimate, refund, search, transfer). Don't spend interview time debating REST purity. Name it, move on.
The one thing to never do is mix styles randomly. If POST /tweets creates a tweet, don't also have POST /createTweet. Pick a convention and stay consistent.
Pagination, Cursors, and Common Patterns
Every read endpoint that returns a list needs pagination. I'll be blunt: if you define a list endpoint without pagination in a system design interview, you've signaled that you haven't thought about scale.
Cursor-based pagination (preferred)
GET /timeline/{user_id}?cursor=eyJjcmVhdGVkX2...&limit=20
Response: {
tweets: [...],
next_cursor: "eyJjcmVhdGVkX2...",
has_more: true
}
The cursor encodes the position in the dataset (typically the created_at timestamp or a composite key of the last item). The server decodes it and does a WHERE created_at < cursor_value ORDER BY created_at DESC LIMIT 20 query.
Why cursor beats offset: offset pagination breaks at scale. OFFSET 10000 LIMIT 20 still scans 10,020 rows. Cursor pagination is O(1) because it's a keyset lookup.
Other patterns to know
| Pattern | When to use |
|---|---|
| Cursor-based | Infinite scroll, feeds, timelines, any ordered list |
| Offset/limit | Admin dashboards, small datasets, when "jump to page 50" is needed |
| Keyset with sort | Sortable tables (ORDER BY different columns) |
| Token-based | When the server manages state (search results, complex queries) |
For your interview: always include a cursor and limit parameter on list endpoints. Mention "cursor-based for performance at scale" and move on. That's enough to signal competence.
Worked Example: Twitter API in Full
Let me walk through the complete API sketch for "Design Twitter," showing how each endpoint reveals architecture decisions.
WRITE PATH
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
POST /tweets
Auth: Bearer token (user_id extracted from token)
Request: { content: string (max 280), media_ids?: string[] }
Response: { tweet_id: string, created_at: timestamp }
Rate: ~5K writes/sec at peak
Notes: Returns immediately. Fan-out to follower timelines
happens asynchronously via a message queue.
POST /follows
Auth: Bearer token
Request: { followee_id: string }
Response: 201 Created or 409 Conflict
Rate: ~500/sec
Notes: Updates the social graph. May trigger timeline
backfill for the new followee's recent tweets.
POST /tweets/{tweet_id}/likes
Auth: Bearer token
Request: (empty, user from token)
Response: 201 Created or 409 Already Liked
Rate: ~50K/sec at peak
Notes: Increments like_count. Eventual consistency
acceptable (counter can lag by seconds).
READ PATH
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
GET /timeline/home?cursor={token}&limit=20
Auth: Bearer token (user_id from token)
Response: {
tweets: [{
tweet_id, author_id, author_name, author_avatar,
content, media_urls[], created_at, like_count,
liked_by_me: boolean
}],
next_cursor: string,
has_more: boolean
}
Rate: ~300K reads/sec at peak
Latency: < 200ms P99
Notes: The critical hot path. Pre-computed timeline
or fan-out-on-read for high-follower accounts.
GET /users/{user_id}/profile
Response: { user_id, username, display_name, bio,
avatar_url, follower_count, following_count }
Rate: ~100K/sec
Now look at what this API reveals about the architecture:
Every service in this diagram exists because an API endpoint required it. The Tweet Service handles POST /tweets. The Timeline Service handles GET /timeline/home. The Fan-out Worker exists because the POST returns immediately but the GET needs pre-computed data. Nothing is speculative.
That's the power of API-first design: the architecture explains itself because it's derived from concrete operations.
How the API Drives Your Data Model
This is the transition point to schema design. Every noun in your API responses becomes a candidate entity in your data model.
From the Twitter API above:
| API Response Field | Entity | Storage Implication |
|---|---|---|
tweet_id, content, created_at | Tweet | Core entity, high write volume |
author_id, author_name, author_avatar | User | Read-heavy, cache candidates |
follower_count, following_count | User (derived) | Pre-computed counters |
liked_by_me | Like (join) | Per-user per-tweet lookup |
next_cursor | Timeline position | Cursor state in cache or encoded in response |
media_urls[] | Media | Object storage (S3), stored separately from tweet |
The access patterns come straight from the GET endpoints:
GET /timeline/homeneeds tweets from all followed users, ordered by time, paginated. This is the critical query that drives the entire read architecture.GET /users/{id}/profileneeds user data plus aggregated counts. Simple point lookup with counters.
I'll often tell candidates: "Your API is your data model in disguise. The nouns are your entities, the GETs are your queries, and the POSTs are your write operations. If you can read your schema directly from your API, you've done it right."
Common Mistakes
Too many endpoints. You're designing a system, not building a REST API catalog. 3 to 5 endpoints is plenty. I've seen candidates list 15 endpoints and spend their entire interview defining APIs instead of designing architecture.
Missing pagination. Any list endpoint without cursor/limit parameters is a red flag. It tells the interviewer you haven't thought about what happens when the dataset grows.
No error responses. Mention at least one error case per write endpoint. "409 Conflict if the user already follows" shows you understand idempotency and constraint handling.
REST purism over clarity. Don't debate whether likes should be POST /likes or PUT /tweets/{id}/likes or POST /tweets/{id}/like. Pick one that's clear, state it, and move on. The interviewer cares about your architecture, not your HTTP method theology.
Skipping the API entirely. The biggest mistake of all. Jumping straight to "I'll have a write service and a read service" without defining what those services actually serve. Your architecture becomes a guess.
Ignoring rate differences. Not all endpoints are equal. If your timeline read is 100x more frequent than your tweet write, say so. This drives caching, scaling, and resource allocation decisions.
The API-as-checklist trick
Before moving to architecture, read your API endpoints back as a checklist: "Can my design handle POST /tweets at 5K/sec? Can it serve GET /timeline at 300K/sec under 200ms? Can it handle POST /follows updating the social graph?" If any answer is "I'm not sure," that's where you need to focus your architecture.
How This Shows Up in Interviews
At junior/mid level: The interviewer expects you to define at least 2 to 3 API endpoints before designing architecture. Showing the request and response shapes demonstrates concrete thinking. You don't need perfect REST semantics, just clear inputs and outputs.
At senior level: The interviewer expects the API sketch to drive your architecture. They'll probe: "Why did you choose cursor-based pagination?" or "What happens if this POST returns 500? Is it safe to retry?" They want to see that your API decisions are deliberate, not accidental.
At staff+ level: The API becomes a tool for scoping conversations. "Let me define the external API and the internal service-to-service contract separately." You're expected to distinguish between the public-facing API (what clients call) and the internal API (what services call each other). You might also discuss API versioning strategy or backward compatibility.
The common failure mode across all levels: spending too long on the API. Remember, 3 to 5 minutes. Write the endpoints, note the implications, and transition to architecture. If the interviewer wants more API depth, they'll ask.
Quick Recap
- Define the API before drawing architecture. The API is a contract that forces clarity about inputs, outputs, and data flow. Without it, your architecture is speculation.
- Use the 3-step sketch: list 3 to 5 core operations, define request/response shapes with specific fields, and identify hidden requirements from those fields. Total time: 3 to 5 minutes.
- Every field in your API response has an architectural implication. Cursor tokens imply ordered storage, inline author names imply denormalization, like counts imply pre-computed aggregates.
- Default to REST naming in interviews. Switch to RPC-style only for clear actions (estimate, refund, search). Don't waste time on HTTP method debates.
- Every list endpoint needs pagination. Cursor-based is the default for feeds and timelines. Offset-based is acceptable only for small, admin-facing datasets.
- The API drives your schema. Response nouns become entities, GET query patterns become access patterns, and POST frequency determines write throughput requirements.
- Read your API as an architecture checklist. If your design can't serve every endpoint at the stated scale and latency, you've found the gap you need to solve.