API Design
In system design interviews, your API design serves as the contract between your system and its users. It's one of the first concrete artifacts you produce, and a well-designed API shows that you understand the problem deeply enough to define how others will interact with your solution.
This page focuses on the practical aspects of designing APIs for interviews—structuring endpoints, handling common patterns, and avoiding pitfalls. For choosing between REST, gRPC, and GraphQL, see Network Communication.
API Design in the Interview
API design typically happens early in your interview (Phase 3 in the Interview Framework), right after clarifying requirements and identifying your data model. The goal is to define the external interface before diving into architecture.
A good API design:
- Maps directly to requirements — Each functional requirement should have a corresponding endpoint
- Uses consistent patterns — Predictable URL structures and response formats
- Handles edge cases — Pagination, errors, authentication
Don't overthink protocol choice. Default to REST for interviews—it's universally understood and maps naturally to CRUD operations. Only mention gRPC (for internal services) or GraphQL (for diverse clients) if the problem specifically calls for it.
Designing RESTful APIs
REST treats everything as a resource that can be created, read, updated, or deleted. Your job is to model your resources and map operations to HTTP methods.
Resource Modeling
Resources are the nouns in your system—the entities from your data model. Good REST design centers on these resources, not on actions.
Good (resource-oriented):
GET /users/123
POST /orders
Bad (action-oriented):
GET /getUser?id=123
POST /createOrder
URL Structure
URLs should be hierarchical and predictable:
/users # Collection of users
/users/123 # Specific user
/users/123/orders # Orders belonging to user 123
/users/123/orders/456 # Specific order for user 123
Guidelines:
- Use plural nouns for collections (
/users, not/user) - Use path parameters for identifiers (
/users/123) - Use query parameters for filtering and options (
/users?status=active) - Keep URLs lowercase with hyphens if needed (
/order-items)
HTTP Methods
Map CRUD operations to HTTP methods:
| Method | Purpose | Idempotent? | Example |
|---|---|---|---|
| GET | Read resource(s) | Yes | GET /users/123 |
| POST | Create new resource | No | POST /users |
| PUT | Replace entire resource | Yes | PUT /users/123 |
| PATCH | Partial update | No* | PATCH /users/123 |
| DELETE | Remove resource | Yes | DELETE /users/123 |
In interviews, you'll mostly use GET and POST. PUT and PATCH are worth mentioning for update operations, but don't get bogged down in the distinction unless the interviewer asks.
Request and Response Design
Be consistent with your request and response formats:
POST /orders
Request:
{
"user_id": "123",
"items": [
{ "product_id": "abc", "quantity": 2 }
]
}
Response (201 Created):
{
"id": "order_789",
"user_id": "123",
"items": [...],
"status": "pending",
"created_at": "2024-01-15T10:30:00Z"
}
Best practices:
- Return the created/updated resource in the response
- Include timestamps (
created_at,updated_at) - Use consistent field naming (snake_case or camelCase—pick one)
- Include resource IDs in responses
HTTP Status Codes
Use appropriate status codes to communicate outcomes clearly:
| Code | Meaning | When to Use |
|---|---|---|
| 200 OK | Success | Successful GET, PUT, PATCH |
| 201 Created | Resource created | Successful POST |
| 204 No Content | Success, no body | Successful DELETE |
| 400 Bad Request | Client error | Invalid input, validation failure |
| 401 Unauthorized | Not authenticated | Missing or invalid credentials |
| 403 Forbidden | Not authorized | Valid credentials, insufficient permissions |
| 404 Not Found | Resource doesn't exist | Invalid resource ID |
| 409 Conflict | State conflict | Duplicate entry, concurrent modification |
| 429 Too Many Requests | Rate limited | Client exceeded rate limit |
| 500 Internal Server Error | Server error | Unexpected server failure |
For interviews, knowing 200, 201, 400, 401, 403, 404, and 500 is usually sufficient. The 429 status code is worth mentioning if you discuss rate limiting.
Pagination
Any endpoint returning a list of items needs pagination. Without it, a query returning millions of results will crash your system.
Offset-Based Pagination
The simplest approach—specify where to start and how many items to return:
GET /orders?offset=20&limit=10
Pros:
- Simple to implement
- Easy to jump to any page
Cons:
- Inconsistent results if data changes between requests (items shift)
- Performance degrades at high offsets (database must skip N rows)
Cursor-Based Pagination
Use an opaque cursor pointing to a specific item:
GET /orders?cursor=eyJpZCI6MTIzfQ&limit=10
Response:
{
"data": [...],
"next_cursor": "eyJpZCI6MTMzfQ",
"has_more": true
}
Pros:
- Stable results even as data changes
- Consistent performance regardless of position
- Better for real-time feeds and infinite scroll
Cons:
- Can't jump to arbitrary pages
- Slightly more complex to implement
For most interview scenarios, offset-based pagination is fine. Mention cursor-based if you're designing a real-time feed or the interviewer asks about handling rapidly changing data.
Authentication
Most systems need to verify who's making requests. The common approaches:
- API Keys — Simple tokens for server-to-server communication
- JWT (JSON Web Tokens) — Self-contained tokens for user authentication in web/mobile apps
- OAuth 2.0 — Delegated authorization for third-party integrations ("Sign in with Google")
In interviews, briefly mention your authentication approach ("we'll use JWT for user auth") and move on. Deep dives into auth flows are rarely necessary unless you're designing an auth system specifically.
Error Handling
Consistent error responses help clients handle failures gracefully:
Response (400 Bad Request):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email format",
"details": [
{ "field": "email", "message": "must be a valid email address" }
]
}
}
Best practices:
- Use appropriate HTTP status codes
- Include a machine-readable error code
- Provide a human-readable message
- Add details for validation errors
- Never expose internal errors or stack traces
Idempotency
An operation is idempotent if making the same request multiple times has the same effect as making it once. This is crucial for handling retries in distributed systems.
Why It Matters
Networks fail. Clients retry. Without idempotency:
- User clicks "Submit Order"
- Request succeeds but response is lost
- Client retries
- User gets charged twice
Implementing Idempotency
For non-idempotent operations (like POST), use idempotency keys:
POST /orders
Headers: Idempotency-Key: unique-request-id-123
The server stores the result associated with this key. If the same key is sent again, return the stored result instead of creating a new order.
Mentioning idempotency in interviews demonstrates production awareness. It's especially relevant when designing payment systems, order processing, or any operation that shouldn't be duplicated.
Quick Reference
Interview Checklist
When presenting your API design, ensure you've covered:
- Each functional requirement maps to an endpoint
- Consistent URL structure (plural nouns, hierarchical paths)
- Appropriate HTTP methods (GET for reads, POST for creates)
- Pagination for list endpoints
- Authentication approach mentioned
- Error handling strategy (consistent format, appropriate status codes)
Example: URL Shortener API
POST /urls
Request: { "long_url": "https://example.com/path", "custom_alias": "my-link" }
Response: { "short_url": "https://short.ly/my-link", "expires_at": "..." }
GET /urls/{short_code}
Response: 301 Redirect to original URL
GET /urls/{short_code}/stats (if analytics required)
Response: { "clicks": 1234, "created_at": "...", "last_accessed": "..." }
DELETE /urls/{short_code}
Response: 204 No Content
Example: Social Feed API
GET /feed?cursor={cursor}&limit=20
Response: { "posts": [...], "next_cursor": "...", "has_more": true }
POST /posts
Request: { "content": "Hello world", "media_ids": ["..."] }
Response: { "id": "post_123", "content": "...", "created_at": "..." }
POST /posts/{id}/like
Response: { "likes_count": 42 }
DELETE /posts/{id}/like
Response: { "likes_count": 41 }
What Interviewers Look For
- Clarity — Can someone unfamiliar with the system understand the API?
- Completeness — Does every requirement have an endpoint?
- Consistency — Are naming and patterns uniform?
- Practicality — Did you consider pagination, errors, and authentication?
You don't need perfect API documentation in an interview. A clear sketch showing your endpoints, methods, and key request/response fields is enough. Spend 3-5 minutes here, then move to the architecture—you can always refine the API as you build out the design.