Overview
API design is the discipline of defining contracts between systems that are easy to use correctly, hard to misuse, and able to evolve without breaking clients. A good API is consistent (same naming, errors, and pagination everywhere), predictable (standard status codes and semantics), and safe (idempotent writes, clear limits).
In interviews, API design is usually the step right after requirements: you define the core endpoints, request and response shapes, and key behaviors such as pagination, idempotency, and error handling. It is also where many long-term costs are decided, because public APIs are very hard to change once clients depend on them.
Once millions of devices use a plug shape, changing it is nearly impossible. Good standards are simple, safe (you cannot plug it in wrong), and leave room for new devices. APIs are the plugs of software.
When to use it
- The first concrete step after requirements in a system design interview.
- Any new service, public API, or internal contract.
- Reviewing proposals for consistency and future evolution.
- Planning versioning and deprecation.
Where it shows up in interviews
Recognize it when: after requirements: define the main endpoints.
- Design Twitter: postTweet, getTimeline
- Design a URL shortener: create, redirect
- Design a ride-hailing app: requestRide, updateLocation
Recognize it when: clients you do not control.
- Design a payments API
- Design a partner integration API
Where it is used in real software
Date-based versions per account, idempotency keys, expandable objects, and consistent error types are widely copied.
Google publishes resource-oriented design rules used across its cloud APIs.
Teams write OpenAPI or protobuf definitions first, then generate clients, servers, and tests.
Key terms
- Contract
- The agreed request and response formats and behaviors.
- Backward compatibility
- Old clients keep working after changes (add, do not remove).
- Idempotency key
- Client-generated ID making retries safe.
- Error envelope
- A consistent error format: code, message, details.
- Deprecation policy
- How long old versions are supported and how clients are warned.
A checklist for designing an API
- 1Start from use cases
List the client actions (post, fetch feed, follow) before naming endpoints.
- 2Model resources and operations
Nouns for resources, standard methods, clear ownership.
- 3Define requests, responses, and errors
Field names, types, required fields, and a single error format.
- 4Handle scale concerns
Pagination, filtering, rate limits, payload size limits, and timeouts.
- 5Make writes safe
Idempotency keys for creates, optimistic concurrency (ETag / If-Match) for updates.
- 6Plan evolution
Versioning scheme, additive changes, deprecation headers, and changelog.
Interview API for a Twitter-like service
Only the core endpoints; details come later
| Endpoint | Request | Response |
|---|---|---|
| POST /v1/tweets | { text, media_ids[] } + Idempotency-Key | 201 { id, created_at } |
| GET /v1/timeline | ?cursor=&limit=50 | 200 { tweets[], next_cursor } |
| POST /v1/users/{id}/follow | - | 204 |
| GET /v1/tweets/{id} | - | 200 { id, text, author, counts } |
NOWEndpoint: POST /v1/tweets | Request: { text, media_ids[] } + Idempotency-Key | Response: 201 { id, created_at }
Four endpoints capture the core product. Mentioning cursor pagination and idempotency up front signals that you are thinking about scale and retries.
Implementation
openapi: 3.1.0info: { title: Orders API, version: "1.0" }paths: /v1/orders: post: parameters: - in: header name: Idempotency-Key required: true schema: { type: string, format: uuid } requestBody: required: true content: application/json: schema: { $ref: "#/components/schemas/CreateOrder" } responses: "201": { description: Created } "409": { $ref: "#/components/responses/Error" } "429": { $ref: "#/components/responses/Error" }components: schemas: CreateOrder: type: object required: [items] properties: items: type: array maxItems: 100 items: { type: object, properties: { sku: { type: string }, qty: { type: integer, minimum: 1 } } } responses: Error: description: Error content: application/json: schema: type: object properties: error: { type: object, properties: { code: { type: string }, message: { type: string } } }Complexity and performance
Every client must update.
Protects servers and clients.
Trade-offs
Many fine-grained calls are flexible but slow on mobile; coarse endpoints are faster but couple clients to server choices.
Strict validation catches bugs early; lenient parsing (ignore unknown fields) helps compatibility.
Variants and related techniques
Different API styles; the same principles of consistency, safety, and evolution apply.
Long operations return 202 Accepted with a job resource or send webhooks on completion.
Common mistakes
- Leaking internal database models.
Fix: Design API resources around client needs, not table columns.
- Inconsistent errors.
Fix: One error envelope with stable machine-readable codes.
- No limits.
Fix: Cap page sizes, payload sizes, and rates from day one.
Interview questions
How do you design an API for a long-running operation?
Return 202 Accepted with a job ID and a status URL. Clients poll the job resource or receive a webhook or push notification when it completes.
How do you evolve an API without breaking clients?
Make additive changes (new optional fields, new endpoints), never repurpose fields, version breaking changes, announce deprecations with headers and timelines, and monitor usage of old versions.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design the API for a URL shortener | Easy | Core endpoints. |
| Design an async video transcoding API | Medium | 202 + jobs + webhooks. |
| Plan a v1 to v2 migration for 1,000 partners | Hard | Deprecation strategy. |